有没有遇到过这种效果,那么这种排布是怎么实现的呢
首先自定义一个类继承ViewGroup ,因为android提供的ViewGroup子类都不能实现这种效果
然后要有一个思路:就是ViewGroup的measure()方法的执行过程是怎么样的--如果有子view的话就先测量子view--然后再测量自己的宽高
1.在onMeasure()方法中获取到所有的子View ,然后对子view进行测量, 然后累加宽度,判断当宽度大于ViewGroup的款丢的时候,进行换行
2.把每行的子view都添加到一个集合line中, 最后再把所有的line都添加到集合lines中
3.在onLayout()方法中遍历所有的集合,对每一行的子view进行layout()操作,然后再换行的时候再把参数左 上 右 下重新累加赋值,继续执行layout()操作
具体的代码如下:
package zz.itcast.googleplay09.view; import java.util.ArrayList; import java.util.List; import zz.itcast.googleplay09.utils.UIUtils; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; /** * 排行界面的流布局(特点,按行显示,如果当前行已经显示满了,才会开辟新行) * @author wangdh * */ public class FlowLayout extends ViewGroup { //标签的间隔:水平、垂直 private int hSpace = UIUtils.dip2px(13); private int vSpace = UIUtils.dip2px(13); public FlowLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public FlowLayout(Context context, AttributeSet attrs) { super(context, attrs); } public FlowLayout(Context context) { super(context); } /** * 分配子view位置(标签) * 主要由左上角点,右下角点,确定控件位置 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //1. 分配每一行的位置 for (int i = 0; i < lines.size(); i++) { Line currentLine = lines.get(i); //分配每一个内容位置,都是只确定左上角点即可 currentLine.layout(l+getPaddingLeft(),t+getPaddingTop()); //非第一行,都需要添加行高,间距 t += currentLine.getLineHeight(); t += vSpace; } } /** * 确定FlowLayout的测量标准 * 父亲有义务测量孩子的大小(宽度、高度) */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //1.测量孩子的大小(宽高的测量标准(宽度高度的mode、size)) //(1).孩子的测量标准,与父亲的测量标准有一定的对应关系。获取父亲的测量标准 //获取父亲 宽高的mode和size int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom(); //(2)获取孩子的宽度高度的mode int childWidthMode = widthMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST :widthMode; int childHeightMode = heightMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST :heightMode; //(3)获取孩子的宽度高度的size(孩子的模式:At_most(size=1000)、未指定(未指定一般size=0,但是这里大小其实给多少都可以。)) // int childWidthSize = 1000; // int childHeightSize = 1000; int childWidthSize = widthSize; int childHeightSize = heightSize; //(4)测量孩子 for (int i = 0; i < getChildCount(); i++) { System.out.println("当前孩子:"+i); View child = getChildAt(i); child.measure(MeasureSpec.makeMeasureSpec(childWidthSize, childWidthMode), MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode)); //获取孩子的宽度 int childMeasuredWidth = child.getMeasuredWidth(); // 2. 将孩子放入行内 currentLineUseWidth +=childMeasuredWidth; //如果当前已用宽度小于父亲宽度 if(currentLineUseWidth<widthSize){ //将孩子放入 currentLine.addChild(child); System.out.println("1放入孩子"); //添加水平间隔 currentLineUseWidth+=hSpace; if(currentLineUseWidth>=widthSize){ //换行 newLine(); System.out.println("1换行"); } }else{ //如果当前行内没有孩子,强制加入 if(currentLine.getChildCount()==0){ currentLine.addChild(child); System.out.println("2放入孩子"); } //换行 newLine(); //(避免换行时,漏掉孩子) i -- ; System.out.println("2换行"); } } //如果最后一行没有在集合内,添加 if(!lines.contains(currentLine)){ lines.add(currentLine); } //3.测量本身FlowLayout int totalHeight = 0; //添加行高 for (int i = 0; i < lines.size(); i++) { totalHeight+=lines.get(i).getLineHeight(); } //间距比行数少一个 totalHeight+=vSpace*(lines.size()-1); setMeasuredDimension(widthSize+getPaddingLeft()+getPaddingRight(), totalHeight+getPaddingTop()+getPaddingBottom()); // super.onMeasure(widthMeasureSpec, heightMeasureSpec); } /** * 换行 */ private void newLine() { //将当前行,放入集合,保存 lines.add(currentLine); currentLine = new Line(); currentLineUseWidth = 0; } //当前行 private Line currentLine = new Line(); //行集合 private List<Line> lines= new ArrayList<FlowLayout.Line>(); //当前行已用宽度 private int currentLineUseWidth = 0; //行:用于存放标签Textview public class Line{ private List<View> childs = new ArrayList<View>(); //1.获取当前行剩余空间 //先获取已经使用的空间(子view的宽度+间隔(子view个数-1)) private int lineUseWidth = 0; /** * 添加孩子 * @param child */ public void addChild(View child) { childs.add(child); //当前行高应该是最高孩子的高度 if(currentLineHeight<child.getMeasuredHeight()){ //当前行高=孩子的高度 currentLineHeight = child.getMeasuredHeight(); } //(1)添加子view的宽度 lineUseWidth +=child.getMeasuredWidth(); } public void layout(int l, int t) { //(2)添加间隔 lineUseWidth += (childs.size()-1)*hSpace; // System.out.println("lineUseWidth:"+lineUseWidth); //(3)获取剩余空间 int leftUseWidth = getMeasuredWidth() -getPaddingLeft() - getPaddingRight() - lineUseWidth; int avgLeftUseWidth = leftUseWidth/childs.size(); // System.out.println("childs.size():"+childs.size()); // System.out.println("avgLeftUseWidth:"+avgLeftUseWidth); //通过行,分配每一个标签的位置 for (int i = 0; i < childs.size(); i++) { View currentView = childs.get(i); //确定具体位置 currentView.layout(l, t, l+currentView.getMeasuredWidth()+avgLeftUseWidth, t+currentView.getMeasuredHeight()); //currentView.layout(l, t, getMeasuredWidth(), getMeasuredHeight()); //非第一行的left不一样 l += currentView.getMeasuredWidth()+avgLeftUseWidth;//子view的宽度(+剩余空间的宽度) l += hSpace; } } private int currentLineHeight = 0; /** * 当前行高度 * @return */ public int getLineHeight() { return currentLineHeight; } /** * 当前行有几个孩子 * @return */ public int getChildCount() { return childs.size(); } } }
那么这个空间就算是完成了,可以直接new出来,然后在代码中动态的添加TextView,效果还是很炫的
注意在每个子view的layout()方法的时候,修改一行代码,那么效果就会变得更加绚丽
//通过行,分配每一个标签的位置 for (int i = 0; i < childs.size(); i++) { View currentView = childs.get(i); //确定具体位置 //currentView.layout(l, t, l+currentView.getMeasuredWidth()+avgLeftUseWidth, t+currentView.getMeasuredHeight());把这一行注释掉,打开下面一行 currentView.layout(l, t, getMeasuredWidth(), getMeasuredHeight()); //非第一行的left不一样 l += currentView.getMeasuredWidth()+avgLeftUseWidth;//子view的宽度(+剩余空间的宽度) l += hSpace; }