自定义View之重写onMeasure

一、重写onMeasure()来修改已有的View的尺寸

步骤

  1. 重写 onMeasure(),并调用 super.onMeasure() 触发原先的测量
  2. 用 getMeasuredWidth() 和 getMeasuredHeight() 取到之前测得的尺寸,利用这两个尺寸来计算出最终尺寸
  3. 使用 setMeasuredDimension() 保存尺寸

代码:


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       //先执行原测量算法
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取原先的测量结果
        int measureWidth=getMeasuredWidth();
        int measureHeight=getMeasuredHeight();
        //利用原先的测量结果计算出新的尺寸
        if(measureWidth>measureHeight){
            measureWidth=measureHeight;
        }else{
            measureHeight=measureWidth;
        }
        //保存计算后的结果
        setMeasuredDimension(measureWidth,measureHeight);
    }
​

二、重写onMeasure()来全新计算自定义View的尺寸

步骤:

  1. 重写 onMeasure0) 把尺寸计算出来
  2. 把计算的结果用 resolveSize() 过滤一遍后保存

  @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        ...
        measuredWidth=...;
        measuredHeight=...;
        
        measuredWidth=resolveSize(measuredWidth,widthMeasureSpec);
        measuredHeight=resolveSize(measuredHeight,heightMeasureSpec);
        
        setMeasuredDimension(measuredWidth,measuredHeight);
    }

       onMeasure()方法的两个参数 widthMeasureSpec和heightMeasureSpec是父View对子View的尺寸限制,子View在计算自己尺寸的时候,需要遵守这两个参数所包含的限制MeasureSpec。

理解MeasureSpec

在 Android 中,View 的大小是由父容器和 View 自身的测量规格(MeasureSpec)共同决定的。

MeasureSpec 由大小和测量模式组成,测量模式有三种取值:

  1. UNSPECIFIED(未指定):父容器对子 View 没有施加任何限制,子 View 可以任意大小。

  2. EXACTLY(精确):父容器已经为子 View 精确指定了大小,子 View 应该匹配这个大小。

  3. AT_MOST(至多):子 View 可以是任何大小,但不能超过父容器指定的大小。

MeasureSpec 是通过静态方法 MeasureSpec.makeMeasureSpec() 创建的,该方法接受两个参数:大小和测量模式。在自定义 View 或者自定义布局中,我们通常会使用 MeasureSpec 来测量子 View 的大小,并根据测量模式来决定子 View 的大小。

在自定义 View 中,我们通常会在 onMeasure() 方法中使用 MeasureSpec 来测量 View 的大小。在这个方法中,我们可以通过 MeasureSpec.getMode() 和 MeasureSpec.getSize() 方法来获取测量模式和大小,然后根据这些信息来确定 View 的最终大小。

解释resolveSize()这个方法:

//代码简化,不是源码
public static int resolveSize(int size, int measureSpec) {
      final int specMode = MeasureSpec.getMode(measureSpec);
        final int specSize = MeasureSpec.getSize(measureSpec);
         switch (specMode) {
            case MeasureSpec.AT_MOST:
                if (specSize < size) {
                    result = specSize | MEASURED_STATE_TOO_SMALL;
                } else {
                    result = size;
                }
                break;
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
            case MeasureSpec.UNSPECIFIED:
            default:
                result = size;
        }
}

resolveSize()这个方法,父View传进来的尺寸限制measureSpec是由类型和尺寸值组成的,首先要调用MeasureSpec.getMode(measureSpec)方法和MeasureSpec.getSize(measureSpec)方法获取限制measureSpec的类型mode和size尺寸值。

限制的类型mode:

MeasureSpec.AT_MOST 限制上线

MeasureSpec.EXACTLY 限制固定尺寸

MeasureSpec.UNSPECIFIED 无限制

三、重写onMeasure()和onLayout()来全新计算自定义ViewGroup的内部布局

onMeasure()的重写,对于ViewGroup来说,包含三部分内容:

步骤:

  1. 调用每个子View的measure(),让子View自我测量
  2. 根据子View给出的尺寸,得出子View的位置,并保存它们的位置和尺寸
  3. 根据子View的位置和尺寸计算出自己的尺寸,并用setMeasuredDimension()保存

理解LayoutParams

       在父View里调用子View的getLayoutParams()方法,可以获得一个LayoutParams对象,它包含了xml文件里的layout_打头的参数的对应值,其中它的width和height这两个属性就分别对应了layout_width和layout_height的值,并且是转换过了的值。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
​
        for(int i=0;i<getChildCount();i++){
            View childView=getChildAt(i);
            LayoutParams lp=childView.getLayoutParams();
            //lp.height   lp.width
        }
    }

结合自己的可用空间来计算出对子View的宽度和高度的限制

可以根据layout_width和layout_height的值,分成三种情况:

第一种情况:固定值

不需要考虑可用空间的问题,直接用EXACTLY把子View尺寸限制为这个固定值就可以了。

第二种情况:match_parent

