<android>自己写一个流式布局吧(FlowLayout)

(android)自己写一个流式布局吧(FlowLayout)

*首先我们要继承ViewGroup 开发自定义布局:


// 步骤一。继承 ViewGroup 开发自定义布局
public class FlowLayout extends ViewGroup {
private int layoutHeight;
private int layoutWidth;
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setBackgroundColor(Color.YELLOW);
}
public FlowLayout(Context context) {
this(context, null, 0);
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

*测量是不可少的 查看有多少个元素

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//3.2. 测量前 清空行
mLines.clear();
//1.4 获取所有子元素
int childCount = getChildCount();
//1.5 遍历每个元素
Line curr = null;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
//1.6 获取子元素的测量宽高
child.measure(0, 0);// 测量包裹内容的宽高
int cWidth = child.getMeasuredWidth();
int cHeight = child.getMeasuredHeight();
System.out.println(“i->” + i + ” (” + cWidth + “,” + cHeight + “)”);
// 分行判断 步骤三。
}
步骤二 创建行对象 , 包含多个 View 的对象
// 步骤二。创建行对象 , 包含多个 View 的对象
class Line {
//2.1 创建存储 View 的集合
public List mViews = new ArrayList();
//2.2 添加元素
//2.3 获取行的高度 : 行里面最高的一个控件的高度 .( 只有元素最高值 设置下,所有元素才能存放 )
public int getLineHeight() {
int height = 0;
for (View view : mViews) {
//2.4 遍历取得最大高度 赋值给 height
view.measure(0, 0);
height = Math.max(height, view.getMeasuredHeight());
}
return height;
}
//2.5. 行内所有元素的宽度 ( 只有把元素的宽度进行累加 )
public int getAllViewWidth() {
int width = 0;
//2.6. 累加
for (View view : mViews) {
//2.7. 遍历
view.measure(0, 0);
width += view.getMeasuredWidth();
}
return width;
}
}

`
步骤三。将所有的子元素装进行里面,如果满一行就得换行继承装
private int vSpace = 6;
private int hSpace = 6;
// 步骤三。将所有的子元素装进行里面,如果满一行就得换行继承装
3.1. 创建构造流式布局的行的集合
private List mLines = new ArrayList<>();
//3.3. 创建第一行
if (mLines.size() == 0) {
curr = new Line();
//3.4. 第一行是属于行集合的
mLines.add(curr);
//3.5. 添加进行第一行的第一个元素进行
curr.mViews.add(child);
} else {
//3.6. 从第二个元素起,余下空间 >= 需要空间 , 可以存放
// 需要空间 = 间隔 + 元素宽度
// 余下空间 = 总宽度 - 所有元素的宽度和 -(n-1)*hSpace
int needSpace = hSpace + cWidth;
// MeasureSpec.getSize 将测量值转换成像素值
int layoutWidth = MeasureSpec.getSize(widthMeasureSpec);
int remainSpace = layoutWidth - curr.getAllViewWidth() - (curr.mViews.size() - 1) * hSpace;
if (remainSpace >= needSpace) {
curr.mViews.add(child);
}
//3.7. 余下空间 < 需要空间 , 换行 ( 创建一个新行 )
else {
//3.8 创建一个新行
curr = new Line();
//3.9, 行是属性于集合的
mLines.add(curr);
//3.10. 往当前行里面添加元素
curr.mViews.add(child);
}
}
步骤四 . 计算出流式布局需要的宽高
// 步骤四 . 计算出流式布局需要的宽高
//4.1 通过 onMeasure 提供的 widthMeasureSpec 测量值 ,再通过 MeasureSpec 的 getSize 获取像素 480
layoutWidth = MeasureSpec.getSize(widthMeasureSpec);
//4.2. 流式布局的高度 跟行数相关,行数越大,高度越大。流式布局高度 = 所有行的高度和 + ( n-1 ) *vSpace
layoutHeight = 0;
//4.2.1 所有行的高度和
for (int i = 0; i < mLines.size(); i++) {
Line line = mLines.get(i);
layoutHeight += line.getLineHeight();
}
//4.2.2. ( n-1 ) *vSpace
layoutHeight = layoutHeight + (mLines.size() - 1) * vSpace;
//4.3 设置布局的宽高 参数 1. 宽 , 参数 高
setMeasuredDimension(layoutWidth, layoutHeight);//
}
步骤五。摆放分好行的每个元素 layout(left,top,right,bottom)
// 步骤五。摆放分好行的每个元素 layout(left,top,right,bottom)
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//5.1. 遍历每一行
for (int i = 0; i < mLines.size(); i++) {
Line line = mLines.get(i);
//5.4 获取当前行的 top
int lineTop = getCurrLineTop(i);
//5.2. 遍历行中的所有元素
for (int j = 0; j < line.mViews.size(); j++) {
View view = line.mViews.get(j);
//5.3 设置元素的 left,top,right bottom
int left = 0;
if (j == 0) {
// 本行中的第一个元素的坐标 0,0
left = 0;
} else// 非第一个元素
{
// 第二个元素起的 left 值为前一个元素的 right 加上水平间隔
left = line.mViews.get(j - 1).getRight() + hSpace;
}
int top = lineTop;
// 宽度 =right-left 高度 = bottom-top
//5.5 将不能使用的空间均分给每个控件,增加它们的宽度
//5.5.1 获取每个元素分配的空间
int eachSpace=getEachSpace(line);
//5.5.2. 分配给每个控件的 right
int right = left + view.getMeasuredWidth()+eachSpace;
int bottom = top + view.getMeasuredHeight();
view.layout(left, top, right, bottom);
//5.5.3. 处理内容居中显示
//MeasureSpec.EXACTLY 100dp
int newWidth=view.getMeasuredWidth()+eachSpace;
int newMeasureWidth=MeasureSpec.makeMeasureSpec(newWidth,MeasureSpec.EXACTLY);
view.measure(newMeasureWidth,MeasureSpec.UNSPECIFIED);//–>onDraw
}
}
}
private int getEachSpace(Line line) {
// 获取余下不可用空间
int remainUnusedSpace=layoutWidth-line.getAllViewWidth()-(line.mViews.size()-1)*hSpace;
// 除以元素个数 3/5 0.6
return (int) (remainUnusedSpace*1f/line.mViews.size());
}
private int getCurrLineTop(int curr) {//
int top = 0;
for (int i = 0; i < curr; i++) {
Line line = mLines.get(i);
// 累加
top += (line.getLineHeight() + vSpace);
}
return top;
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值