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 }