Android开发竟这样获取View宽高?View.post()原理大揭秘
问题背景
在某些场景下,我们需要在Activity的onCreate方法中获取View的宽高,如果我们直接使用getMeasuredHeight()或是getMeasuredWidth()去获取,得到的值都是0
解决方案
- 推荐方案:View.post() ✅ 自动处理视图状态,代码简洁
- ViewTreeObserver
- 通过MeasureSpec自行测量宽高
使用postDelay()延迟一段时间(不推荐,存在时序风险)
View.post()用法案例
java
// 在Activity的onCreate()中
View myView = findViewById(R.id.my_view);
myView.post(new Runnable() {
@Override
public void run() {
int width = myView.getWidth();
int height = myView.getHeight();
Log.d("ViewSize", "Width: " + width + ", Height: " + height);
}
});
View.post()原理要点
核心机制 :HandlerActionQueue
+ AttachInfo
双通道保障
- ▶️ Attach前:通过HandlerActionQueue缓存任务
- ▶️ Attach后:通过主线程Handler立即执行
- 🚨 关键限制:必须依附View树才能生效(单独new的View无效)
post方法源码
java
public boolean post(Runnable action) {
//1
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
//2
getRunQueue().post(action);
return true;
}
主要分为1,2两部分
- 如果AttachInfo不为空则调用mHandler.post(action)方法,attachInfo赋值在dispatchAttachedToWindow方法中,该方法会在view执行绘制、测量的时候调用
java
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
//1、mAttachInfo赋值
mAttachInfo = info;
//2、 执行之前挂起的所有任务,这里的任务是通过 getRunQueue().post(action)挂起的任务, 也就是View.post()方法中当attachInfo为null时执行的那行代码
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
//3、回调View的onAttachedToWindow方法,该方法在onResume之后,View绘制之前执行
onAttachedToWindow();
}
- 如果attachInfo为空也就是当前执行View.post()方法时还没有绘制view,此时会执行getRunQueue().post(action)
java
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
HandlerActionQueue中主要是将Runnable任务保存到了队列中,等到dispatchAttachedToWindow的时机进行执行。也就是说即使我们调用View.post()方法的时候还没有执行view的绘制流程,View.post()方法也会将我们的Runnable任务挂起,等待合适的时机执行
总结
要点 | 说明 |
---|---|
最佳实践 | 优先使用View.post()获取视图尺寸 |
执行时机 | 保证在measure/layout之后,首帧绘制前执行 |
线程安全 | 自动切换到主线程执行 |
注意事项 | View必须已附加到Window,单独创建的View对象无法生效 |