当前位置: 首页 > news >正文

自定义ViewGroup实现流式布局

目录

1、View的绘制流程

2、自定义ViewGroup构造函数的作用

3、onMeasure 方法

3.1、View的度量方式

3.2、onMeasure方法参数的介绍

3.3、自定义ViewGroup onMeasure 方法的实现

4、onLayout方法

5、onDraw方法

6、自定义View的生命周期

7、自定义流式布局的实现

扩展:


1、View的绘制流程

从Activity启动开始,了解View的绘制流程。
图1:Activity启动的流程。
图2:Activity启动的调用流程
图3:显示了View与Window的逻辑结构。
图3中的ViewRootImpl是在图2中的Activity的attach中生成的,是在Activity的onCreate生命周期之前,ViewRootImpl负责处理View 的事件和逻辑。
图4:ViewRootImpl的方法的调用流程
通过ViewRootImpl的performTraversals方法看出View的绘制流程 mearsure、layout、draw,分别对应View及ViewGroup的onMeasure、onLayout和onDraw方法。
图5:Activity在onCreate时使用setContentView 给Activity设置具体的ContentView。

2、自定义ViewGroup构造函数的作用

构造函数用于创建对象,自定义View或ViewGroup会有三个不同用途的构造函数,分别用于
new Object()创建对象、xml解析生成对象和自定义。
/**
* 通过 new FlowLayout() 代码创建
* @param context
*/
public FlowLayout(Context context) {super(context);
}/**
* 通过xml解析创建
* @param context
* @param attrs
*/
public FlowLayout(Context context, AttributeSet attrs) {super(context, attrs);
}/**
* 自定义View 默认的属性以及自定义的属性
* @param context
* @param attrs
* @param defStyleAttr
*/
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
}

3、onMeasure 方法

        可参照LinearLayout或其他布局代码中onMeasure 的具体实现。

3.1、View的度量方式

View的层级结构如上图,在measure时,采用深度递归自上而下进行遍历,在ViewGroup中触发子View的度量。

3.2、onMeasure方法参数的介绍

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
onMeasure方法的参数有2个,分别是widthMeasureSpec和heightMeasureSpec,两个参数分别是 父布局根据父布局的宽高、父布局的padding、当前控件的layout_width或layout_height属性值、当前控件的margin属性值生成的包含width和height相关的mode以及宽高值。
以ViewGroup的width为例,layout_width有三种数值,match_parent、wrap_content和固定值。
当layout_width为match_parent时,分配给的宽度是父布局的宽度。
当layout_width为wrap_content时,因还未知具体需要多大的宽度,所以分配的宽度是父布局的宽度。
当layout_width为固定值时,分配的宽度是固定值。
综上,度量最终计算的宽高只会比参数中给定的宽高小或者相等。

3.3、自定义ViewGroup onMeasure 方法的实现

在自定义ViewGroup时需要实现onMeasure方法,否则layout_width和layout_height属性无效,该ViewGroup的宽高为父控件的宽高。
在重写onMeasure方法时,该方法需要实现的功能如下:
  • 度量子View(深度递归,因为ViewGroup不是叶子节点,一定要度量子View的宽高)
  • 根据子View度量后的宽高计算ViewGroup的宽高
注意
  • 在度量子View的宽高时要考虑ViewGroup的padding和子View的margin值。
  • 在计算ViewGroup的宽高时,要使用子View度量后的宽高,(ViewGroup分配给子View的宽高可能会大,需要子View执行onMeasure方法后确定自己的宽高)
  • 在计算ViewGroup的宽高时要加入ViewGroup的padding值。

4、onLayout方法

在onLayout方法中使用相对父控件的相对布局,而不是相对Android屏幕坐标系的绝对布局,在实现onLayout时只考虑与父控件的关系即可,最终在View绘制时,会将相对位置转换为绝对位置,此工作是由WMS来实现的。
注意:
  • 在自定义ViewGroup的 onLayout中,需考虑ViewGroup的padding和子View的margin值
  • 在自定义View中不用实现onLayout。

5、onDraw方法

ViewGroup的作用在于布局子View,如果需要边框和背景的绘制,在onDraw中实现,如不需要,可不实现。

6、自定义View的生命周期

View的度量、布局和绘制会有多次,但是View的初始化只有一次。

7、自定义流式布局的实现

