onCreate() 中 View 尚未绘制完成

很多时候,我们需要在某个界面刚刚加载的时候,执行一些对 View 进行操作的代码,通常我们把这些代码放在 ActivityonCreate() 方法或 FragmentonCreateView() 方法中。但因为在这些 Hook 方法被调用时,View 并没有绘制完成,很多操作不能生效。

比如,在 onCreate() 中调用某个按钮的 myButton.getHeight(),得到的结果永远是0。不想花时间慢慢看的同学,可以直接去看最后结论中的解决方案。

Button myButton = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_welcome);
myButton = (Button) findViewById(R.id.button1); // 尝试在 onCreate() 中获取控件尺寸
int h = myButton.getHeight();
Log.i(TAG, "onCreate(): Height=" + h); // 得到的结果是 Height=0 // ...
}

类似的情况还有很多,笔者之前自己写了一个下拉刷新的 ListView,希望在 Fragment 完成绘制后,立刻执行一次刷新,并将代码写在了 onCreateView() 中。结果是,列表确实进行了刷新操作,但是没有下拉刷新的动画。

更晚调用的生命周期函数

笔者开始以为,既然 onCreate() 中,控件尚未绘制完成,那么将代码写在更晚执行的一些生命周期函数中,问题是不是能得到解决呢?之后笔者分别在 Activity 中的一些常用的 Hook 函数中,尝试获取控件的尺寸,得到如下结果(按被执行的顺序排列):

onCreate(): Height=0
onStart(): Height=0
onPostCreate(): Height=0
onResume(): Height=0
onPostResume(): Height=0
onAttachedToWindow(): Height=0
onWindowsFocusChanged(): Height=1845

可以看到,直到 onWinodwsFocusChanged() 函数被调用,我们才能得到正确的控件尺寸。其他 Hook 函数,包括在官方文档中,描述为在 Activity 完全启动后才调用的 onPostCreate()onPostResume() 函数,均不能得到正确的结果。

遗憾的是,虽然在 onWinodwsFocusChanged() 函数中,可以得到正确的控件尺寸。但这只在 Activity 中奏效,而在 Fragment 中,该方法并不能生效。

更多的解决方案

1. 使用 ViewTreeObserver 提供的 Hook 方法。

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_welcome);
myButton = (Button) findViewById(R.id.button1); // 向 ViewTreeObserver 注册方法,以获取控件尺寸
ViewTreeObserver vto = myButton.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
int h = myButton.getHeight();
Log.i(TAG, "Height=" + h); // 得到正确结果 // 成功调用一次后,移除 Hook 方法,防止被反复调用
// removeGlobalOnLayoutListener() 方法在 API 16 后不再使用
// 使用新方法 removeOnGlobalLayoutListener() 代替
myButton.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}); // ...
}

该方法在 onGlobalLayout() 方法将在控件完成绘制后调用,因而可以得到正确地结果。该方法在 Fragment 中,也可以使用。

2. 使用 View 的 post() 方法

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_welcome);
myButton = (Button) findViewById(R.id.button1); // 使用myButton 的 post() 方法
myButton.post(new Runnable() {
@Override
public void run() {
int h = myButton.getHeight();
Log.i(TAG, "Height=" + h); // 得到正确结果
}
}); // ...
}

该方法同样在 Fragment 中适用,是目前笔者发现的最佳解决方案。

注意:通过调用测量对象的 post() 方法注册的 Runnable 总在对象完全绘制后才调用,所以可以得到正确的结果。但直接在 onCreate() 中,使用 new Handler().post(mRunnable) 并不能得到正确的结果。

很多教程中,通过延迟一个毫秒数,即 new Handler().postDelayed(mRunnable, 500),来调用执行实际测量工作的 Runnable。这样虽然能得到正确的结果,但显然不是一个好的解决方案:太小的毫秒数不能保证控件完成绘制,太大的毫秒数,会严重破坏用户的操作体验。

结论

如果需要在某个 View 完成绘制后,立刻执行一段代码(如需要获取控件尺寸),最优雅的做法是:在 onCreate() 等生命周期函数中,调用该 Viewpost() 方法。

(参见上面的代码)

使用 ViewTreeObserver 注册 OnGlobalLayoutListener,或使用 ActivityonWinodwsFocusChanged() 方法,也可以达到相同目的。

