最终代码在最后,每行都注释了,不想看写的文字的自己取(我写的是仿照流式布局形式写的,你可以借鉴这个思路写)显示效果在最后。
首先我先简单解释一下原理 , 上图片
ww2
接下来我将一段段的讲解,最终代码在最后,都有我写的注释。
首先写Android这么久了肯定知道,写一个控件有三种方式来控制控件的大小:
match_parent 、wrap_content、(给定确切大小如200dp)
所以自定义View同样也会有这样三种模式,给定View的大小咱就先不说了,就先谈没给定大小, 怎么获得View的宽高。
自定义View跟ViewGroup有一定区别:View可以只走OnMeasure(),OnDraw 就可以运行,ViewGroup走OnMeasure ->OnLayou 可以运行;
所以测量ViewGroup 跟View的大小在OnMeasure()方法内进行
在OnMeasure()方法内我们肯定要先计算View的大小,当然如果我们将所有的View的都添加进ViewGroup却大于ViewGroup的容器肯定不会显示超越ViewGroup的部分,所以这个要注意,当我们View未超过ViewGroup则可以按照我们的思路排序。排序完成后在确定ViewGroup的大小显示,如果是固定值那么我们获得的ViewGroup还是他的固定值。
计算ViewGroup下的所有子View:
因为我写的是一个流式的布局类似,所以我循环内得这样写
首先我的显示第一行的内容,以行为单位显示(显示代码在OnLayout)所以我要计算每个孩子的宽高(孩子的宽相加用于判断当前是否超过了这一行的最大限制)(孩子的高保留最大的用于判断这行高度),如果这行已经占用大宽度已经不能容纳下一个View的时候我们要换行。换行就该把记录的List<View>(放置添加的View的List)制空重新添加下一行,由于我们还是需要在OnLayout显示,所以我们要定义一个全局的List<List<View>>去记录这一行的View,与这行的高度(LineHeight)。
int childCount = getChildCount();//获得孩子的个数以便操作循环
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);//获得第i个孩子的View
LayoutParams child = childView.getLayoutParams();//获得孩子布局的参数
//widthMeasureSpec父布局的宽 getPaddingLeft() + getPaddingRight() 获取父布局边界值 child.width获得孩子View的宽
int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight(), child.width);
//heightMeasureSpec的高 getPaddingLeft() + getPaddingRight() 获取边界值 child.height获得孩子View的高
int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom(), child.height);
childView.measure(childWidthMeasureSpec, childHeightMeasureSpec);//给定某个孩子的大小
//测量完成判此View是否还可以在此行放下
//判断此行是否还可以放下一个子View,如果放不下换行,将数据清空。
// selfWeight ViewGroup的总宽度
int childMeasureWidth = childView.getMeasuredWidth();
int childMeasureHeight = childView.getMeasuredHeight();
if (childMeasureWidth + lineWidthUsed + mHorizontalSpa