Android 自定义ViewGroup

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
            )

        }
    }
}
*/

你可能感兴趣的:(android)