通过view.post方法可以保证在UI线程中进行需要的操作,方便地进行异步通信。以下是官方文档对该方法的注释及源码。
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方法并不会执行
private void show(){
if(myView.getVisibility()==View.GONE){
myView.setVisibility(View.VISIBLE);
}
//对view执行动画
}
但运行后却发现view是一瞬间显现的,并没有执行动画,我想你也猜到了,问题在于动画开始时该View还未成功被设置为可见,导致动画失效。
private void show(){
if(myView.getVisibility()==View.GONE){
myView.setVisibility(View.VISIBLE);
}
myView.post(new Runnable() {
@Override
public void run() {
//对view执行动画
}
});
}
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;
}
}