自定义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,还需要继续分析实现的代码....

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

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

现在开始想......

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

相关推荐
EnCi Zheng9 分钟前
M5-markconv自定义CSS样式指南 [特殊字符]
前端·css·python
kyriewen13 分钟前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技14 分钟前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端
前端老石人25 分钟前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html
CAE虚拟与现实25 分钟前
五一假期闲来无事,来个前段、后端的说明吧
前端·后端·vtk·three.js·前后端
Sarvartha36 分钟前
三目运算符
linux·服务器·前端
晓晨的博客43 分钟前
ROS1录制的bag包转换为ROS2格式
前端·chrome
Wect1 小时前
LeetCode 72. 编辑距离:动态规划经典题解
前端·算法·typescript
donecoding1 小时前
别再让 pnpm 跟着 nvm 跑了!独立安装终极指南
前端·node.js·前端工程化
GISer_Jing1 小时前
AI全栈转型_TS后端学习路线
前端·人工智能·后端·学习