Android视图状态及重绘流程分析,带你一步步深入了解View(三)



  1.         final AttachInfo ai = mAttachInfo;  
  2.         final ViewParent p = mParent;  
  3.         if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {  
  4.             if (p != null && ai != null && ai.mHardwareAccelerated) {  
  5.                 p.invalidateChild(thisnull);  
  6.                 return;  
  7.             }  
  8.         }  
  9.         if (p != null && ai != null) {  
  10.             final Rect r = ai.mTmpInvalRect;  
  11.             r.set(00, mRight - mLeft, mBottom - mTop);  
  12.             p.invalidateChild(this, r);  
  13.         }  
  14.     }  
  15. }  
在这个方法中首先会调用skipInvalidate()方法来判断当前View是否需要重绘,判断的逻辑也比较简单,如果View是不可见的且没有执行任何动画,就认为不需要重绘了。之后会进行透明度的判断,并给View添加一些标记位,然后在第22和29行调用 ViewParent的invalidateChild()方法,这里的ViewParent其实就是当前视图的父视图,因此会调用到ViewGroup的invalidateChild()方法中,代码如下所示:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public final void invalidateChild(View child, final Rect dirty) {  
  2.     ViewParent parent = this;  
  3.     final AttachInfo attachInfo = mAttachInfo;  
  4.     if (attachInfo != null) {  
  5.         final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;  
  6.         if (dirty == null) {  
  7.             ......  
  8.         } else {  
  9.             ......  
  10.             do {  
  11.                 View view = null;  
  12.                 if (parent instanceof View) {  
  13.                     view = (View) parent;  
  14.                     if (view.mLayerType != LAYER_TYPE_NONE &&  
  15.                             view.getParent() instanceof View) {  
  16.                         final View grandParent = (View) view.getParent();  
  17.                         grandParent.mPrivateFlags |= INVALIDATED;  
  18.                         grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;  
  19.                     }  
  20.                 }  
  21.                 if (drawAnimation) {  
  22.                     if (view != null) {  
  23.                         view.mPrivateFlags |= DRAW_ANIMATION;  
  24.                     } else if (parent instanceof ViewRootImpl) {  
  25.                         ((ViewRootImpl) parent).mIsAnimating = true;  
  26.                     }  
  27.                 }  
  28.                 if (view != null) {  
  29.                     if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&  
  30.                             view.getSolidColor() == 0) {  
  31.                         opaqueFlag = DIRTY;  
  32.                     }  
  33.                     if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) {  
  34.                         view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;  
  35.                     }  
  36.                 }  
  37.                 parent = parent.invalidateChildInParent(location, dirty);  
  38.                 if (view != null) {  
  39.                     Matrix m = view.getMatrix();  
  40.                     if (!m.isIdentity()) {  
  41.                         RectF boundingRect = attachInfo.mTmpTransformRect;  
  42.                         boundingRect.set(dirty);  
  43.                         m.mapRect(boundingRect);  
  44.                         dirty.set((int) boundingRect.left, (int) boundingRect.top,  
  45.                                 (int) (boundingRect.right + 0.5f),  
  46.                                 (int) (boundingRect.bottom + 0.5f));  
  47.                     }  
  48.                 }  
  49.             } while (parent != null);  
  50.         }  
  51.     }  
  52. }  
可以看到,这里在第10行进入了一个while循环,当ViewParent不等于空的时候就会一直循环下去。在这个while循环当中会不断地获取当前布局的父布局,并调用它的invalidateChildInParent()方法,在ViewGroup的 invalidateChildInParent()方法中主要是来计算需要重绘的矩形区域,这里我们先不管它,当循环到最外层的根布局后,就会调用ViewRoot的invalidateChildInParent()方法了,代码如下所示:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {  
  2.     invalidateChild(null, dirty);  
  3.     return null;  
  4. }  
这里的代码非常简单,仅仅是去调用了invalidateChild()方法而已,那我们再跟进去瞧一瞧吧:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void invalidateChild(View child, Rect dirty) {  
  2.     checkThread();  
  3.     if (LOCAL_LOGV) Log.v(TAG, "Invalidate child: " + dirty);  
  4.     mDirty.union(dirty);  
  5.     if (!mWillDrawSoon) {  
  6.         scheduleTraversals();  
  7.     }  
  8. }  
