在自定义普通View时,会重写onDraw和onMeasure,不会重写onLayout,因为它主要是用于布局子View用的,只有在自定义ViewGroup时才会用到,本文涉及的所有代码都在文章末尾。
下面自定义一个FlexLayout,实现优先横行排列,当横行空间不够时可以换行的容器,宽高能够自适应,效果图如下
实现大致可以分为以下几个步骤:
注意:在计算的时候需要考虑自身的padding和子View的margin。
提示:默认情况下,ViewGroup中的onDraw不会调用,当给ViewGroup设置背景色之后,onDraw会被调用。
关于onMeasure可参考:自定义View之onMeasure
注释主要代码如下
1class CustomFlexLayout(context: Context, attrs: AttributeSet?) : ViewGroup(context, attrs) {
2
3 override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
4 var x = paddingLeft
5 var y = paddingTop
6 var childMaxHeight = 0
7 for (i in 0 until childCount) {
8 val child = getChildAt(i)
9 val params = child.layoutParams as LayoutParams
10 var left = x + params.leftMargin
11 var top = y + params.topMargin
12 childMaxHeight = childMaxHeight.coerceAtLeast(
13 child.measuredHeight + params.topMargin + params.bottomMargin
14 )
15 if (left + child.measuredWidth > measuredWidth) {
16 left = paddingLeft + params.leftMargin
17 top += childMaxHeight
18 y += childMaxHeight
19 }
20 val bottom = top + child.measuredHeight
21 val right = left + child.measuredWidth
22 child.layout(left, top, right, bottom)
23 x = right + params.rightMargin
24 }
25 }
26
27 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
28 measureChildren(widthMeasureSpec, heightMeasureSpec)
29 var x = paddingLeft
30 var y = paddingTop
31 var childMaxHeight = 0
32 var width = 0
33 var height = 0
34 for (i in 0 until childCount) {
35 val child = getChildAt(i)
36 val params = child.layoutParams as LayoutParams
37 width += child.measuredWidth + params.leftMargin + params.rightMargin
38 var left = x + params.leftMargin
39 var top = y + params.topMargin
40 childMaxHeight = childMaxHeight.coerceAtLeast(
41 child.measuredHeight + params.topMargin + params.bottomMargin
42 )
43 height = y + childMaxHeight
44 if (left + child.measuredWidth > measuredWidth) {
45 left = paddingLeft + params.leftMargin
46 top += childMaxHeight
47 y += childMaxHeight
48 }
49 x = left + child.measuredWidth + params.rightMargin
50 }
51 setMeasuredDimension(
52 resolveSize(width + paddingTop + paddingRight, widthMeasureSpec),
53 resolveSize(height + paddingBottom, heightMeasureSpec)
54 )
55 }
56
57 override fun generateLayoutParams(attrs: AttributeSet?): ViewGroup.LayoutParams {
58 return LayoutParams(context, attrs)
59 }
60
61 class LayoutParams(context: Context, attrs: AttributeSet?) : MarginLayoutParams(context, attrs)
62}
文件 | 大小 | 修改时间 |
---|---|---|
app | 2021年11月05日 | |
build.gradle | 642 B | 2021年11月04日 |
gradle/wrapper | 2021年11月04日 | |
gradle.properties | 1 kB | 2021年11月04日 |
gradlew | 5 kB | 2021年11月04日 |
gradlew.bat | 2 kB | 2021年11月04日 |
local.properties | 434 B | 2021年11月04日 |
settings.gradle | 48 B | 2021年11月04日 |