把子View的尺寸限制为固定值可用宽度或者高度

可用空间的判断方法:

根据自己的MeasureSpec中mode的不同:

1.EXACTLY/AT_MOST   可用空间:MeasureSpec中的size

2.UNSPECIFIED     可用空间:无限大

第三种情况:wrap_content

不能超过父View的边界的情况下,子View自己测量

public class SomeView extends ViewGroup {
...

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        ...
        for(int i=0;i<getChildCount();i++){
            View childView=getChildAt(i);

            LayoutParams lp=childView.getLayoutParams();
            int selfwidthSpecMode=MeasureSpec.getMode(widthMeasureSpec);
            int selfwidthSpecSize=MeasureSpec.getSize(widthMeasureSpec);
            switch (lp.width){
                case MATCH_PARENT:
                    if(selfwidthSpecMode==EXACTLY||selfwidthSpecMode==MeasureSpec.AT_MOST){
                        childWidthSpec=MeasureSpec.makeMeasureSpec(selfwidthSpecSize-usedWidth,EXACTLY);
                    }else{
                        childWidthSpec=MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);
                    }
                    break;
                case  WRAP_CONTENT:
                    if(selfwidthSpecMode==EXACTLY||selfwidthSpecMode==MeasureSpec.AT_MOST){
                        childWidthSpec=MeasureSpec.makeMeasureSpec(selfwidthSpecSize-usedWidth,MeasureSpec.AT_MOST);
                    }else{
                        childWidthSpec=MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);
                    }
                    break;
                default:
                    childWidthSpec=MeasureSpec.makeMeasureSpec(lp.width, EXACTLY);
                    break;
            }
        }
    }
}

关于保存子View位置的两点说明

1.不是所有的Layout都需要保存子View的位置(因为有的Layout可以在布局阶段实时推导出子View的位置,例如LinearLayout)

2.有时候对某些子View需要重复测量两次或多次才能得到正确的尺寸和位置

重写onLayout()来摆放子View

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        for(int i=0;i<getChildCount();i++){
            View childView=getChildAt(i);
            childView.layout(childLeft[i],childTop[i],childRight[i],childBottom[i]);
        }
    }

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[通俗易懂]c语言中指针变量和数值之间的关系

一、指针变量的定义 在C语言中&#xff0c;指针变量是一种特殊类型的变量&#xff0c;它存储的是另一个变量的内存地址。指针变量可以用来间接访问和操作内存中的其他变量。指针变量的定义如下&#xff1a; 数据类型 *指针变量名&#xff1b;其中&#xff0c;数据类型可以是任…

SQL优化:执行计划

前面我们讲述了使用索引或分区表来进行存储层次的优化,也讲述了通过条件提升进行结果集的优化。这边文章我们来学习一下其中的细节,即查看数据库是怎么一步一步把数据拿给我们的。也就是执行计划。 语法 explain sql语句 练习 首先,我们来玩下简单的 explain select * …

HUAWEI WATCH 系列 eSIM 全新开通指南来了

HUAWEI WATCH 系列手表提供了eSIM硬件能力&#xff0c;致力为用户提供更便捷、高效的通信体验。但eSIM 业务是由运营商管理并提供服务的&#xff0c;当前运营商eSIM业务集中全面恢复&#xff0c;电信已经全面恢复&#xff0c;移动大部分省份已经全面放开和多号App开通方式&…

20240107移远的4G模块EC20在Firefly的AIO-3399J开发板的Android11下调通能上网

20240107移远的4G模块EC20在Firefly的AIO-3399J开发板的Android11下调通能上网 2024/1/7 11:17 开发板&#xff1a;Firefly的AIO-3399J【RK3399】SDK&#xff1a;rk3399-android-11-r20211216.tar.xz【Android11】 Android11.0.tar.bz2.aa【ToyBrick】 Android11.0.tar.bz2.ab …

Docker mysql 主从复制

目录 介绍&#xff1a;为什么需要进行mysql的主从复制 主从复制原理&#xff1a; ✨主从环境搭建 主从一般面试问题&#xff1a; 介绍&#xff1a;为什么需要进行mysql的主从复制 在实际的生产中&#xff0c;为了解决Mysql的单点故障已经提高MySQL的整体服务性能&#xff…

如何恢复Mac误删文件?

方法1. 使用撤消命令 当你在 Mac 上删除了错误的文件并立即注意到你的错误时&#xff0c;你可以使用撤消命令立即恢复它。顾名思义&#xff0c;此命令会反转上次完成的操作&#xff0c;并且有多种方法可以调用它。如果你已经采取了其他操作或退出了用于删除文件的应用程序&…

算法与数据结构之数组(Java)

目录 1、数组的定义 2、线性结构与非线性结构 3、数组的表现形式 3.1 一维数组 3.2 多维数组 4、重要特性&#xff1a;随机访问 5、ArrayList和数组 6、堆内存和栈内存 7、数组的增删查改 7.1 插入数据 7.2 删除一个数据 7.3 修改数组 7.4 查找数据 8、总结 什么…

