Android View的 getHeight 和 getMeasuredHeight 的区别

前言

先简单复习一下Android View 的 绘制顺序:

1、onMeasure(测量),先根据构造器传进来的LayoutParams(布局参数),测量view宽高。

2、onLayout(布局),再根据测量出来的宽高参数,进行布局

3、onDraw(绘制),最后绘制出View。

ps:案例中用到了dataBinding

1、使用LayoutParams改变View高度

效果:getHeight 和 getMeasuredHeight 的值是一样的,没有区别;

getWidth 和 getMeasuredWidth 也是同理。

        bind.btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) bind.textBox.getLayoutParams();             
                params.height += 10;
                bind.textBox.setLayoutParams(params);
                Log.d("TAG", "更新后 getHeight:" + bind.textBox.getHeight() + " --- getMeasuredHeight:" + bind.textBox.getMeasuredHeight());
            }
        });

2、使用layout改变View高度

效果:getHeight 的值在变化,而 getMeasuredHeight 的值没有变化,还是初始值。

getWidth 和 getMeasuredWidth 也是同理。

	bind.btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                bind.textBox.layout(
                        bind.textBox.getLeft(),
                        bind.textBox.getTop(),
                        bind.textBox.getRight(),
                        bind.textBox.getBottom() + 10
                );
                Log.d("TAG","更新后 getHeight:" + bind.textBox.getHeight() + " --- getMeasuredHeight:" + bind.textBox.getMeasuredHeight());
            }
        });

3、区别

通过以上方式可以看出,使用layout可以改变View 宽或高 度,但并不会更新View原始测量值,即使使用requestLayout()也不行。

源码:因为它是final类型,不可重写。

4、两种获取宽高值的应用场景

1、View布局完成(就是View执行完onLayout),使用 getHeight / getWidth,这是View源码。

    /**
     * Return the height of your view.
     *
     * @return The height of your view, in pixels.
     */
    @ViewDebug.ExportedProperty(category = "layout")
    public final int getHeight() {
        return mBottom - mTop;
    }

2、反之View没有布局完,使用 getMeasuredHeight  / getMeasuredWidth,比如在onCreate中使用可以获取 宽高值,如果在此 使用getHeight / getWidth 返回会是0,因为此时还没有布局完成。

注意:布局未完成前使用getMeasuredHeight  / getMeasuredWidth,要先主动通知系统测量,才会有值,如果布局已经完成,那就直接用,不需要这一步;

通知系统测量方法:measure(0,0),直接都写0就好,系统会返回正确的值。

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        bind = AppActivityMainBinding.bind(getLayoutInflater().inflate(R.layout.app_activity_main, null));
        setContentView(bind.getRoot());

        // 主动通知系统去测量bind.textBox的高度
        // 注意:在onCreate中,此时的布局还未完成,只有执行了这句代码,bind.textBox.getMeasuredHeight() 才会有值
        bind.textBox.measure(0,0);
        bind.showHeight.setText("getHeight:" + bind.textBox.getHeight() + " --- getMeasuredHeight:" + bind.textBox.getMeasuredHeight());    
    }

5、使用layout改变View宽高,会引发的问题

前言提到,onLayout(布局)是根据测量出来的宽高参数,进行布局的,虽然在视觉上改变了宽高,但测量的宽高值还是原始值,没有改变;

而在Android布局体系中,父View负责刷新、布局显示子View,当子View需要刷新时,则是通知父View来完成,就是循环遍历调用子View的 measure / layout / draw 方法。

由此得出,当布局中某个子View布局发生改变,这个父View就开始循环遍历调用子View的layout,通过布尔值changed判断当前子View是否需要重新布局,changed为true表示当前View的大小或位置改变了 。

这时就会发现 之前通过layout改变宽高的View,会被还原因为onLayout(布局)是根据测量出来的宽高参数,进行布局的,重要的话说三遍。

案例:

我新加了一个TextView,将值显示在屏幕上。

        <TextView
            android:id="@+id/show_height"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

