布局过程的完全解析

news2025/1/16 16:04:38

前言

在这里插入图片描述
那么为什么要分为两个流程呢

因为测量流程是一个复杂的流程,有时候不一定一遍就能得出测量结果,可能需要 2 - 3 次甚至更多

在这里插入图片描述

自定义布局的几种类型,也是自定义布局的两个方法

在这里插入图片描述

实战,第一种类型:改写已有View 的步骤

在这里插入图片描述
需求:实现一个正方形的ImageView,以窄边作为变长,我们可以这样实现:

package com.example.viewtest.view

import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
import kotlin.math.min

class SquareImageView(context: Context, attrs: AttributeSet) : AppCompatImageView(context, attrs) {
    override fun layout(l: Int, t: Int, r: Int, b: Int) {
        val width = r - l
        val height = t - b
        val value = min(width, height)
        super.layout(l, t, r + value, b + value)
    }
}

这样可以实现我们的效果,但是为什么不能这样写呢?

因为这是父View在他的OnLayout方法中会调用字view的layout,让子view将自己的尺寸保存下来,而我们在这个过程中修改了自己的尺寸,父view是不知道的,后续的过程中父view会一直认为我们的尺寸是他传给我们的那个,会发生意想不到的效果

比方说我们在xml中声明的这个view 的宽是300,高是200,在这个view的右边紧挨着放了另一个view,运行的效果会发现这两个view中间有100的距离,就是因为父view认为你是300,而你实际把自己改成了200

接下来展示正确的写法

package com.example.viewtest.view

import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
import kotlin.math.min

class SquareImageView(context: Context, attrs: AttributeSet) : AppCompatImageView(context, attrs) {

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        // 保留这个方法,让父布局去测量我的结果,我的宽高
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)

        // 通过这两个拿到测量之后的结果
        val value = min(measuredWidth, measuredHeight)

        // 修改后的值直接保存,这样才有意义,才会起到作用
        // 不是通过返回值将结果返回给父view,因为之后不一定是父view在使用
        setMeasuredDimension(value, value)

        // measuredWidth、width 的区别
        // measuredWidth 是测量过程中的值,width 是最终的结果值,父view可能会对measuredWidth进行修改,他俩可能值不一样
        // width 只有测量结束才能拿到结果,即使是刷新,在刷新完成之前虽然有值,也是上一次的测量结果
        // 在测量过程中应该使用 measuredWidth,高同理
    }

}

这里额外说一下 measuredWidthwidth 的区别(高同理

    // measuredWidth、width 的区别
    // measuredWidth 是测量过程中的值,width 是最终的结果值,父view可能会对measuredWidth进行修改,他俩可能值不一样
    // width 只有测量结束才能拿到结果,即使是刷新,在刷新完成之前虽然有值,也是上一次的测量结果
    // 在测量过程中应该使用 measuredWidth,高同理

实战,第二种类型:完全自定义View的尺寸

步骤
在这里插入图片描述

package com.example.viewtest.view

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import androidx.appcompat.widget.AppCompatImageView
import com.example.viewtest.R
import com.example.viewtest.ext.dp
import kotlin.math.min

private const val PADDING = 100f
private const val RADIUS = 100f
class CircleView(context: Context, attrs: AttributeSet) : View(context, attrs) {

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)

        val size = (PADDING + RADIUS) * 2

        /**
         * resolveSize 的作用
         * 一值两用,通过 MeasureSpec.getMode 判断返回的约束条件;通过 MeasureSpec.getSize 获取真实的值
         * 如果强制类型,那么使用父类给的值,如果是范围类型,则谁小使用谁,其他则随意使用
         */
        val width = resolveSize(size.toInt(), widthMeasureSpec)
        val height = resolveSize(size.toInt(), heightMeasureSpec)

        setMeasuredDimension(width, height)

    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        canvas.drawCircle(PADDING + RADIUS, PADDING + RADIUS, RADIUS , paint)
    }

}

