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() 方法,也可以达到相同目的。

最新文章

  1. C#的循环语句练习
  2. Code笔记之:CSS+HTML display 属性
  3. jetBrains phpstorm/webstorm 编辑器使用诀窍
  4. 在同一个机器上运行两个jboss修改配置
  5. immutability-javascript
  6. javascript —— HTTP头文件详解
  7. HTML5实现扫描识别二维码/生成二维码
  8. System.Windows.Forms.Timer反编译学习
  9. 【POJ】1035 Spell checker
  10. SQL Server自定义函数( 转载于51CTO )
  11. C#指定某用户对某文夹件的访问权限
  12. (转)log4j(三)——如何控制不同级别的日志信息的输出?
  13. C# -- 使用Parallel并行执行任务
  14. Spring中BeanFactory的对象注册与依赖绑定方式
  15. Vue使用watch监听一个对象中的属性
  16. Oracle12c的安装
  17. shell编程awk基础介绍
  18. How to resize slide dimensions without resizing any objects on the slide?
  19. 我眼中的Linux系统和红帽RHCE认证
  20. CentOS7使用打开关闭防火墙与端口

热门文章

  1. Stream My Contest UVA - 11865(带权最小树形图+二分最小值最大化)
  2. VLC for Android 编译过程
  3. 【BZOJ4568】幸运数字(线性基,树链剖分,ST表)
  4. 【BZOJ3052】【UOJ#58】【WC2013】糖果公园(树上莫队)
  5. 小Z的袜子 题解报告【莫队】
  6. redis 命令行客户端utf8中文乱码问题
  7. 【原创】【2】rich editor系列教程。了解document.execommand操作,保存丢失的range,实时反馈样式给工具栏
  8. socket编程 ------ sockaddr_in 和 sockaddr 的区别
  9. Windows 2008 R2上配置IIS7或IIS7.5中的URLRewrite(URL重写)实例
  10. 深入浅出CSS(一):line-height与vertical-align的性质