自定义viewgroup

重点:onMeasure(),onlayout(),计算子视图的规格

多手搓代码,才有手感

kotlin 复制代码
package com.mircles.test.ui

import android.content.Context
import android.graphics.Rect
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import kotlin.math.max
import androidx.core.view.isGone

class CustomViewGroup @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ViewGroup(context, attrs, defStyleAttr) {

    //存储每行的视图及其位置,mutableListOf<T>()
    private val lines = mutableListOf<List<Pair<View, Rect>>>()

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        //1. 解析父容器给出的测量要求和可用空间
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)

        //2. 测量所有子视图
        measureChildren(widthMeasureSpec, heightMeasureSpec)

        //3. 计算流式布局的行和每行的高度、宽度
        lines.clear()
        var currentLine = mutableListOf<Pair<View, Rect>>()
        var currentLineHeight = 0
        var currentLineWidth = 0
        val maxWidth = if (widthMode == MeasureSpec.UNSPECIFIED) Integer.MAX_VALUE else widthSize

        for (i in 0 until childCount) {
            val child = getChildAt(i)
            if (child.isGone) continue

            val params = child.layoutParams as MarginLayoutParams
            val childWidth = child.measuredWidth + params.leftMargin + params.rightMargin
            val childHeight = child.measuredHeight + params.topMargin + params.bottomMargin

            //判断是否需要换行
            if (currentLineWidth + childWidth > maxWidth) {
                //保存上一行
                if (currentLine.isNotEmpty()) {
                    lines.add(currentLine)
                    currentLine = mutableListOf()
                    currentLineWidth = 0
                    currentLineHeight = 0
                }
            }
            //将子视图添加到当前行
            val rect = Rect().apply {
                left = currentLineWidth + params.leftMargin
                top = currentLineHeight + params.topMargin
                right = left + child.measuredWidth
                bottom = top + child.measuredHeight

            }
            currentLine.add(child to rect)
            currentLineWidth += childWidth
            currentLineHeight = max(currentLineHeight, childHeight)

        }

        if (currentLine.isNotEmpty()) {
            lines.add(currentLine)
        }

        //4. 计算整个布局的最终宽高并设置
        val totalWidth =
            if (widthMode == MeasureSpec.EXACTLY) widthSize else lines.maxOfOrNull { line ->
                line.sumOf {
                    it.second.right + (it.first.layoutParams as MarginLayoutParams).rightMargin
                }
            } ?: 0

        val totalHeight =
            if (heightMode == MeasureSpec.EXACTLY) heightSize else lines.sumOf { line ->
                line.maxOf { it.second.bottom + (it.first.layoutParams as MarginLayoutParams).bottomMargin }
            }

        setMeasuredDimension(totalWidth, totalHeight)

    }


    override fun onLayout(
        changed: Boolean,
        l: Int,
        t: Int,
        r: Int,
        b: Int
    ) {
        //该方法是必须实现的
        //5.遍历所有行和行内的子视图,调用其layout方法确定最终位置
        var currentTop = 0
        for (line in lines) {
            val lineHeight = line.maxOf {
                it.second.height() + (it.first.layoutParams as MarginLayoutParams).bottomMargin
            }

            for ((child, rect) in line) {
                child.layout(
                    rect.left, rect.top + currentTop, rect.right,
                    rect.bottom + currentTop
                )
            }

            currentTop += lineHeight

        }

    }

    override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams? {
        return MarginLayoutParams(context, attrs)
    }

    override fun generateDefaultLayoutParams(): LayoutParams? {
        return MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
    }

    override fun generateLayoutParams(p: LayoutParams?): LayoutParams? {
        return MarginLayoutParams(p)
    }

    override fun shouldDelayChildPressedState(): Boolean {
        return super.shouldDelayChildPressedState()
    }


}
xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <com.mircles.test.ui.CustomViewGroup
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >

        <TextView
            android:layout_width="100dp"
            android:layout_height="60dp"
            android:background="@color/purple_200"
            android:text="111111"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="70dp"
            android:background="@color/teal_200"
            android:text="这是什么"/>

        <TextView
            android:layout_width="120dp"
            android:layout_height="80dp"
            android:background="@color/purple_700"
            android:text="一个橘子"/>



        <TextView
            android:layout_width="100dp"
            android:layout_height="60dp"
            android:background="@color/teal_200"
            android:text="22222"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:background="@color/teal_700"
            android:text="333333"/>

        <TextView
            android:layout_width="400dp"
            android:layout_height="300dp"
            android:background="@color/purple_700"
            android:text="444444"/>

        <TextView
            android:layout_width="400dp"
            android:layout_height="100dp"
            android:background="@color/teal_200"
            android:text="5555555"/>
        <TextView
            android:layout_width="100dp"
            android:layout_height="200dp"
            android:background="@color/purple_700"
            android:text="66666666"/>

    </com.mircles.test.ui.CustomViewGroup>

</ScrollView>

提示:这明显是有BUG,还需要继续分析实现的代码....

窝趣,面试官问,你觉得你的优点是什么?

我:............(我不造呀,不都不知自己有什么长处,死脑子快转起来......)

现在开始想......

......是打造培养优点

相关推荐
小安驾到10 分钟前
【前端的坑】vxe-grid表格tooltip提示框不显示bug
前端·vue.js
去码头整点薯条9822 分钟前
python第五次作业
linux·前端·python
沐墨染38 分钟前
Vue实战:自动化研判报告组件的设计与实现
前端·javascript·信息可视化·数据分析·自动化·vue
局外人LZ1 小时前
Uniapp脚手架项目搭建,uniapp+vue3+uView pro+vite+pinia+sass
前端·uni-app·sass
爱上妖精的尾巴1 小时前
8-5 WPS JS宏 match、search、replace、split支持正则表达式的字符串函数
开发语言·前端·javascript·wps·jsa
为什么不问问神奇的海螺呢丶2 小时前
n9e categraf redis监控配置
前端·redis·bootstrap
云飞云共享云桌面2 小时前
推荐一些适合10个SolidWorks设计共享算力的服务器硬件配置
运维·服务器·前端·数据库·人工智能
刘联其3 小时前
.net也可以用Electron开发跨平台的桌面程序了
前端·javascript·electron
韩曙亮3 小时前
【jQuery】jQuery 选择器 ④ ( jQuery 筛选方法 | 方法分类场景 - 向下找后代、向上找祖先、同级找兄弟、范围限定查找 )
前端·javascript·jquery·jquery筛选方法
前端 贾公子3 小时前
Node.js 如何处理 ES6 模块
前端·node.js·es6