View没有发生改变?不,它改变了,但给TextView赋值后,TextView的宽高发生改变,通知父View刷新,父View开始循环遍历子View的layout方法,导致 通过layout改变宽高的View,又根据 原始测量值,重新布局还原了,由于执行的太快,所以视觉上看不到View这个过程。

日志:

20:29:39.933  D  MTextView --- onLayout --- bottom:1127 --- changed:true
20:29:39.935  D  更新TextView,触发MTextView的 onLayout方法
20:29:39.973  D  MTextView --- onLayout --- bottom:1117 --- changed:true

6、案例文件

MTextView.java

public class MTextView extends androidx.appcompat.widget.AppCompatTextView {

    public MTextView(@NonNull Context context) {
        super(context);
    }

    public MTextView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int heightPxValue = MeasureSpec.getSize(heightMeasureSpec);
        Log.d("TAG", "MTextView --- onMeasure --- heightPxValue:" + heightPxValue);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.d("TAG", "MTextView --- onLayout --- bottom:" + bottom + " --- changed:" + changed);
    }

app_activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        tools:context=".ui.activity.AppMainActivity">

        <TextView
            android:id="@+id/show_height"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="update"
            android:textAllCaps="false" />

        <com.example.xxx.ui.view.MTextView
            android:id="@+id/text_box"
            style="@style/Font_303133_15_bold"
            android:layout_width="200dp"
            android:layout_height="100dp"
            android:background="@color/color_66000000"
            android:gravity="center"
            android:text="hello world" />

    </LinearLayout>

</layout>

AppMainActivity.java

public class AppMainActivity extends AppCompatActivity {

    private AppActivityMainBinding bind;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        bind = AppActivityMainBinding.bind(getLayoutInflater().inflate(R.layout.app_activity_main, null));
        setContentView(bind.getRoot());

        // bind.btn.setOnClickListener(new View.OnClickListener() {
        //    @Override
        //    public void onClick(View v) {
        //         LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) bind.textBox.getLayoutParams();             
        //         params.height += 10;
        //         bind.textBox.setLayoutParams(params);
        //         Log.d("TAG", "更新后 getHeight:" + bind.textBox.getHeight() + " --- getMeasuredHeight:" + bind.textBox.getMeasuredHeight());
        //    }
        // });

        // bind.btn.setOnClickListener(new View.OnClickListener() {
        //    @Override
        //    public void onClick(View v) {
        //        bind.textBox.layout(
        //                bind.textBox.getLeft(),
        //                bind.textBox.getTop(),
        //                bind.textBox.getRight(),
        //                bind.textBox.getBottom() + 10
        //        );
        //        Log.d("TAG","更新后 getHeight:" + bind.textBox.getHeight() + " --- getMeasuredHeight:" + bind.textBox.getMeasuredHeight());
        //    }
        // });

        bind.btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                bind.textBox.layout(
                        bind.textBox.getLeft(),
                        bind.textBox.getTop(),
                        bind.textBox.getRight(),
                        bind.textBox.getBottom() + 10
                );
                bind.showHeight.setText("getHeight:" + bind.textBox.getHeight() + " --- getMeasuredHeight:" + bind.textBox.getMeasuredHeight());
                Log.d("TAG","更新TextView,触发MTextView的 onLayout方法");
            }
        });

    }

}

总结

在View没有布局完成前,想要获取 宽高,使用 getMeasuredHeight  / getMeasuredWidth,记得先通知系统测量;

反之只要显示在屏幕上,getHeight / getWidth 就能拿到值,还是时时数据。

补充一下,如果在xml中给View设置了visibility="gone"注意是xml,getHeight / getWidth 也拿不到值,如果是 visibility="invisible",不受影响。

再如果 在xml中给View设置了visibility="gone",在代码中设置成setVisibility(View.VISIBLE)第一次拿不到值,因为还没有layout完成,之后就可以拿到了,后面再给它设置成setVisibility(View.GONE),也不会受影响,因为已经布局过了。代码在这,我都试过了,核心就是看View有没有onLayout完成。

Activity

@Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);

        Log.d("TAG", "getHeight:" + bind.btn.getHeight()); // 0

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.d("TAG", "getHeight:" + bind.btn.getHeight()); // 0
                bind.btn.setVisibility(View.VISIBLE); // 设置显示
                Log.d("TAG", "getHeight:" + bind.btn.getHeight()); // 0
            }
        }, 2000);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.d("TAG", "getHeight:" + bind.btn.getHeight()); // 126
                bind.btn.setVisibility(View.GONE); // 设置消失
                Log.d("TAG", "getHeight:" + bind.btn.getHeight()); // 126
            }
        }, 5000);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.d("TAG", "getHeight:" + bind.btn.getHeight()); // 126
            }
        }, 8000);

    }

Xml

        <Button
            android:id="@+id/btn"
            android:visibility="gone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="update"
            android:textAllCaps="false" />

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

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

相关文章

Baumer工业相机堡盟工业相机如何通过BGAPISDK将相机图像高速保存到电脑内存(C#)

Baumer工业相机堡盟工业相机如何通过BGAPISDK将相机图像高速保存到电脑内存&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机图像保存到电脑内存的技术背景代码分析注册SDK回调函数BufferEvent声明可以存储相机图像的内存序列和名称在图像回调函数中将图像保存在内存序…

C#核心笔记——(三)在C#中创建类型

3.1 类 类是最常见的一种引用类型&#xff0c;最简单的类的声明如下&#xff1a; class MyClass{}而复杂的类可能包含如下内容&#xff1a; 1.在class关键字之前&#xff1a;类的特性&#xff08;Attribute&#xff09;和修饰符。非嵌套的类修饰符有&#xff1a; public、int…

【计算机网络笔记】物理层——基带传输基础

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

kyuubi整合flink yarn session mode

目录 概述配置flink 配置kyuubi 配置kyuubi-defaults.confkyuubi-env.shhive 验证启动kyuubibeeline 连接使用hive catlogsql测试 结束 概述 flink 版本 1.17.1、kyuubi 1.8.0、hive 3.1.3、paimon 0.5 整合过程中&#xff0c;需要注意对应的版本。 注意以上版本 配置 ky…

互联网Java工程师面试题·Spring Cloud篇

目录 1、什么是 Spring Cloud&#xff1f; 2、使用 Spring Cloud 有什么优势&#xff1f; 3、服务注册和发现是什么意思&#xff1f;Spring Cloud 如何实现&#xff1f; 4、负载平衡的意义什么&#xff1f; 5、什么是 Hystrix&#xff1f;它如何实现容错&#xff1f; 6、什么是…

SpringBoot读取properties文字乱码问题及相关问题

问题&#xff1a;在idea的编辑器中properties文件一般用UTF-8编码&#xff0c;SpringBoot2读取解码方式默认不是UTF-8&#xff0c;当值出现中文时SpringBoot读取时出现了乱码。 解决方式1&#xff1a;在SpringBoot框架层面解决&#xff0c;在配置类注解上添加encoding属性值为…

【FPGA图像处理实战】- FPGA图像处理仿真测试工程(读写BMP图片)

FPGA开发过程中“行为功能仿真”是非常必要的一个过程&#xff0c;如果仿真都没通过&#xff0c;则上板测试必定失败。 FPGA图像处理需要读写大量的图像数据&#xff0c;单看这些图像数据实际是没有规则的&#xff0c;如果直接上板测试&#xff0c;调试起来非常困难&#xff0…

【Vue】Vue Router 在 Vue2 项目中的简单使用案例

前言 Vue Router 是 Vue.js 官方的路由管理器。它可以帮助我们在 Vue2 项目中实现页面之间的切换和导航。以下是在 Vue2 项目中使用 Vue Router 的简单案例。 步骤 安装 Vue Router 首先&#xff0c;我们需要安装 vue-router 包。你可以使用 npm 或 yarn 安装&#xff0c;打开…

C语言实现植物大战僵尸(完整版)

实现这个游戏需要Easy_X 这个在我前面一篇C之番外篇爱心代码有程序教你怎么下载&#xff0c;大家可自行查看 然后就是需要植物大战僵尸的素材和音乐&#xff0c;需要的可以在评论区 首先是main.cpp //开发日志 //1导入素材 //2实现最开始的游戏场景 //3实现游戏顶部的工具栏…

Elasticsearch 8.9 flush刷新缓存中的数据到磁盘源码

一、相关API的handler1、接收HTTP请求的hander2、每一个数据节点(node)执行分片刷新的action是TransportShardFlushAction 二、对indexShard执行刷新请求1、首先获取读锁&#xff0c;再获取刷新锁&#xff0c;如果获取不到根据参数决定是否直接返回还是等待2、在刷新之后transl…

【Azure 架构师学习笔记】- Azure Databricks (2) -集群

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Databricks】系列。 接上文 【Azure 架构师学习笔记】- Azure Databricks (1) - 环境搭建 前言 在上文中提到了ADB 的其中一个核心就是集群&#xff0c;所以这里专门研究一下ADB 的集群。 ADB 集群 首先了解一下ADB…

智能优化算法应用:基于饥饿游戏算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于饥饿游戏算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于饥饿游戏算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.饥饿游戏算法4.实验参数设定5.算法结果6.参考…

抽象类和接口(超重点!!)

[本节目标] 1.抽象类 2.接口 3.Object类 1.抽象类 1.1 抽象类概念 在面向对象的概念中&#xff0c;所有的对象都是通过类来描绘的&#xff0c;但是反过来&#xff0c;并不是所有的类都是用来描绘对象的&#xff0c;如果一个类中没有包含足够的信息来描绘一个具体的对象&a…

编程实战:类C语法的编译型脚本解释器(九)编译语句

系列入口&#xff1a;编程实战&#xff1a;类C语法的编译型脚本解释器&#xff08;系列&#xff09;-CSDN博客 前文已经介绍了编译入口&#xff0c;核心就是语句&#xff0c;本文介绍语句的编译。 一、代码概览 代码太长&#xff0c;直接贴&#xff1a; bool GetSentence(CToke…

urllib爬虫 应用实例(三)

目录 一、 ajax的get请求豆瓣电影第一页 二、ajax的get请求豆瓣电影前十页 三、ajax的post请求肯德基官网 一、 ajax的get请求豆瓣电影第一页 目标&#xff1a;获取豆瓣电影第一页的数据&#xff0c;并保存为json文件 设置url&#xff0c;检查 --> 网络 --> 全部 -…

JPA与MySQL锁实战

前言&#xff1a;最近使用jpa和mysql时&#xff0c;遇到了死锁问题。在解决后将一些排查过程中新学到和复习到的知识点再总结整理一下。首先对InnoDB中锁相关的概念进行介绍&#xff0c;然后展示如何利用JPA提供的排他锁来实现想要的功能&#xff0c;最后对死锁问题进行讨论。 …

MOSFET

MOSFET 电子元器件百科 文章目录 MOSFET前言一、MOSFET是什么二、MOSFET类别三、MOSFET应用实例四、MOSFET作用原理总结前言 MOSFET是一种常见的半导体器件,通过栅极电场控制通道区的导通特性,以控制电流流动。它在现代电子电路中发挥着重要的作用,并广泛应用于各种应用领域…

第一百九十一回 自定义TimePicker:一

文章目录 1. 概念介绍2. 思路与方法2.1 整体思路2.2 实现方法3. 示例代码4. 内容总结我们在上一章回中介绍了"如何自定义一个可选择的星期组件"相关的内容,本章回中将介绍" 自定义TimpePicker".闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们在…

弘扬中华文化 感受戏曲魅力——安徽演艺小分队赴和田交流演出

为进一步弘扬中华优秀传统文化&#xff0c;促进皖和两地交往交流交融&#xff0c;12月2日&#xff0c;安徽省演艺小分队走进和田新夜市登台演出&#xff0c;黄梅戏、独唱、民乐演奏、杂技等丰富多样的表演&#xff0c;为观众们送上了一场文化盛宴。 安徽演艺小分队赴和田交流演…

Stable Diffusion AI绘画系列【18】:东方巨龙,威武霸气

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…