Android UI绘制之View重绘
16lz
2021-01-23
在Android UI 绘制机制之View创建过程介绍了Android应用绘制View的创建过程,本文将介绍图形如何刷新。我们知道Android的UI界面是通过View和ViewGroup分层树进行定义的,如下图所示。一般在View发生改变时对UI进行重绘,本文介绍重绘的过程。
图1:Android UI界面结构
单UI界面上某一个UI变化了,会显示地调用View对象中的invalidate()。这里需要注意的是invalidate只是标记计算脏区,真正的onDraw过程是有UI线程来完成的,下面来分析整个流程。
1. View需要重绘调用
public void invalidate() { invalidate(true); }
2. child View调用invalidate时,首先找到自己父View(View的成员变量mParent记录自己的父View),然后将AttachInfo中保存的信息告诉父View刷新自己,父View调用invalidateChild函数刷新child View void invalidate(boolean invalidateCache) { if (skipInvalidate()) { return; } if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED || isOpaque() != mLastIsOpaque) { ... ... final ViewParent p = mParent; // 获取父类对象 // noinspection PointlessBooleanExpression,ConstantConditions if (!HardwareRenderer.RENDER_DIRTY_REGIONS) { if (p != null && ai != null && ai.mHardwareAccelerated) { p.invalidateChild(this, null); return; } } if (p != null && ai != null) { final Rect r = ai.mTmpInvalRect; r.set(0, 0, mRight - mLeft, mBottom - mTop); // 设置View的尺寸 p.invalidateChild(this, r); // 调用parent对象让parent对象重绘制child } }}
3.再看ViewGroup中的invalidateChild方法的实现 public final void invalidateChild(View child, final Rect dirty) { ... ... ViewParent parent = this; final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { ... ... if (dirty == null) { ...... do { View view = null; if (parent instanceof View) { view = (View) parent; if (view.mLayerType != LAYER_TYPE_NONE) { view.mLocalDirtyRect.setEmpty(); if (view.getParent() instanceof View) { final View grandParent = (View) view.getParent(); grandParent.mPrivateFlags |= INVALIDATED; grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID; } } if ((view.mPrivateFlags & DIRTY_MASK) != 0) { // already marked dirty - we're done break; } } ...... } while (parent != null); } else { ...... do { View view = null; if (parent instanceof View) { view = (View) parent; if (view.mLayerType != LAYER_TYPE_NONE && view.getParent() instanceof View) { final View grandParent = (View) view.getParent(); grandParent.mPrivateFlags |= INVALIDATED; grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID; } } ...... parent = parent.invalidateChildInParent(location, dirty); //层层刷新 ....... } while (parent != null); } }}
在 Android UI 绘制机制之View创建过程文章中介绍了DocorView是应用的根,DocorView的parent设置成了Viewroot。再来看ViewRoot中的invalidateChildInParent。 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) { invalidateChild(null, dirty); return null; }
public void invalidateChild(View child, Rect dirty) { checkThread(); //检测是否是UI线程 if (dirty == null) { // Fast invalidation for GL-enabled applications; GL must redraw everything invalidate(); return; } if (mCurScrollY != 0 || mTranslator != null) { mTempRect.set(dirty); dirty = mTempRect; if (mCurScrollY != 0) { dirty.offset(0, -mCurScrollY); } if (mTranslator != null) { mTranslator.translateRectInAppWindowToScreen(dirty); } if (mAttachInfo.mScalingRequired) { dirty.inset(-1, -1); } } if (!mDirty.isEmpty() && !mDirty.contains(dirty)) { mAttachInfo.mSetIgnoreDirtyState = true; mAttachInfo.mIgnoreDirtyState = true; } mDirty.union(dirty); if (!mWillDrawSoon) { scheduleTraversals(); } }
在ViewRoot 中最后都会调用scheduleTraversals,由scheduleTraversals发送一个DO_TRAVERSAL消息,由ViewRoot线程调用performTraversals函数实现UI绘制。performTraversals比较复杂,这里不再介绍,它最终会调用View的ondraw。本文与 Android UI 绘制机制之View创建过程大致介绍了Android 应用UI窗口View的建立及刷新流程。 更多相关文章
- Android 使用Vitamio打造自己的万能播放器(3)――本地播放(主界面、
- Android用户界面 UI组件--TextView及其子类(二) Button,selector
- Android 设置界面修改为Iphone的tab菜单风格
- 总结线程交互
- Android 线程完全解析
- 主线程中Looper的轮询死循环为何没有阻塞主线程?