Android视图重绘,使用invalidate还是requestLayout
概述
在我们在进行自定义View的相关开发中,当我们更改了当前View的状态,比如大小,位置等,我们需要重新刷新整个界面,保证显示最新的状态。在Android中,让当前的视图重绘有两种方式,invalidate和requestLayout,今天我们看看这两种方式的原理以及区别。
分析
invalidate的原理
public void invalidate() { invalidate(true); }
最后会调用到invalidateInternal这个方法
1 void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, 2 boolean fullInvalidate) { 3 if (mGhostView != null) { 4 mGhostView.invalidate(true); 5 return; 6 } 7 8 if (skipInvalidate()) { 9 return; 10 } 11 12 if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) 13 || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) 14 || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED 15 || (fullInvalidate && isOpaque() != mLastIsOpaque)) { 16 if (fullInvalidate) { 17 mLastIsOpaque = isOpaque(); 18 mPrivateFlags &= ~PFLAG_DRAWN; 19 } 20 21 mPrivateFlags |= PFLAG_DIRTY; 22 23 if (invalidateCache) { 24 mPrivateFlags |= PFLAG_INVALIDATED; 25 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; 26 } 27 28 // Propagate the damage rectangle to the parent view. 29 final AttachInfo ai = mAttachInfo; 30 final ViewParent p = mParent; 31 if (p != null && ai != null && l < r && t < b) { 32 final Rect damage = ai.mTmpInvalRect; 33 damage.set(l, t, r, b); 34 p.invalidateChild(this, damage); 35 } 36 .....
我们看到方法的最后调用了ViewParent的invalidateChild方法,因为ViewParent是个接口,invalidateChild是空实现,我们去看看它的实现类ViewRootImpl中的invalidateChild是如何做的
@Override public void invalidateChild(View child, Rect dirty) { invalidateChildInParent(null, dirty); }
1 @Override 2 public ViewParent invalidateChildInParent(int[] location, Rect dirty) { 3 checkThread(); 4 if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty); 5 6 if (dirty == null) { 7 invalidate(); 8 return null; 9 } else if (dirty.isEmpty() && !mIsAnimating) { 10 return null; 11 } 12 13 if (mCurScrollY != 0 || mTranslator != null) { 14 mTempRect.set(dirty); 15 dirty = mTempRect; 16 if (mCurScrollY != 0) { 17 dirty.offset(0, -mCurScrollY); 18 } 19 if (mTranslator != null) { 20 mTranslator.translateRectInAppWindowToScreen(dirty); 21 } 22 if (mAttachInfo.mScalingRequired) { 23 dirty.inset(-1, -1); 24 } 25 } 26 27 invalidateRectOnScreen(dirty); 28 29 return null; 30 }
又会调用ViewRootImpl中的invalidate方法
void invalidate() { mDirty.set(0, 0, mWidth, mHeight); if (!mWillDrawSoon) { scheduleTraversals(); } }
这里调用了scheduleTraversals重新开始了View的绘制,我们知道View的绘制是从ViewRootImpl的performTraversals方法开始的。我们看看scheduleTraversals是不是触发了performTraversals。
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }
在scheduleTraversals方法中我们发现了一个mTraversalRunnable对象,这个对象就是我们要观察的重点
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
我们发现这个对象就是一个Runnable对象,我们在scheduleTraversals方法中传入mTraversalRunnable 就会执行run方法,其中又调用了doTraversal这个方法
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }
最后我们发现在doTraversal方法中调用了performTraversals开始了View的重新绘制,这就是invalidate的整个过程。
requestLayout的原理
public void requestLayout() { if (mMeasureCache != null) mMeasureCache.clear(); if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) { // Only trigger request-during-layout logic if this is the view requesting it, // not the views in its parent hierarchy ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot != null && viewRoot.isInLayout()) { if (!viewRoot.requestLayoutDuringLayout(this)) { return; } } mAttachInfo.mViewRequestingLayout = this; } mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; if (mParent != null && !mParent.isLayoutRequested()) { mParent.requestLayout(); } if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) { mAttachInfo.mViewRequestingLayout = null; } }
其中会调用ViewParent的requestLayout方法,同样,我们去看ViewRootImpl中的requestLayout方法。
Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
这里调用了scheduleTraversals,后面的步骤就和上面invalidate时一样了。相对来说,requestLayout的流程还是比较简单的。
区别
既然两种方式都可以完成View的重绘,那么有什么区别呢?
使用invalidate重绘当前视图是不会再次执行measure和layout流程的。因为视图没有强制重新测量的标志位,而且大小也没有发生过变化,所以这时只有draw流程可以得到执行。
如果你希望视图的绘制流程可以完完整整地重新走一遍,就不能使用invalidate()方法,而应该调用requestLayout()了
相关文章
- [Android Pro] 横竖屏切换时,禁止activity重新创建,android:configChanges="keyboardHidden|orientation" 不起作用
- [Android Traffic] 让android应用在传输网络数据的时候更省电
- [Android Pro] Android权限设置android.permission完整列表
- Android教程-android studio 制作.9 图片
- 【黑马Android】(02)短信发送器/布局演示/android下单位/android下Junit/保存数据/android下权限/xml解析和序列化
- android性能优化1
- 导入旧版本Android项目时的“Unable to resolve target ‘android
- 《android开发艺术探索》读书笔记(十五)--Android性能优化
- uni-app:ios/android中的nvue和vue页面加载自定义字体(hbuilderx 3.7.3)
- Android Studio gradle插件版本和gradle版本对应关系
- 《android开发艺术探索》读书笔记(十五)--Android性能优化
- [转]Android开发环境搭建(图文教程)
- android 证书验证流程分析_Android签名机制之---签名验证过程详解
- Android 10.0 关于startActivity finishActivity displayActivity流程详解
- 【Android Gradle 插件】ProductFlavor 配置 ( ProductFlavor#externalNativeBuild配置 | cmake 配置 | ndkBuild 配置 )
- ANDROID 开发,安装离线安装包的下载地址及安装方法。
- Android PullToZoomListView实现放大回弹效果
- 编译Android VNC Server(pc远程控制android)(四十九)
- ANDROID嵌入式应用Unity3D视图(画廊3D模型)
- Error:Could not determine the class-path for interface com.android.builder.model.AndroidProject
- android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
- Android 开发 --Unable to resolve target 'android-19'
- android studio(as)找不到手机
- Android 理解DP、SP、PX的区别
- Android 9.0/P(android p指安卓9.0版本) okhttp3网络请求出错
- Android jni线程同步和互斥(生产者和消费者模式)
- android开发,开发环境(Android Studio)安装
- Mac安装IDA后各版本android_server
- Android Binder Driver流程分析