很经常当我们动态创建某些View时,需要通过获取他们的width和height来确定别的view的布局,但是在onCreate()获取view的width和height会得到0.view.getWidth()和view.getHeight()为0的根本原因是控件还没有完成绘制,你必须等待系统将绘制完View时,才能获得。这种情况当你需要使用动态布局(使用wrap_content或match_parent)就会出现。一般来讲在Activity.onCreate(...)、onResume()方法中都没有办法获取到View的实际宽高。所以,我们必须用一种变通的方法,等到View绘制完成后去获取width和Height。下面有一些可行的解决方案。

1、监听Draw/Layout事件:ViewTreeObserver

ViewTreeObserver监听很多不同的界面绘制事件。一般来说OnGlobalLayoutListener就是可以让我们获得到view的width和height的地方.下面onGlobalLayout内的代码会在View完成Layout过程后调用。

 
 1 view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
2 @Override
3 public void onGlobalLayout() {
4 mScrollView.post(new Runnable() {
5 public void run() {
6 view.getHeight(); //height is ready
7 }
8 });
9 }
10 });
 

但是要注意这个方法在每次有些view的Layout发生变化的时候被调用(比如某个View被设置为Invisible),所以在得到你想要的宽高后,记得移除onGlobleLayoutListener:

在 SDK Lvl < 16时使用
public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)

在 SDK Lvl >= 16时使用
public void removeOnGlobalLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)

2、将一个runnable添加到Layout队列中:View.post()

这个解决方案是我最喜欢的,但是几乎没人知道有这个方法。简单地说,只要用View.post()一个runnable就可以了。runnable对象中的方法会在View的measure、layout等事件后触发,具体的参考Romain Guy

UI事件队列会按顺序处理事件。在setContentView()被调用后,事件队列中会包含一个要求重新layout的message,所以任何你post到队列中的东西都会在Layout发生变化后执行。

 
1 final View view=//smth;
2 ...
3 view.post(new Runnable() {
4 @Override
5 public void run() {
6 view.getHeight(); //height is ready
7 }
8 });
 

这个方法比ViewTreeObserver好:
1、你的代码只会执行一次,而且你不用在在每次执行后将Observer禁用,省心多了。
2、语法很简单

参考:
http://stackoverflow.com/a/3602144/774398
http://stackoverflow.com/a/3948036/774398

3、重写View的onLayout方法

这个方法只在某些场景中实用,比如当你所要执行的东西应该作为他的内在逻辑被内聚、模块化在view中,否者这个解决方案就显得十分冗长和笨重。

 
1 view = new View(this) {
2 @Override
3 protected void onLayout(boolean changed, int l, int t, int r, int b) {
4 super.onLayout(changed, l, t, r, b);
5 view.getHeight(); //height is ready
6 }
7 };
 

需要注意的是onLayout方法会调用很多次,所以要考虑好在这个方法中要做什么,或者在第一次执行后禁用掉你的代码。

附加:获取固定宽高

如果你要获取的view的width和height是固定的,那么你可以直接使用:

1 View.getMeasureWidth()
2 View.getMeasureHeight()

但是要注意,这两个方法所获取的width和height可能跟实际draw后的不一样。官方文档解释了不同的原因

View的大小由width和height决定。一个View实际上同时有两种width和height值。

第一种是measure width和measure height。他们定义了view想要在父View中占用多少width和height(详情见Layout)。measured height和width可以通过getMeasuredWidth() 和 getMeasuredHeight()获得。

第二种是width和height,有时候也叫做drawing width和drawing height。这些值定义了view在屏幕上绘制和Layout完成后的实际大小。这些值有可能跟measure width和height不同。width和height可以通过getWidth()和getHeight获得。

