view.post()

通过view.post方法可以保证在UI线程中进行需要的操作,方便地进行异步通信。以下是官方文档对该方法的注释及源码。

Causes the Runnable to be added to the message queue.The runnable will be run on the user interface thread.
public boolean post(Runnable action) {    
  final AttachInfo attachInfo = mAttachInfo;
  if (attachInfo != null) {       
  return attachInfo.mHandler.post(action);
  }    
// Assume that post will succeed later       
  ViewRootImpl.getRunQueue().post(action); 
  return true;
}

意思是将任务添加到消息队列中,保证在UI线程执行。从本质上说,它还是依赖于以Handler、Looper、MessageQueue、Message为基础的异步消息处理机制。相对于新建Handler进行处理更加便捷。下面举一个常用的例子,比如在onCreate方法中获取某个view的宽高,而直接View#getWidth获取到的值是0。要知道View显示到界面上需要经历onMeasure、onLayout和onDraw三个过程,而View的宽高是在onLayout阶段才能最终确定的,而在Activity#onCreate中并不能保证View已经执行到了onLayout方法,也就是说Activity的声明周期与View的绘制流程并不是一一绑定。那为什么调用post方法就能起作用呢?首先MessageQueue是按顺序处理消息的,而在setContentView()后队列中会包含一条询问是否完成布局的消息,而我们的任务通过View#post方法被添加到队列尾部,保证了在layout结束以后才执行。

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.my_text);
        // 下面这一行log打印的是0,0
        Log.d("test", "mTextView width : " + mTextView.getMeasuredWidth() + " - height : " + mTextView.getMeasuredHeight());
        mTextView.post(new Runnable() {

            @Override
            public void run() {
                // 下面这一行log打印的是TextView测量后的宽高
                Log.d("test", "mTextView width : " + mTextView.getMeasuredWidth() + " - height : " + mTextView.getMeasuredHeight());
            }
        });
    }

1、执行post,此时View是已经开始被meaure

2、由于这个方法是在主线程中才执行的,如果是子线程调用了view.post,post方法并不会执行

下面我举一个例子,加深大家的理解。假如你有一个需求,需要在某个页面中加入一个点击事件:从AppBar(ActionBar)下滑出一个控件,并且在该控件出现的同时依然可以点击AppBar上的MenuItem。因此不能使用popupWindow,只能将该控件放置于布局内,同时为了不阻碍其他控件的交互,需要将该view的可见性设置为GONE。于是你写下下面的代码
private void show(){
    if(myView.getVisibility()==View.GONE){
     myView.setVisibility(View.VISIBLE);
     }
    //对view执行动画
  }
但运行后却发现view是一瞬间显现的,并没有执行动画,我想你也猜到了,问题在于动画开始时该View还未成功被设置为可见,导致动画失效。
而把动画执行加入到View#post中后可以达到理想的效果
private void show(){
  if(myView.getVisibility()==View.GONE){
     myView.setVisibility(View.VISIBLE);
  }
     myView.post(new Runnable() {
            @Override
            public void run() {
                //对view执行动画
            }
        });
  }

原因在于将view的可见性从GONE设置为VISIBLE时会申请requestLayout,而layout是异步执行的,所以setVisibility方法返回了但是该操作还未完成。前面说了post方法可以保证新任务是在layout调用过后执行。下面是setVisibility方法中比较关键的代码:

if (newVisibility == VISIBLE) {
        if ((changed & VISIBILITY_MASK) != 0) {
            /*
             * If this view is becoming visible, invalidate it in case it changed while
             * it was not visible. Marking it drawn ensures that the invalidation will
             * go through.
             */
            mPrivateFlags |= PFLAG_DRAWN;
            invalidate(true);

            needGlobalAttributesUpdate(true);

            // a view becoming visible is worth notifying the parent
            // about in case nothing has focus.  even if this specific view
            // isn't focusable, it may contain something that is, so let
            // the root view try to give this focus if nothing else does.
            if ((mParent != null) && (mBottom > mTop) && (mRight > mLeft)) {
                mParent.focusableViewAvailable(this);
            }
        }
    }

    /* Check if the GONE bit has changed */
    if ((changed & GONE) != 0) {
        needGlobalAttributesUpdate(false);
        // 这里申请了重新布局
        requestLayout();

        if (((mViewFlags & VISIBILITY_MASK) == GONE)) {
            if (hasFocus()) clearFocus();
            clearAccessibilityFocus();
            destroyDrawingCache();
            if (mParent instanceof View) {
                // GONE views noop invalidation, so invalidate the parent
                ((View) mParent).invalidate(true);
            }
            // Mark the view drawn to ensure that it gets invalidated properly the next
            // time it is visible and gets invalidated
            mPrivateFlags |= PFLAG_DRAWN;
        }
        if (mAttachInfo != null) {
            mAttachInfo.mViewVisibilityChanged = true;
        }
    }

http://www.jianshu.com/p/b1d5e31e2011
https://my.oschina.net/carbenson/blog/1477290






你可能感兴趣的:(android2017)