Android的布局体系中,父View负责刷新、布局显示子View;而当子View需要刷新时,则是通知父View来完成。这种处理逻辑在View的代码中明确的表现出来:

  void invalidate(boolean invalidateCache) {            final AttachInfo ai = mAttachInfo;            final ViewParent p = mParent;            //noinspection PointlessBooleanExpression,ConstantConditions            if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {                if (p != null && ai != null && ai.mHardwareAccelerated) {                    // fast-track for GL-enabled applications; just invalidate the whole hierarchy                    // with a null dirty rect, which tells the ViewAncestor to redraw everything                    p.invalidateChild(this, null);                    return;                }            }            if (p != null && ai != null) {                final Rect r = ai.mTmpInvalRect;                r.set(0, 0, mRight - mLeft, mBottom - mTop);                // Don't call invalidate -- we don't want to internally scroll                // our own bounds                p.invalidateChild(this, r);            }        }    }


View调用invalidate时,首先找到自己父View(View的成员变量mParent记录自己的父View),然后将AttachInfo中保存的信息告诉父View刷新自己。

View的父子关系的建立分为两种情况

1) View加入ViewGroup

private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) {        .....            // tell our children        if (preventRequestLayout) {            child.assignParent(this);        } else {            child.mParent = this;        }       .....}

2)DecorView注册给WindowManagerImpl时,产生一个ViewRoot作为其父View

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView){    .....    view.assignParent(this);    ....}

AttachInfo是在View第一次attachWindow时,ViewRoot传给自己的子View的。这个AttachInfo之后,会顺着布局体系一直传递到最底层的View

View.java

void dispatchAttachedToWindow(AttachInfo info, int visibility) {    mAttachInfo = info;    .....}

ViewGroup.java

void dispatchAttachedToWindow(AttachInfo info, int visibility) {    super.dispatchAttachedToWindow(info, visibility);    for (int i = 0; i < count; i++) {        children[i].dispatchAttachedToWindow(info, visibility);    }}

并且在新的View被加入ViewGroup时,也会将该AttachInfo传给加入的View

ViewGroup.java

private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) {    child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));}

到这里明白了mParentAttachInfo代表的意义,可以继续刷新过程的分析。

invalidate中,调用父ViewinvalidateChild,这是一个从第向上回溯的过程,每一层的父View都将自己的显示区域与传入的刷新Rect做交集。

public final void invalidateChild(View child, final Rect dirty) {    ViewParent parent = this;    final AttachInfo attachInfo = mAttachInfo;    if (attachInfo != null) {        final int[] location = attachInfo.mInvalidateChildLocation;        // 需要刷新的子View的位置         location[CHILD_LEFT_INDEX] = child.mLeft;        location[CHILD_TOP_INDEX] = child.mTop;        // If the child is drawing an animation, we want to copy this flag onto        // ourselves and the parent to make sure the invalidate request goes through        final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;        // Check whether the child that requests the invalidate is fully opaque        final boolean isOpaque = child.isOpaque() && !drawAnimation && child.getAnimation() != null;        // Mark the child as dirty, using the appropriate flag        // Make sure we do not set both flags at the same time        final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;        do {            View view = null;            if (parent instanceof View) {                view = (View) parent;            }            if (drawAnimation) {                if (view != null) {                        view.mPrivateFlags |= DRAW_ANIMATION;                } else if (parent instanceof ViewRoot) {                        ((ViewRoot) parent).mIsAnimating = true;                }            }                // If the parent is dirty opaque or not dirty, mark it dirty with the opaque                // flag coming from the child that initiated the invalidate            if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {                view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;            }            parent = parent.invalidateChildInParent(location, dirty);        } while (parent != null);    }} public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {    if ((mPrivateFlags & DRAWN) == DRAWN) {        if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=                        FLAG_OPTIMIZE_INVALIDATE) {            // 根据父View的位置,偏移刷新区域             dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, location[CHILD_TOP_INDEX] - mScrollY);            final int left = mLeft;            final int top = mTop;            //计算实际可刷新区域             if (dirty.intersect(0, 0, mRight - left, mBottom - top) ||                        (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {                mPrivateFlags &= ~DRAWING_CACHE_VALID;                location[CHILD_LEFT_INDEX] = left;                location[CHILD_TOP_INDEX] = top;                return mParent;            }        } else {            mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;            location[CHILD_LEFT_INDEX] = mLeft;            location[CHILD_TOP_INDEX] = mTop;           dirty.set(0, 0, mRight - location[CHILD_LEFT_INDEX],                        mBottom - location[CHILD_TOP_INDEX]);                return mParent;            }        }        return null;}

这个向上回溯的过程直到ViewRoot那里结束ViewRoot对这个最终的刷新区域做刷新

ViewRoot.java

public void invalidateChild(View child, Rect dirty) {    scheduleTraversals();}

另外:

Invalidate()方法不能放在线程中,所以需要把Invalidate()方法放在Handler中。在MyThread中只需要在规定时间内发送一个Message给handler,当Handler接收到消息就调用Invalidate()方法。

postInvalidate()方法就可以放在线程中做处理,就不需要Handler。

而上面的新线程MyThread可以放在OnCreate()中开始,也可以放在OnStart()中开始。

Invalidate()方法和postInvalidate()都可以在主线程中调用而刷新视图。

Invalidate()方法在SDK中是这样描述的:Invalidatethe whole view. If the view is visible, onDraw(Canvas) will be called at somepoint in the future. This must be called from a UI thread. To call from anon-UI thread, call postInvalidate().  当Invalidate()被调用的时候,View的OnDraw()就会被调用,Invalidate()必须是在UI线程中被调用,如果在新线程中更新视图的就调用postInvalidate()。

 










更多相关文章

  1. Android(安卓)GraphicBuffer
  2. Android(安卓)电话的反射调用机制实现静默接听电话
  3. Android(安卓)增量更新实例(Smart App Updates)
  4. Android启动过程
  5. 实现三星S3蒲公英水波纹效果(三)——Activity水波纹实现篇
  6. Android的Service总结
  7. Android(安卓)user defined service handling
  8. Android(安卓)AIDL 实例
  9. android TraceView (图形化性能测试工具)使用入门笔记

随机推荐

  1. Android处理图片透明度并绘画图片
  2. Android中读写文件
  3. Android 中H.264/AVC codec的开发
  4. androidhttp网络通信
  5. android下抓包
  6. Android TTS学习(补充)--我能说中文
  7. ListView 样式自定义
  8. EditText字数限制
  9. ClassLoader(java 与 android 对比)
  10. Android平台一日游