Android: 在onCreate()中获得对象尺寸
onCreate() 中 View 尚未绘制完成
很多时候,我们需要在某个界面刚刚加载的时候,执行一些对 View
进行操作的代码,通常我们把这些代码放在 Activity
的 onCreate()
方法或 Fragment
的 onCreateView()
方法中。但因为在这些 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()
等生命周期函数中,调用该 View
的 post()
方法。
(参见上面的代码)
使用 ViewTreeObserver
注册 OnGlobalLayoutListener
,或使用 Activity
的 onWinodwsFocusChanged()
方法,也可以达到相同目的。
最新文章
- C#的循环语句练习
- Code笔记之:CSS+HTML display 属性
- jetBrains phpstorm/webstorm 编辑器使用诀窍
- 在同一个机器上运行两个jboss修改配置
- immutability-javascript
- javascript —— HTTP头文件详解
- HTML5实现扫描识别二维码/生成二维码
- System.Windows.Forms.Timer反编译学习
- 【POJ】1035 Spell checker
- SQL Server自定义函数( 转载于51CTO )
- C#指定某用户对某文夹件的访问权限
- (转)log4j(三)——如何控制不同级别的日志信息的输出?
- C# -- 使用Parallel并行执行任务
- Spring中BeanFactory的对象注册与依赖绑定方式
- Vue使用watch监听一个对象中的属性
- Oracle12c的安装
- shell编程awk基础介绍
- How to resize slide dimensions without resizing any objects on the slide?
- 我眼中的Linux系统和红帽RHCE认证
- CentOS7使用打开关闭防火墙与端口
热门文章
- Stream My Contest UVA - 11865(带权最小树形图+二分最小值最大化)
- VLC for Android 编译过程
- 【BZOJ4568】幸运数字(线性基,树链剖分,ST表)
- 【BZOJ3052】【UOJ#58】【WC2013】糖果公园(树上莫队)
- 小Z的袜子 题解报告【莫队】
- redis 命令行客户端utf8中文乱码问题
- 【原创】【2】rich editor系列教程。了解document.execommand操作,保存丢失的range,实时反馈样式给工具栏
- socket编程 ------ sockaddr_in 和 sockaddr 的区别
- Windows 2008 R2上配置IIS7或IIS7.5中的URLRewrite(URL重写)实例
- 深入浅出CSS(一):line-height与vertical-align的性质