更新最新完成的一个自定义view!看了一遍鸿洋大神的文章,然后自己根据自己的理解实现了一个属于自己的FlawLayout!
先上一张效果的截图!
这里已经测试了超长文字的显示,没有问题,可以正常使用!
实现的逻辑:
首先,确定自己需要哪些数据;
1.每行的高度(用于确定该行的底部坐标,layout子view的时候以此为参考确定位置,同时如果设置为wrap_content的时候也需要用到,总高度即是所有行高的和)
2.每行的宽度(如果设置FlawLayout为wrap_content时候需要自己计算出view的宽度,而FlawLayout的宽度就是每一行宽度的最大值)
3.每个view的l t r b 坐标 (用于给子view定位)
计算逻辑:
1.遍历该FlawLayout内所有的view,依次取出子view在当前行试放
2.如果已占有的宽度加上当前子view的宽度小于FlawLayout给的最大宽度,则当前子view应当放在该行,然后更新该行的高度和宽度
3.如果该行已占有的宽度加上当前子view的宽度后大于FlawLayout给的最大宽度,则当前子view应当放在下一行,更新整个FlawLayout宽度和高度
4.最后注意一点,如果当前子view是最后一个,应该要”强制”换行,其实就是要再次更新整个FlawLayout的高度和宽度
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int childCount = getChildCount();// 获取子view的个数
int lineWidth = 0;// 用于记录当前行已有的宽度
int lineHeight = 0;// 用于记录当前行现在高度(高度取的是当前行中最高子view的高度)
int totalWidth = 0;// 用于记录当前之前的行宽的最大值(例如:当前是行3,totalWidth是行1的宽度和行2宽度的最大值)
int totalHeight = 0;// 用于记录当前行之前的行高的总和(例如:当前是行3,totalHeight是行1的高度 +
// 行2高度)
List<MyView> currentList = null;
currentList = new ArrayList<MyView>();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
child.setOnClickListener(this);
LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
int childWidth = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
int childHeight = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
if (lineWidth + childWidth <= widthSize) {//计算当前view可以被放在当前行
currentList.add(new MyView(child, i, lineWidth));//将当前view加入到currentList中进行记录
lineWidth += childWidth;//更新当前行已经被占用的宽度(也是如果放下一个view的起始位置)
lineHeight = Math.max(lineHeight, childHeight);//更新当前行的高度
} else {// 当前view在当前行放不下,需要换行.换入新的一行
//下面四行其实还是在做上一行的数据更新工作
totalWidth = Math.max(totalWidth, lineWidth);//换行了,说明上一行完成测量工作,更新整个ViewGroup的宽度
totalHeight += lineHeight;//更新已有行的总高度
children.add(currentList);//将上一行中所有的view的集合currentList加入到children中
lineHeights.add(new Integer(totalHeight));//记录上一行底部其实位置
//下面的数据是当前行的数据
lineWidth = childWidth;//更新当前行已有的宽度
lineHeight = childHeight;//更新当前行的高度
currentList = new ArrayList<>();//新建一个新的currentList用于记录当前行的所有view
currentList.add(new MyView(child, i, 0));//将当前view加入到currentList中
}
if (i == childCount - 1) {//如果是最后一个子view,不会因为放置不下被强制换行,所以要手动"换行",记录关于该行的行信息
totalWidth = Math.max(totalWidth, lineWidth);
totalHeight += lineHeight;
children.add(currentList);
lineHeights.add(new Integer(totalHeight));
}
}
setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : totalWidth, heightMode == MeasureSpec.EXACTLY ? heightSize : totalHeight);
}
以上就是在onmeasure中的计算过程!
处理完计算过程基本上就完成了整个FlawLayout了!再者就是去布局子view了!
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0; i < children.size(); i++) {
List<MyView> list = children.get(i);
int size = list.size();
for (int j = 0; j < size; j++) {
MyView myView = list.get(j);
View child = myView.view;
LayoutParams params = (LayoutParams) child.getLayoutParams();
int measuredHeight = child.getMeasuredHeight();
int measuredWidth = child.getMeasuredWidth();
child.layout(myView.leftLength + params.leftMargin, lineHeights.get(i) - measuredHeight - params.bottomMargin, myView.leftLength + params.leftMargin + measuredWidth, lineHeights.get(i) - params.bottomMargin);
}
}
}