package com.liluj.androidui.custom;import static com.scwang.smartrefresh.layout.util.SmartUtil.dp2px;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.liluj.androidui.utils.UnitConvertUtils;
import java.util.ArrayList;
import java.util.List;/**
* 自定义流式布局
*/
public class FlowLayout extends ViewGroup {//水平间隔private int horizontalSpace = dp2px(20);//垂直间隔private int veticalSpace = dp2px(20);private List<List<View>> lineViewList; //记录每行的Viewprivate List<Integer> heightList; //记录每列的高度。/*** 通过 new FlowLayout() 代码创建* @param context*/public FlowLayout(Context context) {super(context);}/*** 通过xml解析创建* @param context* @param attrs*/public FlowLayout(Context context, AttributeSet attrs) {super(context, attrs);}/*** 自定义View 默认的属性以及自定义的属性* @param context* @param attrs* @param defStyleAttr*/public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);}private void initMeasure() {/*** 此两个参数是为了给Layout布局用的,界面不只绘制一次,会重新绘制,所以在每次执行onMeasure时要重新初始化,而不是在创建FlowLayout时初始化*/lineViewList = new ArrayList();heightList = new ArrayList<>();}/*** 依据子View的宽高 确定自己的宽高* 注意:* 1、margin值在父控件中处理* 1.1 当前ViewGroup的margin值在父控件已处理* 1.2 在度量和布局子View时要考虑margin值,否则子View的margin属性无用(此例子没处理子View的margin值)* 2、padding值包含在ViewGroup以及View的宽高* 2.1 在ViewGroup和View的 onMeasure、onLayout、onDraw时要考虑自身的padding值* @param widthMeasureSpec horizontal space requirements as imposed by the parent.*                         The requirements are encoded with*                         {@link android.view.View.MeasureSpec}.* @param heightMeasureSpec vertical space requirements as imposed by the parent.*                         The requirements are encoded with*                         {@link android.view.View.MeasureSpec}.**/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);initMeasure();int count = getChildCount();List<View> lineView = new ArrayList<>();int lineUsedWidth = 0; //当前行已使用的宽度int lineHeight = 0; //当前行的高度int mWidth = 0;  //view的宽度int mHeight = 0; //view的高度/*** 父控件根据自己的宽高以及当前View的LayoutParam属性(宽、高、margin)分配给当前View的宽高,固定值就是固定值,* match_parent和wrap_content为父控件的宽度,*/int maxWidth = MeasureSpec.getSize(widthMeasureSpec);//子View可使用的最大宽度,需减去ViewGroup的paddingLeft和paddingRightint childrenCanUseWidth = maxWidth - getPaddingLeft()-getPaddingRight();int maxHeight = MeasureSpec.getSize(heightMeasureSpec);/*** 度量子View并计算ViewGroup属性为wrap_content时实际的宽高*/for(int i = 0;i<count;i++){View view = getChildAt(i);/*** 根据当前View的MeasureSpec属性、padding值和子View的LayoutParams值(android:layout_width和android:layout_height的属性值)来给子View分配空间*/int measureSpecWidth = getChildMeasureSpec(widthMeasureSpec,getPaddingLeft()+getPaddingRight(),view.getLayoutParams().width);int measureSpecHeight = getChildMeasureSpec(heightMeasureSpec,getPaddingTop()+getPaddingBottom(),view.getLayoutParams().height);/*** 触发子View度量自己的空间,子View度量自己的大小后校正子View的大小*/view.measure(measureSpecWidth,measureSpecHeight);int measuredWidth = view.getMeasuredWidth();int measuredHeight = view.getMeasuredHeight();//换行if(lineUsedWidth + measuredWidth > childrenCanUseWidth){lineViewList.add(lineView);heightList.add(lineHeight);mWidth = Math.max(mWidth,lineUsedWidth);mHeight = mHeight + lineHeight + veticalSpace;lineUsedWidth = 0;lineHeight = 0;lineView = new ArrayList<>();}lineView.add(view);lineUsedWidth = lineUsedWidth + measuredWidth + horizontalSpace;lineHeight = Math.max(lineHeight, measuredHeight);/*** 添加上最后一行的宽高*/if(i == count -1){lineViewList.add(lineView);heightList.add(lineHeight);mWidth = Math.max(mWidth,lineUsedWidth);mHeight = mHeight + lineHeight;}}//ViewGroup的宽高加上padding值mWidth = mWidth+getPaddingLeft()+getPaddingRight();mHeight = mHeight+getPaddingTop()+getPaddingBottom();/*** 获取当前View 宽高的mode*/int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);/*** 当View的宽高为固定值时,使用固定值,此处固定值只会比按照wrap_content计算出来的值大,不会小,与父控件分配给当前View的宽高值有关。* 可参见ViewGroup的getChildMeasureSpec方法*/int realWidth = (widthMode == MeasureSpec.EXACTLY) ? maxWidth:mWidth;int realHeight = (heightMode == MeasureSpec.EXACTLY) ? maxHeight:mHeight;setMeasuredDimension(realWidth,realHeight);}/*** 布局子View,布局子View时使用相对坐标(相对父View进行布局)而不是绝对坐标(Window坐标系)* @param changed This is a new size or position for this view* @param l Left position, relative to parent* @param t Top position, relative to parent* @param r Right position, relative to parent* @param b Bottom position, relative to parent*/@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {int curL = getPaddingLeft();int curT = getPaddingTop();for(int i = 0;i< lineViewList.size();i++){List<View> lineView = lineViewList.get(i);for(int j = 0;j<lineView.size();j++){View view = lineView.get(j);int left = curL;int top = curT;int right = curL + view.getMeasuredWidth();int bottom = curT + view.getMeasuredHeight();view.layout(left,top,right,bottom);curL = right + horizontalSpace;}curL = getPaddingLeft();curT = curT + heightList.get(i)+veticalSpace;}}
}

