常用的自定义View例子一( FlowLayout)
在Android开发中,我们经常会遇到流布式的布局,经常会用来一些标签的显示,比如qq中个人便签,搜索框下方提示的词语,这些是指都是流布式的布局,今天我就我们日常开放中遇到的流布式布局坐一些总结
转载请注明博客地址:http://blog.csdn.net/gdutxiaoxu/article/details/51765428
**源码下载地址:https://github.com/gdutxiaoxu/CustomViewDemo.git **
1. 先给大家看一下效果
- 图一
- 图二
仔细观察,我们可以知道图二其实是图一效果的升级版,图一当我们控件的宽度超过这一行的时候,剩余的宽度它不会自动分布到每个控件中,而图二的效果当我们换行的时候,如控件还没有占满这一行的时候,它会自动把剩余的宽度分布到每个控件中
2.废话不多说了,大家来直接看来看一下图一的源码
1)代码如下
1 | /** |
2)思路解析
首先我们重写onMeasure方法,在OnMeasure方法里面我们调用measureChild()这个方法去获取每个孩子的宽度和高度,每次增加一个孩子我们执行 widthUsed += childUsedWidth;
添加完一个孩子以后我们判断widthUsed是够超出控件本身的最大宽度widthSpecSize,
若没有超过执行widthUsed += childUsedWidth; if (childUsedHeight > childMaxHeightOfThisLine) { childMaxHeightOfThisLine = childUsedHeight; }
超过控件的宽度执行
heightUsed += childMaxHeightOfThisLine + verticalSpacing; widthUsed = paddingLeft + paddingRight + childUsedWidth; childMaxHeightOfThisLine = childUsedHeight;
最后调用 setMeasuredDimension(widthSpecSize, heightUsed);这个方法去设置它的大小
在OnLayout方法里面,所做的工作就是去摆放每一个孩子的位置 ,判断需不需要换行,不需要更改left值,需要换行,更改top值
3)注意事项
讲解之前,我们先来了解一下一个基本知识
从这张图片里面我们可以得出这样结论
- Width=控件真正的宽度(realWidth)+PaddingLeft+PaddingRight
- margin是子控件相对于父控件的距离
注意事项
- 为了支持控件本身的padding属性,我们做了处理,主要代码如下
1
2
3
4
5
6
7
8
9int widthUsed = paddingLeft + paddingRight;
int heightUsed = paddingTop + paddingBottom;
----------
if (widthUsed + childUsedWidth < widthSpecSize) {
widthUsed += childUsedWidth;
if (childUsedHeight > childMaxHeightOfThisLine) {
childMaxHeightOfThisLine = childUsedHeight;
}
} - 为了支持子控件的margin属性,我们同样也做了处理
1
2
3
4
5
6
7
8Rect marginRect = getMarginRect(child);
int leftMargin=marginRect.left;
int rightMargin=marginRect.right;
int topMargin=marginRect.top;
int bottomMargin=marginRect.bottom;
childUsedWidth += leftMargin + rightMargin;
childUsedHeight += topMargin + bottomMargin;
即我们在计算孩子所占用的宽度和高度的时候加上margin属性的高度,接着在计算需要孩子总共用的宽高度的时候加上每个孩子的margin属性的宽高度,这样自然就支持了孩子的margin属性了
4.缺陷
如下图所见,在控件宽度参差不齐的情况下,控件换行会留下一些剩余的宽度,作为想写出鲁棒性的代码的我们会觉得别扭,于是我们相处了解决办法。
解决方法见下面
图二源码解析
废话不多说,先看源码
1 | /** |
2.思路解析
对比图一的实现思路,我们封装了Line这个内部类,看到这个名字,相信大家都猜到是什么意思了,其实就是一个Line实例对象代表一行,Line里面的List
children用来存放孩子 private List<View> children;//一行里面所添加的子View集合
Line里面还封装了void onLayout(int l, int t)方法,即自己去拜访每个孩子的位置,
实现剩余的宽度平均分配,主要体现在这几行代码1
2
3
4
5
6
7
8
9
10if (surplus > 0) {
//如果有剩余宽度,则将剩余宽度平分给每一个子控件
surplusChild = (int) (surplus / children.size()+0.5);
}
-------
//重新分配每个孩子的大小
child.measure(MeasureSpec.makeMeasureSpec(
child.getMeasuredWidth()+surplusChild,MeasureSpec.EXACTLY)
,MeasureSpec.makeMeasureSpec(height,MeasureSpec.EXACTLY));今天就写到这里了,有时间再来补充,最近考试比较忙,已经好久没有更新博客了。