这个方法也不长,它在第6行又调用了scheduleTraversals()这个方法,那么我们继续跟进:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void scheduleTraversals() {  
  2.     if (!mTraversalScheduled) {  
  3.         mTraversalScheduled = true;  
  4.         sendEmptyMessage(DO_TRAVERSAL);  
  5.     }  
  6. }  
可以看到,这里调用了sendEmptyMessage()方法,并传入了一个DO_TRAVERSAL参数。了解Android异步消息处理机制的朋友们都会知道,任何一个Handler都可以调用sendEmptyMessage()方法来发送消息,并且在handleMessage()方法中接收消息,而如果你看一下ViewRoot的类定义就会发现,它是继承自Handler的,也就是说这里调用 sendEmptyMessage()方法出的消息,会在ViewRoot的handleMessage()方法中接收到。那么赶快看一下handleMessage()方法的代码吧,如下所示:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void handleMessage(Message msg) {  
  2.     switch (msg.what) {  
  3.     case DO_TRAVERSAL:  
  4.         if (mProfile) {  
  5.             Debug.startMethodTracing("ViewRoot");  
  6.         }  
  7.         performTraversals();  
  8.         if (mProfile) {  
  9.             Debug.stopMethodTracing();  
  10.             mProfile = false;  
  11.         }  
  12.         break;  
  13.     ......  
  14. }  

熟悉的代码出现了!这里在第7行调用了performTraversals()方法,这不就是我们在前面一篇文章中学到的视图绘制的入口吗?虽然经过了很多辗转的调用,但是可以确定的是,调用视图的invalidate()方法后确实会走到performTraversals()方法中,然后重新执行绘制流程。之后的流程就不需要再进行描述了吧,可以参考Android视图绘制流程完全解析,带你一步步深入了解View(二) 这一篇文章。

了解了这些之后,我们再回过头来看看刚才的selectDrawable()方法中到底做了什么才能够控制背景图的改变,代码如下所示:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public boolean selectDrawable(int idx) {  
  2.     if (idx == mCurIndex) {  
  3.         return false;  
  4.     }  
  5.     final long now = SystemClock.uptimeMillis();  
  6.     if (mDrawableContainerState.mExitFadeDuration > 0) {  
  7.         if (mLastDrawable != null) {  
  8.             mLastDrawable.setVisible(falsefalse);  
  9.         }  
  10.         if (mCurrDrawable != null) {  
  11.             mLastDrawable = mCurrDrawable;  
  12.             mExitAnimationEnd = now + mDrawableContainerState.mExitFadeDuration;  
  13.         } else {  
  14.             mLastDrawable = null;  
  15.             mExitAnimationEnd = 0;  
  16.         }  
  17.     } else if (mCurrDrawable != null) {  
  18.         mCurrDrawable.setVisible(falsefalse);  
  19.     }  
  20.     if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {  
  21.         Drawable d = mDrawableContainerState.mDrawables[idx];  
  22.         mCurrDrawable = d;  
  23.         mCurIndex = idx;  
  24.         if (d != null) {  
  25.             if (mDrawableContainerState.mEnterFadeDuration > 0) {  
  26.                 mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;  
  27.             } else {  
  28.                 d.setAlpha(mAlpha);  
  29.             }  
  30.             d.setVisible(isVisible(), true);  
  31.             d.setDither(mDrawableContainerState.mDither);  
  32.             d.setColorFilter(mColorFilter);  
  33.             d.setState(getState());  
  34.             d.setLevel(getLevel());  
  35.             d.setBounds(getBounds());  
  36.         }  
  37.     } else {  
  38.         mCurrDrawable = null;  
  39.         mCurIndex = -1;  
  40.     }  
  41.     if (mEnterAnimationEnd != 0 || mExitAnimationEnd != 0) {  
  42.         if (mAnimationRunnable == null) {  
  43.             mAnimationRunnable = new Runnable() {  
  44.                 @Override public void run() {  
  45.                     animate(true);  
  46.                     invalidateSelf();  
  47.                 }  
  48.             };  
  49.         } else {  
  50.             unscheduleSelf(mAnimationRunnable);  
  51.         }  
  52.         animate(true);  
  53.     }  
  54.     invalidateSelf();  
  55.     return true;  
  56. }  
这里前面的代码我们可以都不管,关键是要看到在第54行一定会调用invalidateSelf()方法,这个方法中的代码如下所示:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void invalidateSelf() {  
  2.     final Callback callback = getCallback();  
  3.     if (callback != null) {  
  4.         callback.invalidateDrawable(this);  
  5.     }  
  6. }  
可以看到,这里会先调用getCallback()方法获取Callback接口的回调实例,然后再去调用回调实例的invalidateDrawable()方法。那么这里的回调实例又是什么呢?观察一下View的类定义其实你就知道了,如下所示:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Callback,  
  2. AccessibilityEventSource {  
  3.     ......  
  4. }  
View类正是实现了 Callback接口,所以刚才其实调用的就是View中的invalidateDrawable()方法,之后就会按照我们前面分析的流程执行重绘逻辑,所以视图的背景图才能够得到改变的。

另外需要注意的是,invalidate()方法虽然最终会调用到performTraversals()方法中,但这时measure和layout流程是不会重新执行的,因为视图没有强制重新测量的标志位,而且大小也没有发生过变化,所以这时只有draw流程可以得到执行。而如果你希望视图的绘制流程可以完完整整地重新走一遍,就不能使用invalidate()方法,而应该调用requestLayout()了。这个方法中的流程比invalidate()方法要简单一些,但中心思想是差不多的,这里也就不再详细进行分析了。

这样的话,我们就将视图状态以及重绘的工作原理都搞清楚了,相信大家对View的理解变得更加深刻了。感兴趣的朋友可以继续阅读 Android自定义View的实现方法,带你一步步深入了解View(四) 。

  1.         final AttachInfo ai = mAttachInfo;  
  2.         final ViewParent p = mParent;  
  3.         if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {  
  4.             if (p != null && ai != null && ai.mHardwareAccelerated) {  
  5.                 p.invalidateChild(thisnull);  
  6.                 return;  
  7.             }  
  8.         }  
  9.         if (p != null && ai != null) {  
  10.             final Rect r = ai.mTmpInvalRect;  
  11.             r.set(00, mRight - mLeft, mBottom - mTop);  
  12.             p.invalidateChild(this, r);  
  13.         }  
  14.     }  
  15. }  