实战,第三种类型:完全自定义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?, attrs: AttributeSet?) : ViewGroup(context, attrs) {
    private val childrenBounds = mutableListOf<Rect>()

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        var widthUsed = 0
        var heightUsed = 0
        var lineWidthUsed = 0
        var lineMaxHeight = 0
        val specWidthSize = MeasureSpec.getSize(widthMeasureSpec)
        val specWidthMode = MeasureSpec.getMode(widthMeasureSpec)
        for ((index, child) in children.withIndex()) {

            // 测量子类的限制类型以及他的宽,确定他最终的真实宽度
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUsed)

            // 判断是否需要换行,换行需要重制上一行的内容
            if (specWidthMode != MeasureSpec.UNSPECIFIED &&
                lineWidthUsed + child.measuredWidth > specWidthSize) {
                lineWidthUsed = 0
                heightUsed += lineMaxHeight
                lineMaxHeight = 0
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUsed)
            }

            if (index >= childrenBounds.size) {
                childrenBounds.add(Rect())
            }
            val childBounds = childrenBounds[index]
            childBounds.set(lineWidthUsed, heightUsed, lineWidthUsed + child.measuredWidth, heightUsed + child.measuredHeight)

            lineWidthUsed += child.measuredWidth
            // 已经使用的最大宽度为当我自己的宽度
            widthUsed = max(widthUsed, lineWidthUsed)
            // 当前行的最大高度
            lineMaxHeight = max(lineMaxHeight, child.measuredHeight)
        }
        val selfWidth = widthUsed
        val selfHeight = heightUsed + lineMaxHeight
        // 确定我自己的宽高
        setMeasuredDimension(selfWidth, 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)
        }
    }

    // 调用 measureChildWithMargins 时会强转报错
    override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
        return MarginLayoutParams(context, attrs)
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/998173.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

day34 Map Properties