解决获取View的width和Height为0的4种方法的更多相关文章

  1. 解决在onCreate()过程中获取View的width和Height为0的4种方法

    很经常当我们动态创建某些View时,需要通过获取他们的width和height来确定别的view的布局,但是在onCreate()获取view的width和height会得到0.view.getWid ...

  2. 解决在onCreate()过程中获取View的width和Height为0的方法

    最近在看Android底层代码的view绘制原理的时候讲到一个很有意思的事情,也是我几年前刚开始学习Android开发的时候比较纳闷的一个问题,如果你不理解Android的底层绘制,请看我之前一片文章 ...

  3. iPad中控制器view的width和height

    一.iPad中控制器view的width和height 1> 规律 * width 是宽高中最小的那个值 * height 是宽高中最大的那个值 2> 举例(比如窗口根控制器的view,有 ...

  4. Android查缺补漏(View篇)--在 Activity 的 onCreate() 方法中为什么获取 View 的宽和高为0?

    在 Activity 的 onCreate() 方法中为什么获取 View 的宽和高为0 ? @Override protected void onCreate(Bundle savedInstanc ...

  5. Activity中获取view的高度和宽度为0的原因以及解决方案

    在activity中可以调用View.getWidth.View.getHeight().View.getMeasuredWidth() .View.getgetMeasuredHeight()来获得 ...

  6. 使用wpaint绘图软件时:Uncaught DOMException: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The image argument is a canvas element with a width or height of 0.

    使用wpaint绘图软件时:Uncaught DOMException: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': Th ...

  7. Java 获取*.properties配置文件中的内容 ,常见的两种方法

    import java.io.InputStream; import java.util.Enumeration; import java.util.List; import java.util.Pr ...

  8. C#中实现输入汉字获取其拼音(汉字转拼音)的2种方法

    主要介绍了C#中实现输入汉字获取其拼音(汉字转拼音)的2种方法,本文分别给出了使用微软语言包.手动编码实现两种实现方式,需要的朋友可以参考下 本文刚发布时,只写了一个实现方式,使用的是微软的语言包,但 ...

  9. JS获取客户端IP地址、MAC和主机名七种方法

    一.使用JS获取客户端IP的几个方法方法一(只针对IE且客户端的IE允许AcitiveX运行,通过平台:XP,SERVER03,2000).获取客户端IP代码:<HTML><HEAD ...

随机推荐

  1. webpack.config.js====entry入口文件的配置

    1.  一般是采用对象语法: entry: { index: './src/default/js/index.js' }, https://webpack.css88.com/concepts/ent ...

  2. 前后端分离 vue+springboot 跨域 session+cookie失效问题

    环境: 前端 vue   ip地址:192.168.1.205 后端 springboot2.0  ip地址:192.168.1.217 主要开发后端. 问题: 首先登陆成功时将用户存在session ...

  3. intelliJ idea 激活和配置

    1. 双击打开 Intellij IDEA 桌面快捷方式图标,如下图: 2. 点击 ok 按钮,进入激活页面: 3. 他提供的三种方式激活:账号激活,激活码激活,服务器地址激活,我们选择激活码激活,去 ...

  4. 【javascript类库】zepto和jquery的md5加密插件

    [javascript类库]zepto和jquery的md5加密插件 相信很多人对jQuery并不陌生,这款封装良好的插件被很多开发者使用. zepto可以说是jQuery在移动端的替代产品,它比jQ ...

  5. iOS 学习随记 (一)

    入行IT也已经很多年了,厌倦了Windows平台的工作, 4月初突然抽风买了台Mac就开始决定转身做iOS/OS X下的App开发了. 从适应Mac机器到开始编程没有花费太长时间,也因为有C#和Jav ...

  6. windows server 2008 r2 启用 Windows Defender

    单击“开始”,指向“管理工具”,然后单击“服务器管理器”. 在“服务器管理器”中,单击“功能”,然后在“服务器管理器”细节窗格中的“功能摘要”下,单击“添加功能”. 此时会启动“添加功能向导”. 在“ ...

  7. cesium-大规模人群运动测试

    环境:cesium1.57: 笔记本电脑:集成显卡+独显Navida 1060 测试内容:大规模人群运动(500人,可设置运动的路径),可行性及帧率 测试结果:21-23FPS,较为流畅:集显70%- ...

  8. Python学习——1113

    在命令界面直接输入python,进入交互模式,相当于启动了Python解释器,需要一行一行的输入,输入一行,执行一行. 在命令界面直接运行.py文件,相当于直接启动了Python解释器,一次性把.py ...

  9. cityspace

    类别的网址:https://blog.csdn.net/u010069760/article/details/77847595 r,g,b:  250 170 160 parking   244 35 ...

  10. c#中的里氏转换和Java中强制类型转换在多态中的应用

    在c#中: 注意: 子类并没有继承父类的构造函数,而是会默认调用父类那个无参数的构造函数. 如果一个子类继承了一个父类,那么这个子类除了可以使用自己的成员外,还可以使用从父类那里继承过来的成员.但是父 ...