在这个方法中首先会调用skipInvalidate()方法来判断当前View是否需要重绘,判断的逻辑也比较简单,如果View是不可见的且没有执行任何动画,就认为不需要重绘了。之后会进行透明度的判断,并给View添加一些标记位,然后在第22和29行调用 ViewParent的invalidateChild()方法,这里的ViewParent其实就是当前视图的父视图,因此会调用到ViewGroup的invalidateChild()方法中,代码如下所示:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public final void invalidateChild(View child, final Rect dirty) {  
  2.     ViewParent parent = this;  
  3.     final AttachInfo attachInfo = mAttachInfo;  
  4.     if (attachInfo != null) {  
  5.         final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;  
  6.         if (dirty == null) {  
  7.             ......  
  8.         } else {  
  9.             ......  
  10.             do {  
  11.                 View view = null;  
  12.                 if (parent instanceof View) {  
  13.                     view = (View) parent;  
  14.                     if (view.mLayerType != LAYER_TYPE_NONE &&  
  15.                             view.getParent() instanceof View) {  
  16.                         final View grandParent = (View) view.getParent();  
  17.                         grandParent.mPrivateFlags |= INVALIDATED;  
  18.                         grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;  
  19.                     }  
  20.                 }  
  21.                 if (drawAnimation) {  
  22.                     if (view != null) {  
  23.                         view.mPrivateFlags |= DRAW_ANIMATION;  
  24.                     } else if (parent instanceof ViewRootImpl) {  
  25.                         ((ViewRootImpl) parent).mIsAnimating = true;  
  26.                     }  
  27.                 }  
  28.                 if (view != null) {  
  29.                     if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&  
  30.                             view.getSolidColor() == 0) {  
  31.                         opaqueFlag = DIRTY;  
  32.                     }  
  33.                     if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) {  
  34.                         view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;  
  35.                     }  
  36.                 }  
  37.                 parent = parent.invalidateChildInParent(location, dirty);  
  38.                 if (view != null) {  
  39.                     Matrix m = view.getMatrix();  
  40.                     if (!m.isIdentity()) {  
  41.                         RectF boundingRect = attachInfo.mTmpTransformRect;  
  42.                         boundingRect.set(dirty);  
  43.                         m.mapRect(boundingRect);  
  44.                         dirty.set((int) boundingRect.left, (int) boundingRect.top,  
  45.                                 (int) (boundingRect.right + 0.5f),  
  46.                                 (int) (boundingRect.bottom + 0.5f));  
  47.                     }  
  48.                 }  
  49.             } while (parent != null);  
  50.         }  
  51.     }  
  52. }  