效果图:

扩展:

  • 将xml解析为UI
例如:LinearLayout标签解析为LinearLayout 对象,layout_width,layout_height、layout_weight会生成对应的LayoutParam。
MeasureSpec用于表示View以及ViewGroup的宽高
xml解析采用反射实现。

相关文章:

自定义ViewGroup实现流式布局

目录 1、View的绘制流程 2、自定义ViewGroup构造函数的作用 3、onMeasure 方法 3.1、View的度量方式 3.2、onMeasure方法参数的介绍 3.3、自定义ViewGroup onMeasure 方法的实现 4、onLayout方法 5、onDraw方法 6、自定义View的生命周期 7、自定义流式布局的实现 扩展&#xff…...

Git版本控制

目录 版本控制 概念 为什么需要版本控制&#xff1f; 常见的版本控制工具 Git 1、安装 2、了解基本的Linux命令 3、配置git 用户名和邮箱 4、git 工作模式 5、git 项目管理 6、git 分支 托管平台 远程仓库 Gitee 关联多个远程库 Git服务器 Git GUI 版本控制 概…...

若依之权限处理

若依之权限处理 若依前后端不分离版本使用的是shiro进行权限控制&#xff0c;本文主要是对shiro在若依中的使用进行分析。 RBAC权限模型 RBAC是指基于角色的访问控制。其基本思想是&#xff0c;对系统的各种权限不是直接授予具体的用户&#xff0c;而是在用户集合与权限集合…...

华为OD机试真题 Java 实现【矩阵最大值】【2023 B卷 100分】,附详细解题思路

一、题目描述 给定一个仅包含0和1的N*N的二维矩阵,请计算二维矩阵的最大值。 计算规则如下: 1、每行元素按下标顺序组成一个二进制数(下标越大越排在低位),二进制数的值就是该行的值。矩阵各行值之和为矩阵的值。 2、允许通过向左或向右整体循环移动每行元素来改变各元…...

ModuleNotFoundError: No module named ‘transformers_modules.chatglm-6b_v1‘的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...

MMPretrain代码课

安装注意事项 训练时需要基于算法库源码进行开发&#xff0c;所以需要git clone mmpretrain仓库。如果只调用&#xff0c;则pip install 即可。 from mmpretrain import get_model, list_models,inference_model分别用于模型的获取、例举、推理 此时还没加载预训练权重 tor…...

Selenium自动化程序被检测为爬虫,怎么屏蔽和绕过

Selenium 操作被屏蔽 使用selenium自动化网页时&#xff0c;有一定的概率会被目标网站识别&#xff0c;一旦被检测到&#xff0c;目标网站会拦截该客户端做出的网页操作。 比如淘宝和大众点评的登录页&#xff0c;当手工打开浏览器&#xff0c;输入用户名和密码时&#xff0c…...

Nvidia Jetson Orin:开发技巧

Jetson PXXX定义 P2180 -> Jetson TX1 P3310 -> Jetson TX2 P3489 -> Jetson TX2i P3448 -> Jetson Nano devkit P3448-0020 -> Jetson Nano production module P2888 -> Jetson Xavier P2888-0060 -> Jetson Xavier-8GB P3701 -> Jetson AGX Orin D…...

为什么需要 git 和 相关的小知识

为什么需要git和相关的小知识 先看一个实际需求&#xff0c;引出Git 问题: 公司五一活动计划 ● 先说一个最简单的情况&#xff0c;比如你做了公司五一活动计划书(如图) 解决方案: 版本管理工具(Git) 一句话: Git 是目前最流行的分布式版本控制软件 Git 是怎么来的? Git…...

(详解)vue中实现主题切换的三种方式

