view 由GONE变为VISIBLE获取宽、高为0,setVisibility(VISIBLE)发生了什么?

  • 问题原因: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();
...
}
    1. 查看 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();
        }
···
}

    1. 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

你可能感兴趣的:(view 由GONE变为VISIBLE获取宽、高为0,setVisibility(VISIBLE)发生了什么?)