可以看到,这里在第10行进入了一个while循环,当ViewParent不等于空的时候就会一直循环下去。在这个while循环当中会不断地获取当前布局的父布局,并调用它的invalidateChildInParent()方法,在ViewGroup的 invalidateChildInParent()方法中主要是来计算需要重绘的矩形区域,这里我们先不管它,当循环到最外层的根布局后,就会调用ViewRoot的invalidateChildInParent()方法了,代码如下所示:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {  
  2.     invalidateChild(null, dirty);  
  3.     return null;  
  4. }  
这里的代码非常简单,仅仅是去调用了invalidateChild()方法而已,那我们再跟进去瞧一瞧吧:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void invalidateChild(View child, Rect dirty) {  
  2.     checkThread();  
  3.     if (LOCAL_LOGV) Log.v(TAG, "Invalidate child: " + dirty);  
  4.     mDirty.union(dirty);  
  5.     if (!mWillDrawSoon) {  
  6.         scheduleTraversals();  
  7.     }  
  8. }  
这个方法也不长,它在第6行又调用了scheduleTraversals()这个方法,那么我们继续跟进:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void scheduleTraversals() {  
  2.     if (!mTraversalScheduled) {  
  3.         mTraversalScheduled = true;  
  4.         sendEmptyMessage(DO_TRAVERSAL);  
  5.     }  
  6. }  
