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

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

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

现在开始想......

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

相关推荐
曦樂~8 小时前
【Web Development】02 CSS
前端·css
风骏时光牛马8 小时前
后端服务接口开发与数据逻辑处理
前端
东鹏特饮8 小时前
经典打砖块(Breakout)游戏
前端·css·css3
IT_陈寒9 小时前
Vite的HMR把我整不会了:一个生产环境的热更新坑
前端·人工智能·后端
Mh9 小时前
如何使用双缓冲优化视频切换
前端
risenav9 小时前
低头蓄力,抬头起跳:《萤虫跳跳》的 Rokid AIUI 开发实战
前端
ZhengEnCi9 小时前
如何看待 Fable 5.2 用纯代码生成 1.4MB 视觉作品:光追、水面、倒影、火光的程序化生成原理
前端
再吃一根胡萝卜10 小时前
DSH 插件的 Host 与 Client:为什么「只加载了 Host」,以及要不要自己写 Client 加载器
前端
parade岁月10 小时前
AI 写的前端代码为什么总像 5 年前的?Chrome 官方出手了
前端
tryCbest11 小时前
Vue系列——第 15 篇:内置组件全家桶——Transition、Teleport、Suspense、KeepAlive
前端·vue.js