step:
重写onMeasure 、遍历测量子VIew,得到尺寸后保存,有些子VIew可能需要重新测量,然后得出所有子VIew位置和尺寸后,计算出自己的尺寸,然后使用setMeasuredDimension(w,h)保存结果
step2:
遍历每个子VIew,调用layout方法来将位置和尺寸传给子VIew
code:
package com.example.myapplication.view import android.content.Context import android.util.AttributeSet import android.view.ViewGroup import androidx.core.view.children class TagLayout(context: Context,attributeSet: AttributeSet):ViewGroup(context,attributeSet) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { for (child in children){ } } override fun onLayout(p0: Boolean, l: Int, t: Int, r: Int, b: Int) { for (child in children){ if (children.indexOf(child) == 0){ child.layout(0,0,(r-l) /2,(b-t) /2 ) }else{ child.layout((r-l) /2,(b-t) /2,r-l,b-t ) } } } }
//计算子View模式以及父View的模式范围
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val witchSpecMode = MeasureSpec.getMode(widthMeasureSpec) //模式 val witchSpecSize = MeasureSpec.getSize(widthMeasureSpec) //大小 for ((index, child) in children.withIndex()) { var childParams = child.layoutParams var childWitchSpecMode = 0 var childWitchSpecSize = 0 when (layoutParams.width) { LayoutParams.MATCH_PARENT -> {
when (witchSpecMode) { //计算ViewGroup取值 MeasureSpec.EXACTLY -> { childWitchSpecMode = MeasureSpec.EXACTLY //精确值 childWitchSpecSize - usedSpecSize } MeasureSpec.AT_MOST -> { childWitchSpecMode = MeasureSpec.AT_MOST // 200dp.... 或者填充 childWitchSpecSize - usedSpecSize } MeasureSpec.UNSPECIFIED -> { //父VIew没限制 可用空间无限,子View填满 childWitchSpecMode = MeasureSpec.UNSPECIFIED childWitchSpecSize = 0 //早期Android witchSpecSize 默认时0,现在Android 写不写0会影响计算结果 } }
} LayoutParams.WRAP_CONTENT -> { when (witchSpecMode) { //有精确空间,不要超过大小} MeasureSpec.EXACTLY, MeasureSpec.AT_MOST -> { childWitchSpecMode = MeasureSpec.AT_MOST childWitchSpecSize - usedSpecSize } MeasureSpec.UNSPECIFIED -> { //父VIew没限制 可用空间无限,子View填满 childWitchSpecMode = MeasureSpec.UNSPECIFIED childWitchSpecSize = 0 //早期Android witchSpecSize 默认时0,现在Android 写不写0会影响计算结果 } } } else -> { //精确要求大小 childWitchSpecMode = MeasureSpec.EXACTLY childWitchSpecSize - layoutParams.width } } child.measure() val childBounds = childrenBounds[index] childBounds.set() } val selfWidth val selfHeight setMeasuredDimension(selfWidth, selfHeight) }
以上计算方法Android给出一个方法 ->
measureChildWithMargins(view,measureW,useW,measureH,useH)
这个方法的child.getLayourParams 会转成MarginLayoutParams 需要我们重写generateLayoutParams方法进行转换
override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams { return MarginLayoutParams(context,attrs) }
具体算法跟layout 相关
package com.example.myapplication.view import android.content.Context import android.graphics.Rect import android.util.AttributeSet import android.view.ViewGroup import androidx.core.view.children import kotlin.math.max class TagLayout(context: Context, attributeSet: AttributeSet) : ViewGroup(context, attributeSet) { private val childrenBounds = mutableListOf() override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { var witchUse = 0 var lineWitchUse = 0 var heightUse = 0 var lineMaxheigh = 0 val specSize = MeasureSpec.getSize(widthMeasureSpec) val specMode = MeasureSpec.getMode(widthMeasureSpec) for ((index, child) in children.withIndex()) { measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUse) if ((specMode != MeasureSpec.UNSPECIFIED) && (lineWitchUse + child.measuredWidth > specSize)){ //如果时不限制大小,则 specSize 就没有用了,然后计算已用和自身超过最大值 换行 lineWitchUse = 0 heightUse += lineMaxheigh lineMaxheigh = 0 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUse) } if (index >= childrenBounds.size) { childrenBounds.add(Rect()) } val childBounds = childrenBounds[index] childBounds.set(lineWitchUse, heightUse, lineWitchUse + child.measuredWidth, heightUse + child.measuredHeight) //测量阶段用measureWitch... lineWitchUse += child.measuredWidth witchUse = max(witchUse,lineWitchUse) //计算比较最大宽度 确保时最大宽度 lineMaxheigh = max(lineMaxheigh, child.measuredHeight) } val selfWidth = witchUse val selfHeight = heightUse + lineMaxheigh setMeasuredDimension(selfWidth, selfHeight) } override fun onLayout(p0: Boolean, l: Int, t: Int, r: Int, b: Int) { for ((index, child) in children.withIndex()) { val childBounds = childrenBounds[index] child.layout( childBounds.left, childBounds.top, childBounds.right, childBounds.bottom ) } } override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams { return MarginLayoutParams(context,attrs) } } /* package com.example.myapplication.view import android.content.Context import android.graphics.Rect import android.util.AttributeSet import android.view.ViewGroup import androidx.core.view.children class TagLayout(context: Context, attributeSet: AttributeSet) : ViewGroup(context, attributeSet) { private val childrenBounds = listOf () override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val witchSpecMode = MeasureSpec.getMode(widthMeasureSpec) //模式 val witchSpecSize = MeasureSpec.getSize(widthMeasureSpec) //大小 for ((index, child) in children.withIndex()) { measureChildWithMargins(child,widthMeasureSpec,,heightMeasureSpec,heightUse) val childBounds = childrenBounds[index] childBounds.set() var childParams = child.layoutParams var childWitchSpecMode = 0 var childWitchSpecSize = 0 when (layoutParams.width) { LayoutParams.MATCH_PARENT -> { when (witchSpecMode) { //计算ViewGroup取值 MeasureSpec.EXACTLY -> { childWitchSpecMode = MeasureSpec.EXACTLY //精确值 200dp.... 或者填充 childWitchSpecSize - usedSpecSize } MeasureSpec.AT_MOST -> { childWitchSpecMode = MeasureSpec.AT_MOST //精确值 200dp.... 或者填充 childWitchSpecSize - usedSpecSize } MeasureSpec.UNSPECIFIED -> { //父VIew没限制 可用空间无限,子View填满 childWitchSpecMode = MeasureSpec.UNSPECIFIED childWitchSpecSize = 0 //早期Android witchSpecSize 默认时0,现在Android 写不写0会影响计算结果 } } } LayoutParams.WRAP_CONTENT -> { when (witchSpecMode) { //有精确空间,不要超过大小} MeasureSpec.EXACTLY, MeasureSpec.AT_MOST -> { childWitchSpecMode = MeasureSpec.AT_MOST childWitchSpecSize - usedSpecSize } MeasureSpec.UNSPECIFIED -> { //父VIew没限制 可用空间无限,子View填满 childWitchSpecMode = MeasureSpec.UNSPECIFIED childWitchSpecSize = 0 //早期Android witchSpecSize 默认时0,现在Android 写不写0会影响计算结果 } } } else -> { //精确要求大小 childWitchSpecMode = MeasureSpec.EXACTLY childWitchSpecSize - layoutParams.width } } } val selfWidth val selfHeight setMeasuredDimension(selfWidth, selfHeight) } override fun onLayout(p0: Boolean, l: Int, t: Int, r: Int, b: Int) { for ((index, child) in children.withIndex()) { val childBounds = childrenBounds[index] child.layout( childBounds.left, childBounds.top, childBounds.right, childBounds.bottom ) } } } */