1 class WaterFlowLayout constructor(context: Context, attrs: AttributeSet) : ViewGroup(context, attrs) {
2
3 override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
4 //定义子view的初始位置为parent传入的初始位置
5 var left: Int
6 var right: Int
7 var top: Int
8 var bottom: Int
9 //开始遍历
10 //当前行高
11 var currentHeight = 0
12 list.forEachIndexed { index, arrayList ->
13
14 var currentWidth = 0
15 arrayList.forEach {
16 val marginLayoutParams = it.layoutParams as MarginLayoutParams
17 top = t + currentHeight + marginLayoutParams.topMargin
18 left = l + currentWidth + marginLayoutParams.leftMargin
19 right = left + it.layoutParams.width + marginLayoutParams.rightMargin
20 bottom = top + it.layoutParams.height + marginLayoutParams.bottomMargin
21 currentWidth = right
22 it.layout(left, top, right,bottom)
23 }
24 currentHeight += heightList.get(index)
25 }
26 }
27
28 override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
29 return MarginLayoutParams(context, attrs)
30 }
31
32 /**
33 * 因为onLayout时需要遍历各个子view确定他们的位置
34 * 所以我们需要一个list来保存各个子view
35 * list的每个元素表示一行
36 */
37 private var list = arrayListOf<ArrayList<View>>()
38
39 /**
40 * 同样为了onLayout时确定每一行的行高
41 * 我们再加一个List保存行高信息
42 */
43 private var heightList = arrayListOf<Int>()
44
45 /**
46 * 通过源码我们知道 这里传进来的时parent的约束信息
47 */
48 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
49 //因为onMeasure方法会调用两次 所以需要clear
50 list.clear()
51 heightList.clear()
52 //根据传进来的参数,获取父容器的宽高
53 val parentWidth = MeasureSpec.getSize(widthMeasureSpec)
54 val parentHeight = MeasureSpec.getSize(heightMeasureSpec)
55
56 //定义变量记录最终测量出的宽高信息
57 var measureWidth = 0
58 var measureHeight = 0
59 //获取父容器传进来的Mode信息
60 val widthMode = MeasureSpec.getMode(widthMeasureSpec)
61 val heightMode = MeasureSpec.getMode(heightMeasureSpec)
62 //如果Mode是match_parent, 那么直接返回父容器的宽高
63 if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY){
64 measureWidth = parentWidth
65 measureHeight = parentHeight
66 }else{
67 //如果不是EXACTLY,就需要计算宽高
68
69 //定义当前行宽,高
70 var currentWidth = 0
71 var currentHeight = 0
72
73 //定义一个list保存当前行里的child
74 var childList = arrayListOf<View>()
75 //遍历childs
76 children.forEach {
77 val marginLayoutParams = it.layoutParams as MarginLayoutParams
78 //每个元素的宽等于它本身的宽加左右Margin
79 val childWidth = it.layoutParams.width + marginLayoutParams.leftMargin + marginLayoutParams.rightMargin
80 //每个元素的高等于它本身的高加上下Margin
81 val childHeight = it.layoutParams.height +marginLayoutParams.topMargin + marginLayoutParams.bottomMargin
82
83 //如果当前已有行宽+该子view行宽已经大于父容器给定的行宽, 则需要换行
84 if (currentWidth + childWidth > parentWidth){
85 //换行
86 //保存上一行的行高
87 heightList.add(currentHeight)
88
89 //换行后,新行宽度就等于子View的宽度
90 currentWidth = childWidth
91 //新行高度等于子View的高度
92 currentHeight = childHeight
93 //最终测量出来的宽度为各行的最大宽度
94 measureWidth = Math.max(currentWidth, measureWidth)
95 //最终测量出来的高度为各行高度累加
96 measureHeight += currentHeight
97
98 //换行的话, 先把原本的当前行加到list中
99 list.add(childList)
100
101 //再把childlist置空,放入新的childView
102 childList = arrayListOf(it)
103
104 }else {
105 //否则, 将该子view放在当前行
106 currentWidth +=childWidth
107 //高度取已有行高和当前子view高度之间的最大值
108 currentHeight = Math.max(currentHeight, childHeight)
109 //最终测量出来的宽度为各行的最大宽度
110 measureWidth = Math.max(currentWidth, measureWidth)
111
112 //将这个子view放在当前行
113 childList.add(it)
114 }
115 }
116 //for循环执行完之后,将最后一个当前行加入到list
117 Log.d("-----", "end child list size:"+childList.size )
118 list.add(childList)
119 measureHeight += currentHeight
120 heightList.add(currentHeight)
121 }
122 //必须set, 否则会抛出IllegalStateException
123 setMeasuredDimension(measureWidth, measureHeight)
124 }
125 }