Android: 在onCreate()中获得对象尺寸的更多相关文章

  1. Android在onCreate()中获得控件尺寸

    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceSt ...

  2. Android在OnCreate中获取控件的宽度和高度

    在Android中,有时需要对控件进行测量,得到的控件宽度和高度可以用来做一些计算.在需要自适应屏幕的情况下,这种计算就显得特别重要.另一方便,由于需求的原因,希望一进入界面后,就能得到控件的宽度和高 ...

  3. Android 在OnCreate()中获取控件高度与宽度

    试过在OnCreate()中获取控件高度与宽度的童鞋都知道,getWidth()与getHeight()方法返回是0,具体原因 看一下Activity的生命周期 就会明白. 上代码: 方法一: int ...

  4. Android在onCreate中获取控件的宽高

    在某些需求下,我们需要在onCreate的时候就获取到控件的宽高,但是如果直接用view.getWidth()或view.getHeight()会得到0.这是因为在onCreate执行的时候,控件还没 ...

  5. 如何获取Android系统中申请对象的信息

    最近一直在做有关内存方面的优化工作,在做优化的过程,除了关注内存的申请量以及GC的情况之外,我们经常需要想方法找出是那些对象占用了大量内存,以及他们是如何导致GC的,这意味着我们需要获取对象申请的信息 ...

  6. Android中Parcelabel对象的使用和理解

    1. Parcelable接口 Interface for classes whose instances can be written to and restored from a Parcel. ...

  7. Android应用项目中BaseAdapter、SimpleAdapter和ArrayAdapter中的三种适配器

    一.写在前面: 本次我们来讲解一下Android应用中三个适配器:BaseAdapter.SimpleAdapter和ArrayAdapter.其中常见的是BaseAdapter,也是个人推荐使用的适 ...

  8. android Activity类中的finish()、onDestory()和System.exit(0) 三者的区别

    android Activity类中的finish().onDestory()和System.exit(0) 三者的区别 Activity.finish() Call this when your a ...

  9. Android应用如何支持屏幕多尺寸多分辨率问题

    作为Android应用程序开发者都知道android是一个“碎片化”的世界.多种系统版本.多种尺寸.多种分辨率.多种机型,还有不同的厂商定制的不同ROM,你开发的应用会在不可预期的手机上报错.这给开发 ...

随机推荐

  1. VSS2005清除管理员密码

    1.下载工具ultraedit 2.登录到服务器,找到VSS库文件夹,data\um.dat 3.复制到自己桌面,用ultraedit打开,进入 引用内容 00000080h: 55 55 03 29 ...

  2. TCP/IP协议详解---概述

        工作之后,才发现以前在学校里学的东西忘得太快太干净了,现在需要一点点地捡起来了,要不然写几行程序会闹很多笑话会出现很多bug的.从今天开始,翻一翻<TCP/IP协议详解 卷1>这本 ...

  3. CDN公共库、前端开发常用插件一览表(VendorPluginLib)

    =======================================================================================前端CDN公共库===== ...

  4. Linux(五)shell编程基础

    一.Linux shell简介 1.shell概述 Shell 是用户与内核进行交互操作的一种接口,目前最流行的 Shell 称为 bash Shell          Shell 是一门编程语言& ...

  5. Resharper报“Possible multiple enumeration of IEnumerable”

    问题描述:在IEnumerable使用时显示警告 分析:如果对IEnumerable多次读取操作,会有因数据源改变导致前后两次枚举项不固定的风险,最突出例子是读取数据库的时候,第二次foreach时恰 ...

  6. linux shell学习四

    Shell分支语句 case 值 in 模式1) command1 command2 command3 ;; 模式2) command1 command2 command3 ;; *) command ...

  7. nodejs文件压缩-使用gulp命令(安装过程)

    为了代码安全问题,一般发布程序的时候需要将js代码进行压缩,记录一下安装流程.避免忘记. 安装插件

  8. 维护后面的position sg函数概念,离线+线段 bzoj 3339

    3339: Rmq Problem Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1160  Solved: 596[Submit][Status][ ...

  9. C11构造函数的改善

    1.委托构造函数 委托构造函数就是允许在同一个类中一个构造函数可以调用另一个构造函数,从而在初始化时简化变量的初始化. class CTest { public: int x; int y; int ...

  10. 重构改善既有代码设计--重构手法08:Replace Method with Method Object (以函数对象取代函数)

    你有一个大型函数,其中对局部变量的使用,使你无法釆用 Extract Method. 将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的值域(field) 然后你可以在同一个对象中将这个大型 ...