解决获取View的width和Height为0的4种方法

很经常当我们动态创建某些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获得。

时间: 2024-10-29 04:47:47

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

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

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

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

在 Activity 的 onCreate() 方法中为什么获取 View 的宽和高为0 ? @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my_view); myview = ViewUtils.find(this, R.id.myview); getViewSize("onCr

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

在activity中可以调用View.getWidth.View.getHeight().View.getMeasuredWidth() .View.getgetMeasuredHeight()来获得某个view的宽度或高度,但是在onCreate().onStrart().onResume()方法中会返回0,这是应为当前activity所代表的界面还没显示出来没有添加到WindowPhone的DecorView上或要获取的view没有被添加到DecorView上或者该View的visibili

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

主要介绍了C#中实现输入汉字获取其拼音(汉字转拼音)的2种方法,本文分别给出了使用微软语言包.手动编码实现两种实现方式,需要的朋友可以参考下 本文刚发布时,只写了一个实现方式,使用的是微软的语言包,但是对多音字的效果不怎么理想,甚至个别字会出现很诡异的错误,因此,现在扩展另一个方法,手动实现. 方式一.使用微软语言包 微软为了开发者实现国际化语言的互转,提供了Microsoft Visual Studio International Pack,这个扩展包里面有中文.日文.韩文.英语等各国语言包,

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

一.使用JS获取客户端IP的几个方法方法一(只针对IE且客户端的IE允许AcitiveX运行,通过平台:XP,SERVER03,2000).获取客户端IP代码:<HTML><HEAD><TITLE>GetLocalIP</TITLE></HEAD><BODY>获取IP:<script language="JavaScript"> function GetLocalIPAddr(){ var oSetti

view的getWidth() getHeight() 总是返回0 的几种解决方法

当我们在oncreate 调用 getwith() 与 getheigh()时返回0 的原因是我们的view 在此时还没有绘制出来,不光在oncreate .Activity生命周期中,onStart,onResume,onCreate都不是真正visible的时间点,真正的visible时间点是onWindowFocusChanged()函数被执行时. 所以一个解决方法: 就是将 getwith放到onWindowFocusChanged()中.这个onWindowFocusChanged指的

获取鼠标点击相对于Canva位置的2种方法

如果给Canvas添加 onmousedown事件,获取到的鼠标位置都是相对于当前文档的位置(x,y): 第一种转换: (x-x1,y-y1) x,y为鼠标点击位置,getBoundingClientRect方法是canvas自带的获取可绘画区域的位置信息的函数 function windowToCanvas(x, y) { var bbox = canvas.getBoundingClientRect(); return { x: x - bbox.left * (canvas.width /

常用获取Android崩溃日志和IOS崩溃日志的几种方法

一:前言 在日常测试app时,经常会遇到崩溃问题,测试快速抓取到崩溃日志可以有效方便开发进行定位,快速解决问题所在测试做到测试分析,定位是非常重要的,这也是判断一个测试能力指标的一大维度. 二:Android崩溃日志 一.通过adb logcat获取 # 清除日志,新手上路时,日志内容很多,对于能毕现的日志,可以先清除后重新获取 adb logcat -c # 然后再次运行崩溃操作,再抓取日志 # 存储日志到当前目录下的 carsh.log 中 adb logcat -d *:W > crash

利用多线程解决多事务不同定时区间歇触发问题的一种方法

1.背景 项目中需要解决一个这样的问题:有两个事务均需要每隔几秒触发一次,但是两个事务都只是在规定的时间区域内才进行该行为.比如,在早上八点到晚上六点为上班时间,在这个时间段内两个事务必须每隔几秒被执行一次,下班后则停止工作. 2.思路 这个问题最开始我是想利用timer来解决,即做两个timer,一个timer来控制一个事务,然后在各事务对应时间区域内执行. 但是考虑到Timer是在主线程里执行的,Thread 是在自己的线程中执行的,在多核多线程的 CPU 中,利用Thread的效率高更高一