可以看到,这里调用了sendEmptyMessage()方法,并传入了一个DO_TRAVERSAL参数。了解Android异步消息处理机制的朋友们都会知道,任何一个Handler都可以调用sendEmptyMessage()方法来发送消息,并且在handleMessage()方法中接收消息,而如果你看一下ViewRoot的类定义就会发现,它是继承自Handler的,也就是说这里调用 sendEmptyMessage()方法出的消息,会在ViewRoot的handleMessage()方法中接收到。那么赶快看一下handleMessage()方法的代码吧,如下所示:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void handleMessage(Message msg) {  
  2.     switch (msg.what) {  
  3.     case DO_TRAVERSAL:  
  4.         if (mProfile) {  
  5.             Debug.startMethodTracing("ViewRoot");  
  6.         }  
  7.         performTraversals();  
  8.         if (mProfile) {  
  9.             Debug.stopMethodTracing();  
  10.             mProfile = false;  
  11.         }  
  12.         break;  
  13.     ......  
  14. }  

熟悉的代码出现了!这里在第7行调用了performTraversals()方法,这不就是我们在前面一篇文章中学到的视图绘制的入口吗?虽然经过了很多辗转的调用,但是可以确定的是,调用视图的invalidate()方法后确实会走到performTraversals()方法中,然后重新执行绘制流程。之后的流程就不需要再进行描述了吧,可以参考Android视图绘制流程完全解析,带你一步步深入了解View(二) 这一篇文章。

了解了这些之后,我们再回过头来看看刚才的selectDrawable()方法中到底做了什么才能够控制背景图的改变,代码如下所示:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public boolean selectDrawable(int idx) {  
  2.     if (idx == mCurIndex) {  
  3.         return false;  
  4.     }  
  5.     final long now = SystemClock.uptimeMillis();  
  6.     if (mDrawableContainerState.mExitFadeDuration > 0) {  
  7.         if (mLastDrawable != null) {  
  8.             mLastDrawable.setVisible(falsefalse);  
  9.         }  
  10.         if (mCurrDrawable != null) {  
  11.             mLastDrawable = mCurrDrawable;  
  12.             mExitAnimationEnd = now + mDrawableContainerState.mExitFadeDuration;  
  13.         } else {  
  14.             mLastDrawable = null;  
  15.             mExitAnimationEnd = 0;  
  16.         }  
  17.     } else if (mCurrDrawable != null) {  
  18.         mCurrDrawable.setVisible(falsefalse);  
  19.     }  
  20.     if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {  
  21.         Drawable d = mDrawableContainerState.mDrawables[idx];  
  22.         mCurrDrawable = d;  
  23.         mCurIndex = idx;  
  24.         if (d != null) {  
  25.             if (mDrawableContainerState.mEnterFadeDuration > 0) {  
  26.                 mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;  
  27.             } else {  
  28.                 d.setAlpha(mAlpha);  
  29.             }  
  30.             d.setVisible(isVisible(), true);  
  31.             d.setDither(mDrawableContainerState.mDither);  
  32.             d.setColorFilter(mColorFilter);  
  33.             d.setState(getState());  
  34.             d.setLevel(getLevel());  
  35.             d.setBounds(getBounds());  
  36.         }  
  37.     } else {  
  38.         mCurrDrawable = null;  
  39.         mCurIndex = -1;  
  40.     }  
  41.     if (mEnterAnimationEnd != 0 || mExitAnimationEnd != 0) {  
  42.         if (mAnimationRunnable == null) {  
  43.             mAnimationRunnable = new Runnable() {  
  44.                 @Override public void run() {  
  45.                     animate(true);  
  46.                     invalidateSelf();  
  47.                 }  
  48.             };  
  49.         } else {  
  50.             unscheduleSelf(mAnimationRunnable);  
  51.         }  
  52.         animate(true);  
  53.     }  
  54.     invalidateSelf();  
  55.     return true;  
  56. }  
这里前面的代码我们可以都不管,关键是要看到在第54行一定会调用invalidateSelf()方法,这个方法中的代码如下所示:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void invalidateSelf() {  
  2.     final Callback callback = getCallback();  
  3.     if (callback != null) {  
  4.         callback.invalidateDrawable(this);  
  5.     }  
  6. }  
可以看到,这里会先调用getCallback()方法获取Callback接口的回调实例,然后再去调用回调实例的invalidateDrawable()方法。那么这里的回调实例又是什么呢?观察一下View的类定义其实你就知道了,如下所示:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Callback,  
  2. AccessibilityEventSource {  
  3.     ......  
  4. }  
View类正是实现了 Callback接口,所以刚才其实调用的就是View中的invalidateDrawable()方法,之后就会按照我们前面分析的流程执行重绘逻辑,所以视图的背景图才能够得到改变的。

另外需要注意的是,invalidate()方法虽然最终会调用到performTraversals()方法中,但这时measure和layout流程是不会重新执行的,因为视图没有强制重新测量的标志位,而且大小也没有发生过变化,所以这时只有draw流程可以得到执行。而如果你希望视图的绘制流程可以完完整整地重新走一遍,就不能使用invalidate()方法,而应该调用requestLayout()了。这个方法中的流程比invalidate()方法要简单一些,但中心思想是差不多的,这里也就不再详细进行分析了。

这样的话,我们就将视图状态以及重绘的工作原理都搞清楚了,相信大家对View的理解变得更加深刻了。感兴趣的朋友可以继续阅读 Android自定义View的实现方法,带你一步步深入了解View(四) 。

内容概要:本文提出了一种融合多尺度Wavelet模型的跨文化英语交际智能模型系统(FL-DP-Wavelet),旨在通过多模态数据融合、多尺度特征提取与跨文化适应性建模,提升智能系统的文化敏感性和语境理解能力。该模型通过结合小波变换与深度学习优化语言信号的时频特征提取,基于跨文化敏感性发展模型(DMIS)构建文化适应性评估模块,并设计多模态数据融合框架,增强跨文化场景下的语义解析鲁棒性。实验结果显示,系统在跨文化语境下的语义理解准确率提升12.7%,文化适应性评分优于基线模型15.3%。 适合人群:从事跨文化交流、国际商务、外语教育的研究人员和技术开发者,特别是对智能系统在跨文化场景中的应用感兴趣的学者和工程师。 使用场景及目标:①跨文化商务谈判、教育合作和公共外交等场景中,需要提升智能系统的文化敏感性和语境理解能力;②帮助系统实现实时文化适应,减少因文化差异引起的语义误判和非语言行为冲突;③通过多模态数据融合,增强智能系统在复杂跨文化环境中的语义解析能力。 其他说明:该研究不仅提出了新的理论框架和技术路径,还在实际应用中验证了其有效性和优越性。未来将聚焦于小波-Transformer耦合、联邦学习隐私保护和在线学习算法,进一步推动系统向自主文化融合演进。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值