android自定义view系列实战篇-viewgroup(代码片段)

Q-CODER Q-CODER     2022-11-24     550

关键词:

自定义 ViewGroup 其实也不复杂,但是要对子 View 的 margin 属性支持,就还需要花点精力。

下面自己写了一个自定义的 FlowLayout,支持了本身的 padding 属性的同时,也支持子 View 的 margin 属性。基本注释都已尽可能详尽地写在代码中。

先上效果图

兄弟们,上代码

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

/**
 *
 * @Author: QCoder
 * @CreateDate: 2021/12/6
 * @Description: 流式布局,当一行放不下后,换行放
 * 支持了本身的 Padding 属性,以及子 View 的 margin 属性
 * @Email: 526416248@qq.com
 */
class QFlowLayout(context: Context, attrs: AttributeSet) : ViewGroup(context, attrs) 

    //通过矩阵记录每个子 View margin 后的具体位置
    private val childrenBounds = mutableListOf<Rect>()
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) 
        //获取 QFlowLayout 的宽高的期望值 (XSize) 和 测量模式(XMode),其中 X 代表宽或高,下面同义。
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)

        // QFlowLayout 的实际宽度
        var selfWidth = 0
        // QFlowLayout 的实际高度
        var selfHeight = 0
        //当行的宽度(当行已有宽度,由子 View 和 margin,padding 属性累加起来的)
        var currentLineWidth = 0
        //当行的高度
        var currentLineHeight = 0

        //遍历测量子 View
        for (child in children) 
            //判断,若 visibility == GONE ,即不可见又不占位置的时候,跳过测量
            if (child.visibility == GONE) continue
            //测量子 View。child 当前的子 View;XMeasureSpec 是QFlowLayout 对子 View 的期望
            measureChild(child, widthMeasureSpec, heightMeasureSpec)

            //获取到子 View 的 layout属性。
            val lp = child.layoutParams as MarginLayoutParams
            val childWidth = child.measuredWidth + lp.leftMargin + lp.rightMargin
            val childHeight = child.measuredHeight + lp.topMargin + lp.bottomMargin
            //判断是否需要换行,true 需要换行;false 不需要换行
            if (currentLineWidth + childWidth > widthSize - paddingLeft - paddingRight) 

                //将当前宽度和原先的宽度对比后,重置宽度为当前子 View 宽度
                selfWidth = max(selfWidth, currentLineWidth)
                currentLineWidth = childWidth


                selfHeight += currentLineHeight
                currentLineHeight = childHeight
                childrenBounds.add(
                    //因为需要换行,所以当前的子 View 是在新的一行,那么
                    // left 左边界 = 当前子View 的 leftMargin + QFlowLayout 的 paddingLeft
                    // top 上边界 = 累计的高度 selfHeight + 当前子View 的 topMargin + QFlowLayout 的 paddingTop
                    // right 右边界 = 当前子View 的宽度 + left
                    // bottom 下边界 = top + 当前子View 的高度
                    Rect(
                        lp.leftMargin + paddingLeft, //left
                        selfHeight + lp.topMargin + paddingTop, //top
                        child.measuredWidth + lp.leftMargin + paddingLeft, //right
                        selfHeight + lp.topMargin + paddingTop + child.measuredHeight //bottom
                    )
                )
             else 
                //因为不需要换行,所以当前的子 View 在当行的接轨上去,那么
                // left 左边界 = 当行宽度 currentLineWidth + 当前子View 的 leftMargin + QFlowLayout 的 paddingLeft
                // top 上边界 = 累计的高度 selfHeight + 当前子View 的 topMargin + QFlowLayout 的 paddingTop
                // right 右边界 = 当行宽度 currentLineWidth + left
                // bottom 下边界 = top + 当前子View 的高度
                childrenBounds.add(
                    Rect(
                        currentLineWidth + lp.leftMargin + paddingLeft,//left
                        selfHeight + lp.topMargin + paddingTop,//top
                        child.measuredWidth + currentLineWidth + lp.leftMargin + paddingLeft, //right
                        selfHeight + lp.topMargin + paddingTop + child.measuredHeight//bottom
                    )
                )

                //不需要换行,所以当前行的宽度 = 原来的宽度 + 当前子 View 的宽度
                currentLineWidth += childWidth
                //行的高度,我们只需要知道最高的那就行
                currentLineHeight = max(currentLineHeight, childHeight)
            

        
        selfWidth = max(selfWidth, currentLineWidth) + paddingRight + paddingLeft
        selfHeight += currentLineHeight + paddingTop + paddingBottom
        setMeasuredDimension(
            if (widthMode == MeasureSpec.EXACTLY) widthSize else selfWidth,
            if (heightMode == MeasureSpec.EXACTLY) heightSize else selfHeight
        )
    

    override fun onLayout(changed: 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)
    

    override fun generateLayoutParams(p: LayoutParams): LayoutParams 
        return MarginLayoutParams(p)
    

    override fun generateDefaultLayoutParams(): LayoutParams 
        return MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
    


最后,附上一个最近我整理的有关自定义 View 的知识网络结构图

链接:https://pan.baidu.com/s/1COTMibrJtANax7cXYL_eeA
提取码:tbok

android自定义view系列实战篇-view(代码片段)

QRatingViewAcustomviewforratingwhicheasytomakeanduse,butfunctionisexcellent.github-QRatingViewEffectPictureProperties<declare-styleablename="QRatingView"><!--未选中图片--><attrname="normalIcon"format="reference"/><!--选中图片--><... 查看详情

android自定义view系列实战篇-viewgroup(代码片段)

