有没有遇到过这种效果,那么这种排布是怎么实现的呢
首先自定义一个类继承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;
}
具体的原理跟上面的类似,至于textView的背景颜色,就可以在代码中动态的设置随机的十六进制颜色值,至于具体操作放到下一次博客里面了