蓝桥杯基础知识2 全排列 next_permutation(), prev_permutation()

蓝桥杯基础知识2 全排列 next_permutation()&#xff0c; prev_permutation() #include<bits/stdc.h> using namespace std;int a[10];int main(){for(int i 1; i < 4; i)a[i] i; //4*3*2*1 24bool tag true;while(tag){for(int i1; i < 4; i)cout << a[…

Fiddler工具 — 8.会话列表(Session List)

1、会话列表说明 Fiddler抓取到的每条HTTP请求&#xff08;每一条称为一个session&#xff09;。 主要包含了请求的ID编号、状态码、协议、主机名、URL、内容类型、body大小、进程信息、自定义备注等信息。如下图&#xff1a; 说明&#xff1a; 名称含义#抓取HTTP Request的顺…

电脑如何屏幕录制?轻松录制高清视频

在当今信息化的时代&#xff0c;电脑已经成为工作和生活的重要工具。无论是在进行演示、教学还是记录重要操作步骤时&#xff0c;屏幕录制都是非常有用的。可是电脑如何屏幕录制呢&#xff1f;本篇文章将介绍三种常见的电脑屏幕录制方法&#xff0c;通过学习这些方法&#xff0…

[C#]使用DlibDotNet人脸检测人脸68特征点识别人脸5特征点识别人脸对齐人脸比对FaceMesh

【官方框架地址】 https://github.com/takuya-takeuchi/DlibDotNet 【算法介绍】 DlibDotNet是一个开源的.NET库&#xff0c;用于实现机器学习和计算机视觉应用。它基于C库dlib&#xff0c;通过C/CLI封装了dlib的所有功能&#xff0c;为.NET开发者提供了简单易用的API。以下是…

力扣刷题记录(29)LeetCode:695、1020、130

695. 岛屿的最大面积 这道题和计算岛屿周长类似&#xff0c;在这里dfs的功能就是由一块陆地出发&#xff0c;找出这块陆地所在的岛屿并返回岛屿面积。 class Solution { public:int dfs(vector<vector<int>>& grid,int i,int j){if(i<0||i>grid.size())…

微信小程序 获取地址信息(uniapp)

参考API地址&#xff1a;微信小程序JavaScript SDK | 腾讯位置服务 <script> // 引入SDK核心类&#xff0c;js文件根据自己业务&#xff0c;位置可自行放置var QQMapWX require(../../js/uploadImg/qqmap-wx-jssdk.js);export default {data(){return{qqmapsdk:}},onL…

【Spring Cloud】组件概念详解

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《Spring Cloud》。&#x1f3af;&#x1f3af; &am…

Hive精选10道面试题

1.Hive内部表和外部表的区别&#xff1f; 内部表的数据由Hive管理&#xff0c;外部表的数据不由Hive管理。 在Hive中删除内部表后&#xff0c;不仅会删除元数据还会删除存储数据&#xff0c; 在Hive中删除外部表后&#xff0c;只会删除元数据但不会删除存储数据。 内部表一旦…

odoo17 | 用户界面的基本交互

前言 现在我们已经创建了我们的新模型及其 相应的访问权限&#xff0c;是时候了 与用户界面交互。 在本章结束时&#xff0c;我们将创建几个菜单以访问默认列表 和窗体视图。 数据文件 &#xff08;XML&#xff09; Odoo在很大程度上是数据驱动的&#xff0c;因此模块定义的…

pytoch安装

pytoch安装 1. 准备工作1.1 需要提前安装的软件 2. 安装pyTorch我遇到的问题 3. 显卡测试4. CPU与GPU切换方法4.1 创建张量4.2 第一种切换方法4.3 第二种切换方法 1. 准备工作 1.1 需要提前安装的软件 Anaconda 史上最全最详细的Anaconda安装教程CUDA CUDA安装教程&#xff0…

Python笔记06-文件操作

文章目录 文件的编码文件读取文件写入文件追加 文件的编码 编码技术即&#xff1a;翻译的规则&#xff0c;记录了如何将内容翻译成二进制&#xff0c;以及如何将二进制翻译回可识别内容。算机中有许多可用编码&#xff1a;UTF-8、GBK、Big5等 不同的编码&#xff0c;将内容翻译…

其他排序(基数排序,希尔排序和桶排序)(数据结构课设篇3,python版)(排序综合)

本篇博客主要详细讲解一下其他排序&#xff08;基数排序&#xff0c;希尔排序和桶排序&#xff09;也是排序综合系列里最后一篇博客。第一篇博客讲解的是LowB三人组&#xff08;冒泡排序&#xff0c;插入排序&#xff0c;选择排序&#xff09;&#xff08;数据结构课设篇1&…

【C++】深入了解构造函数之初始化列表

目录 一、再谈构造函数 1、引入 1&#xff09;构造函数体赋值 2&#xff09;不同成员变量赋值 2、初始化列表 一、再谈构造函数 1、引入 1&#xff09;构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值…