...详尽地写在代码中。先上效果图兄弟们,上代码importandroid.content.Contextimportandroid.graphics.Rectimportandroid.util.AttributeSetimportandroid.view.ViewGroupimportandroidx.core.view.childrenimportkotlin.math.max/****@Author:QCoder*@CreateDate:2021/12/6*@Descript... 查看详情

android进阶之自定义view实战九宫格手势解锁实现

一.引言在上篇博客Android进阶之自定义View实战(一)仿iOSUISwitch控件实现中我们主要介绍了自定义View的最基本的实现方法。作为自定义View的入门篇,仅仅介绍了Canvas的基本使用方法,而对用户交互层面仅仅处理了单击事件... 查看详情

攻克android软键盘的疑难杂症

系列教程:推翻自己和过往,重学自定义View自定义View系列教程01–常用工具介绍自定义View系列教程02–onMeasure源码详尽分析自定义View系列教程03–onLayout源码详尽分析自定义View系列教程04–Draw源码分析及其实践自定义View系列教... 查看详情

android高级ui系列教程(代码片段)

自定义View包含什么?布局:onLayoutonMeasure/Layout:viewGroup显示:onDraw/View:  canvaspaintmatrixcliprectanimationpath(贝塞尔)line交互:onTouchEvent/组合的viewGroup自定义View如何分类?1、自定义View2、自定义ViewGroup项目实战——... 查看详情

android进阶之旅-自定义view篇

...段子项目分享将于5月中旬结束,了解具体详情请移步Android进阶之旅与你同行。经过反复的思考,首先分享*Android进阶之旅-自定义View篇*。  跟内涵段子项目不一样的是,我会先从自定义View的最基础开始,一直... 查看详情

android自定义view视差动画(代码片段)

本系列自定义View全部采用kt**系统:**macandroidstudio:4.1.3**kotlinversion:**1.5.0gradle:gradle-6.5-bin.zip废话不多说,先来看今天要完成的效果:在上一篇:androidsetContentView()解析中我们介绍了,如何通过Factory2来自己解析View,那么我们就通过这个机... 查看详情

android自定义view之网易云推荐歌单界面(代码片段)

系列文章目录Android自定义view之网易云推荐歌单界面文章目录系列文章目录前言一、实现1.自定义一个圆角图片控件(也可直接使用第三方框架)2.进行布局摆设3.图片切换动画效果二、实现效果展示三、总结先来看看网易... 查看详情

android自定义view基础篇

...2π(弧度)==>180(角度)=π(弧度)几种创建或使用颜色的方式Android自定义属性可分为以下几步:2.自定义View中获取属性3.在布局中使用4.属性值的类型归纳 查看详情

android自定义view之3d正方体(代码片段)

系列文章目录Android自定义view之3D正方体文章目录系列文章目录前言一、小提二、将传感器改成事件分发机制三、使用四、源码TouchSurfaceView.javaMainActivity.java总结前言在之前写了一篇关于3D效果的文章,借助传感器展示,有... 查看详情

android自定义view基础篇(代码片段)

目录一、基本结构1.1重写onMeasure方法1.2三种测量模式1.3子View的onMeasure方法参数测量模式的由来1.4子View的onMeasure重写的基本写法1.5通过案例了解onMeasure的作用二、组件的属性2.1属性的基本定义2.2自定义属性读取的优先级三、综合... 查看详情

android自定义view系列-measurespec(代码片段)

在【Android】自定义View系列-绘制流程一文中,在测量过程中,是通过一定的规则得出最后我们测量的宽高,然后通过setMeasuredDimension()保存结果。那么这里说到的规则主要就是---MeasureSpec和LayoutParamsMeasureSpec是?ÿ... 查看详情

android自定义view之利用drawarc方法实现动态效果(代码片段)

系列文章目录Android自定义view之利用drawArc实现动态效果文章最后会附上源码文章目录系列文章目录前言一、准备1.测量2.初始化画笔3.自定义属性二、关键方法介绍drawArc三.实现1.思路2.效果图源码前言前几天看了一位字节Android工... 查看详情

android自定义view:矩形图表(代码片段)

本系列自定义View全部采用kt系统macandroidstudio:4.1.3kotlinversion1.5.0gradle:gradle-6.5-bin.zip本篇效果:tips:本篇是在上一篇的基础上来绘制的,背景表格,和左侧文字都是上一篇的东西,如果不清楚可以先学习上一篇!绘制网格和文字这是上一篇... 查看详情

android自定义view:矩形图表(代码片段)

本系列自定义View全部采用kt系统macandroidstudio:4.1.3kotlinversion1.5.0gradle:gradle-6.5-bin.zip本篇效果:tips:本篇是在上一篇的基础上来绘制的,背景表格,和左侧文字都是上一篇的东西,如果不清楚可以先学习上一篇!绘制网格和文字这是上一篇... 查看详情

android进阶之自定义view实战贝塞尔曲线应用

Android进阶之自定义View实战(三)贝塞尔曲线应用一、引言在自定义View中,常常看到这样一些非常规的UI效果,如水滴、心型、水波、仿真书页翻动、弹射床等效果,这里面都包含一个重要的要素:贝塞尔曲线(Béziercurve&#x... 查看详情

自定义view系列一自定义view的构造函数,自定义属性(代码片段)

...列一自定义View的构造函数,自定义属性引:自定义View对于Android开发者是一道坎.虽然说是坎但是也得走过去的!此系列文章作为学习自定义View的一系列学习笔记.在进入学习自定义View的殿堂, 查看详情

android进阶之自定义view实战仿iosuiswitch控件实现

一.引言个人觉得,自定义View一直是Android开发最变换莫测、最难掌握、最具吸引力的地方。因为它涉及到的知识点比较多,想在实际应用中驾轻就熟,由浅入深,你需要掌握以下知识点:1.View的绘制机制以及Canvas、... 查看详情