目录 一、背景 二、实现思路 方法1&#xff1a;定义全局的CSS变量 方法2&#xff1a;切换已定义好的css文件 方法3&#xff1a;切换顶级CSS类名 (需使用css处理器,如sass、less等) 一、背景 在我们开发中我们会遇到像是需要切换程序风格、主题切换啦这种应用场景。 参考大佬…...

英国皇家植物园采用机器学习预测植物抗疟性,将准确率从 0.46 提升至 0.67

内容一览&#xff1a;疟疾是严重危害人类生命健康的重大传染病&#xff0c;研究人员一直在致力于寻找新的植物源性抗疟疾化合物&#xff0c;以研发相关药物。近期英国皇家植物园利用机器学习 算法 有效预测了植物抗疟性&#xff0c;该研究成果目前已发表在《Frontiers in Plant…...

基于Locust实现MQTT协议服务的压测脚本

一、背景简介 业务背景大概介绍一下&#xff0c;就是按照国标规定&#xff0c;车辆需要上传一些指定的数据到ZF的指定平台&#xff0c;同时车辆也会把数据传到企业云端服务上&#xff0c;于是乎就产生了一些性能需求。 目前我们只是先简单的进行了一个性能场景的测试&#xf…...

AURIX TC3XX Cached PFLASH与Non-Cached PFLASH的区别

Cached ? Non-Cached&#xff1f; 在阅读TC3XX的用户手册时&#xff0c;在内存映射表中&#xff0c;有两个segment都是Program Flash&#xff0c;而且大小都一样是3M&#xff0c;一个是segment 8 另一个是segment10 这难免让人产生疑惑&#xff0c;二者区别在哪&#xff1f; …...

uniapp开发小程序-显示左滑删除效果

一、效果图&#xff1a; 二、代码实现&#xff1a; <template><view class"container"><view class"myorderList"><uni-swipe-action><uni-swipe-action-item class"swipe-action-item" :right-options"option…...

FPGA 的数字信号处理:Verilog 实现简单的 FIR 滤波器

该项目介绍了如何使用 Verilog 实现具有预生成系数的简单 FIR 滤波器。 绪论 不起眼的 FIR 滤波器是 FPGA 数字信号处理中最基本的模块之一&#xff0c;因此了解如何将具有给定抽头数及其相应系数值的基本模块组合在一起非常重要。因此&#xff0c;在这个关于 FPGA 上 DSP 基础…...

使用粒子群优化算法(PSO)辨识锂电池二阶RC模型参数(附MATLAB代码)

目录 一、原理部分 二、代码详解部分 三、结果及分析 一、原理部分 PSO算法由美国学者于 1995 年提出&#xff0c;因其算法简单、效果良好&#xff0c;而在很多领域得到了广泛应用。该算法的起源是模拟鸟群的觅食过程&#xff0c;形成一种群体智能搜索算法。 其核心是&#…...

如何利用地面控制点实现倾斜摄影三维模型数据的几何坐标变换和纠正?

如何利用地面控制点实现倾斜摄影三维模型数据的几何坐标变换和纠正&#xff1f; 倾斜摄影是一种在空中拍摄地表物体的技术&#xff0c;可以获得高分辨率、高精度的三维模型数据&#xff0c;广泛应用于城市规划、建筑设计、土地管理等领域。然而&#xff0c;由于航拍时无法避免姿…...

设计规则之里氏替换原则

tip: 作为程序员一定学习编程之道&#xff0c;一定要对代码的编写有追求&#xff0c;不能实现就完事了。我们应该让自己写的代码更加优雅&#xff0c;即使这会费时费力。 相关规则&#xff1a; 推荐&#xff1a;体系化学习Java&#xff08;Java面试专题&#xff09; 1.6大设…...

【叠高高】叠蛋糕游戏的微信小程序开发流程详解

记得小时候玩过的搭积木游戏吗&#xff0c;和叠高高游戏原理差不多的&#xff0c;与之类似的还有盖高楼游戏&#xff0c;就是看谁盖的&#xff08;叠的&#xff09;最高&#xff0c;这里讲一下比较基础的叠高高游戏小程序实现过程&#xff0c;对编程感兴趣的同学可以参考学习一…...

收集关键词的方法有哪些?(如何查找精准的行业流量关键词)

关键词的收集通常可以通过以下几种方法: 关键词收集方法 1.根据市场价值、搜索词竞争性和企业实际产品特征进行筛选&#xff1a;确定您的关键词列表之前&#xff0c;建议先进行市场分析&#xff0c;了解您的竞争对手、行业状况和目标受众等信息&#xff0c;以更好地了解所需的特…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...