问题原因:view 从GONE 切换为Visible之后 会开启异步消息通知view重绘。所以此时立即获取widht 与 measuredWidth 都是0
解决方法:
//xml 中我们设置view visible= GONE
view.setVisibility(View.VISIBLE);
//在消息队列尾部 追加一个消息,setVisibility 这条消息结束后调用
view.post(new Runnable(){
view.getMeasuredWidth();
view.getWidth();
});
针对问题 源码分析:
- 1.查看GONE
view.setVisibility()>setFlags()
/* Check if the GONE bit has changed */
if ((changed & GONE) != 0) {
needGlobalAttributesUpdate(false);
requestLayout();
...
}
- 查看 view.requestLayout()
public void requestLayout() {
···
//获取viewRootIml.并调用requestLayoutDuringLayout(),待更新的view集合
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
//向上传递
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
···
}
- view.requestLayout最终都是由 ViewRootImpl.requestLayout()来负责调度消息,在消息执行线程中最终调用,Measure,Layout,Draw三个方法。
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
···
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
···
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
···
performTraversals();
···
}
private void performTraversals() {
···
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
//内执行 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
//内执行 host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); host及mView
performDraw();
//内执行 drawSoftware()>mView.draw(canvas);
···
}
总结:View从GONE切为VISIBLE后会 进行重绘。而重绘 与 立即获取view 宽高的代码实际上是 无时序的,这就解释了偶尔能获取偶尔不能。还是要看手机性能?
END
感谢以下博客分析
Android窗口机制(四)ViewRootImpl与View和WindowManager