Map<String,Integer> map new HashMap<>(); map.put("a",1);map.put("b",2);map.put("c",3);map.put("d",4);Integer a map.put("a", 2);System.out.println(a);Integer chinese map.put("语文",1…

新一代G7系列浪潮云海超融合EC纠删功能设计

浪潮云海在2023年5月正式发布新一代InCloud Rail G7系列超融合一体机&#xff0c;其内置的InCloud dSAN超融合存储组件&#xff0c;基于新一代的硬件平台设计&#xff0c;支持全栈RDMA协议&#xff0c;同时在EC纠删功能上也带来全新体验&#xff0c;为新时代用户提供更丰富的产…

PYTHON(一)——认识python、基础知识

一、为什么要学习python&#xff1f; Python 被认为是人工智能、机器学习的首选语言&#xff0c;可以说是全世界最流行通用范围最广的语言&#xff0c;几乎可以完成所有的任务&#xff0c;像设计游戏、建网站、造机器人甚至人工智能等都广泛使用Python。 二、输出&#xff08;…

注解-宋红康

目录 一、注解&#xff08;Annotation&#xff09;概述二、常见的注解实例三、如何自定义注解四、JDK中的四个元注解五、Java8注解的新特性1、可重复注解2、类型注解 一、注解&#xff08;Annotation&#xff09;概述 二、常见的注解实例 三、如何自定义注解 自定义注解必须配…

查询硬盘序列号、物理地址及对应批处理命令

首先说明&#xff1a; 通过winR -> cmd -> diskpart -> list disk -> select disk 0 -> detail disk -> 然后显示磁盘ID等&#xff0c;这不是序列号&#xff0c;只是磁盘ID而已。 查询序列号命令很简单&#xff1a; wmic diskdrive get serialnumber或者 w…

权限、认证与授权

权限、认证与授权 1、权限概述 &#xff08;1&#xff09;什么是权限 权限管理&#xff0c;一般指根据系统设置的安全策略或者安全规则&#xff0c;用户可以访问而且只能访问自己被授权的资源&#xff0c;不多不少。权限管理几乎出现在任何系统里面&#xff0c;只要有用户和…

webstorm HbuilderX工具未配置

问题&#xff1a;调试动迁uni app h5项目&#xff0c;报错 webstorm是换了电脑新安装&#xff0c; HBuilerx是从旧电脑拷贝过来的解压的文件 解决&#xff1a; 把uniapp插件&#xff0c;卸载 再重启webstorm,重装安装uniapp Tool&#xff0c; 安装第一个&#xff0c;免费。…

拓展外部SRAM

外部拓展芯片 IS62WV51216A 芯片手册 支持高速时钟通道时间为45、55ns 芯片引脚定义 通道时序 读定义表 一个纵列表示当前使用的高速通道的时间&#xff0c;选一个纵列作为参数标准。 地址控制读时序 如图&#xff0c;大概需要三个参数 写时序定义表 还是选择55ns参数 写…

数据接口工程对接BI可视化大屏(五)数据接口发布

文章目录 第5章 数据接口发布5.1 编写Service5.2 从MySQL中返回数据5.2.*1 封装Bean5.2.*2 编写Mapper5.2.3 编写ServiceImpl5.2.4 编写Controller5.2.5 测试 5.3 从Redis中返回数据5.3.1 封装Bean5.3.2 编写Mapper5.3.3 编写ServiceImpl5.3.4 编写Controller5.3.5 测试 5.4 从…

day34 Set

概述 Set也是集合Collection接口的子接口 Set也是集合Collection接口的子接口 特点&#xff1a;不保证元素有顺序&#xff0c;数组元素不可以重复 HashSet: 底层是基于HashMap的。元素是无序的。元素不可重复&#xff0c;去重机制是依据hashCode()和equals()方法 LinkedHas…

数据结构-堆的实现及应用(堆排序和TOP-K问题)

数据结构-堆的实现及应用[堆排序和TOP-K问题] 一.堆的基本知识点1.知识点 二.堆的实现1.堆的结构2.向上调整算法与堆的插入2.向下调整算法与堆的删除 三.整体代码四.利用回调函数避免对向上和向下调整算法的修改1.向上调整算法的修改2.向下调整算法的修改3.插入元素和删除元素函…

vue学习之列表渲染

列表渲染 创建 demo8.html,内容如下 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</…

LeetCode算法心得——判断能否在给定时间到达单元格(动态模拟)

大家好&#xff0c;我是晴天学长&#xff0c;这是一个动态模拟题&#xff0c;跟大佬相比&#xff0c;我的有点繁琐了&#xff0c;但是也算是锻炼到自己的一些细节问题&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。 1) .判断能否在给定时间到达单元…

Pytorch面试题整理(2023.09.10)

1、pytorch如何微调fine tuning&#xff1f; 在加载了预训练模型参数之后&#xff0c;需要finetuning 模型&#xff0c;可以使用不同方式finetune。 局部微调&#xff1a;加载了模型参数后&#xff0c;只想调节最后几层&#xff0c;其他层不训练&#xff0c;也就是不进行梯度…

【Endnote】如何出现“作者(年份) found that ....”的格式?

如何出现“作者&#xff08;年份&#xff09; found that ....”的格式&#xff1f; 非常简单&#xff01;先用endnote插入文献后&#xff0c;默认显示&#xff1a; 然后&#xff0c;重点来了&#xff0c;点开这个&#xff1a; 然后&#xff0c;将此处的default改成Display as…

unique_ptr的大小探讨

unique_ptr大小和删除器有很大关系&#xff0c;具体区别看如下代码的分析。不要让unique_ptr占用的空间太大&#xff0c;否则不会达到裸指针同样的效果。 #include <iostream> #include <memory> using namespace std;class Widget {int m_x;int m_y;int m_z;publ…

国产化改造之Mysql迁移方案:Mysql Galera Cluster

一、背景 因某业务系统OS国产化改造&#xff0c;现需将生成环境Mysql 主从迁移到新部署的BCLinux OS主机上&#xff1b;如果保障业务不断&#xff0c;平滑迁移并成功割接将是本次方案的重要方向&#xff0c;现场环境涉及需迁移数据780G左右&#xff0c;目标主机OS版本&#xff…

day35 线程

程序&#xff1a;是为了完成特定的任务&#xff0c;用某种语言编写的一组有序指令的集合&#xff0c;是一段静态的代码 进程&#xff1a;是程序的一次执行过程 线程&#xff1a;线程是进程中的一个执行单元 线程是调度和执行的单位 处理器和线程间的关系 创建线程(重点) 创建…

时序分解 | MATLAB实现MVMD多元变分模态分解信号分量可视化

时序分解 | MATLAB实现MVMD多元变分模态分解信号分量可视化 目录 时序分解 | MATLAB实现MVMD多元变分模态分解信号分量可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 MVMD多元变分模态分解 可直接替换Excel运行包含频谱相关系数图 Matlab语言 1.算法新颖小众&…

LTGNet-超分辨率OCTA图像分割

目录 一、摘要 二、引言 三、方法 A. Reference-based框架 B. Learnable Texture Generator 四、实验 五、总结 一、摘要 研究背景&#xff1a;光学相干断层血管成像(OCTA)是一种新的视网膜微血管成像方式&#xff0c;已广泛应用于临床。 高分辨率OCT血管造影对于定性和…