中文字幕av高清_国产视频一二区_男女羞羞羞视频午夜视频_成人精品一区_欧美色视_在线视频这里只有精品

千鋒教育-做有情懷、有良心、有品質的職業教育機構

手機站
千鋒教育

千鋒學習站 | 隨時隨地免費學

千鋒教育

掃一掃進入千鋒手機站

領取全套視頻
千鋒教育

關注千鋒學習站小程序
隨時隨地免費學習課程

當前位置:首頁  >  技術干貨  > Android源碼分析requestLayout和invalidate的區別?

Android源碼分析requestLayout和invalidate的區別?

來源:千鋒教育
發布人:xqq
時間: 2023-10-12 20:23:28 1697113408

一、requestLayout和invalidate的區別

requestLayout() 和 invalidate() 的區別在于它們作用的范圍不同。requestLayout() 用于通知 View 進行重新布局,即測量、布局和繪制三個步驟都會重新進行;而 invalidate() 用于通知 View 進行重繪,僅僅是在原有的尺寸和位置上重新繪制 View,不會重新進行測量和布局。

requestLayout:調用 View.requestLayout 方法后會依次調用 performMeasure, performLayout 和 performDraw 方法,調用者 View 及其父 View 會重新從上往下進行 measure, layout 流程,一般情況下不會執行 draw 流程(子 View 會通過判斷其尺寸/頂點是否發生改變而決定是否重新 measure/layout/draw 流程)。因此,當只需要進行重繪時可以使用 invalidate 方法,如果需要重新測量和布局則可以使用 requestLayout 方法,而 requestLayout 方法不一定會重繪,因此如果要進行重繪可以再手動調用 invalidate 方法。invalidate:調用 View.invalidate() 方法后會逐級往上調用父 View 的相關方法,最終在 Choreographer 的控制下調用 ViewRootImpl.performTraversals() 方法。只有滿足 mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null … 等條件才會執行 measure 和 layout 流程,否則只執行 draw 流程,draw 流程的執行過程與是否開啟硬件加速有關:關閉硬件加速則從 DecorView 開始往下的所有子 View 都會被重新繪制。開啟硬件加速則只有調用 invalidate 方法的 View 才會重新繪制。

二、requestLayout方法介紹

1、View.requestLayout

Java復制代碼public void requestLayout() {    if (mMeasureCache != null) mMeasureCache.clear();    if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {        // 如果處于 Layout 則將該請求加入 ViewRootImpl 中的任務隊列中        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;    }}// ViewRootImplboolean requestLayoutDuringLayout(final View view) {    if (!mLayoutRequesters.contains(view)) {        mLayoutRequesters.add(view);    }    // ...}

如果此時處于 Layout 則將該請求加入 ViewRootImpl 中的任務隊列中,否則向上調用父 View 的 requestLayout 方法,直到 ViewRootImpl 中:

Java復制代碼public void requestLayout() {    if (!mHandlingLayoutInLayoutRequest) {        checkThread();        mLayoutRequested = true;        scheduleTraversals();    }}

ViewRootImpl.requestLayout 方法在 check 了線程后將 mLayoutRequested 置為 true 且調用 scheduleTraversals 方法,于是在 Vsync 信號到來后會調用 performTraversals 方法。由于 mLayoutRequested == true,因此會依次執行 performMeasure, performLayout 以及 performDraw 方法開始 View 的繪制流程。

2、繪制過程

measure:

接下來看看 View.requestLayout 方法對整個 View 樹的影響。首先看一下 View.measure 方法:

Java復制代碼public final void measure(int widthMeasureSpec, int heightMeasureSpec) {    // ...    final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;    final boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec;    final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;    final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec) && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);    final boolean needsLayout = specChanged && (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);    if (forceLayout || needsLayout) {        // ...        onMeasure(widthMeasureSpec, heightMeasureSpec);        // 設置 PFLAG_LAYOUT_REQUIRED 標志位        mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;    }}

在 View.requestLayout 方法中已經看到給當前 View 及其父 View 都添加了 PFLAG_FORCE_LAYOUT 標志位,因此其 forceLayout == ture,即會執行 onMeasure 方法測量。而對于未設置 PFLAG_FORCE_LAYOUT 標志位的 View 則需要判斷其尺寸是否發生改變才會決定調用 onMeasure 與否。我們看到調用 onMeasure 后又設置了 PFLAG_LAYOUT_REQUIRED 標志位。

layout:

接著看 View.layout 方法:

Java復制代碼public void layout(int l, int t, int r, int b) {    boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {        onLayout(changed, l, t, r, b);        // ...    }    // ...}

由于調用 onMeasure 后設置了 PFLAG_LAYOUT_REQUIRED 標志位,因此也會跟著執行 onLayout 方法。另外看一下 setOpticalFrame 和 setFrame 方法,其中 setOpticalFrame 方法中最終也會調用到 setFrame 方法:

Java復制代碼protected boolean setFrame(int left, int 較好, int right, int bottom) {    boolean changed = false;    if (mLeft != left || mRight != right || mTop != 較好 || mBottom != bottom) {        int oldWidth = mRight - mLeft;        int oldHeight = mBottom - mTop;        int newWidth = right - left;        int newHeight = bottom - 較好;        boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);        invalidate(sizeChanged);        // ...    }}

因此可以看到當 View 四個頂點發生變化時也會調用 onLayout 方法,且會調用 View.invalidate 方法,并將 View 的寬高是否發生變化傳給 invalidateCache 參數。

draw:

ViewRootImpl.performDraw 會調用到 ViewRootImpl.draw 方法:

Java復制代碼private boolean draw(boolean fullRedrawNeeded) {    final Rect dirty = mDirty;    if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {        // 硬件繪制/軟件繪制    }}

dirty 是臟區,在 ViewRootImpl.invalidate 方法中會調用 mDirty.set() 方法為其設置邊界值,如果上面 View 的頂點沒有發生變化則不會調用 invalidate 方法,則 dirty.isEmpty() 返回 true,因此整個 View 樹都不會重繪。

3、小結

調用 View.requestLayout 方法后會依次調用 performMeasure, performLayout 和 performDraw 方法,調用者 View 及其父 View 會從上往下重新進行 measure, layout 流程,一般情況下不會執行 draw 流程(子 View 會通過判斷其尺寸/頂點是否發生改變而決定是否重新 measure/layout/draw 流程)。

三、invalidate方法介紹

1、View.invalidate

先看一下 invalidate 這個方法:

Java復制代碼public void invalidate() {    invalidate(true);}public void invalidate(boolean invalidateCache) {    invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);}void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) {    if (skipInvalidate()) { // 判斷是否需要跳過 invalidate        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            || (fullInvalidate && isOpaque() != mLastIsOpaque)) { // 判斷是否重繪        if (fullInvalidate) {            mLastIsOpaque = isOpaque(); // 重新設置 Opaque            mPrivateFlags &= ~PFLAG_DRAWN; // 移除 PFLAG_DRAWN 標志位        }        mPrivateFlags |= PFLAG_DIRTY; // 設置 PFLAG_DIRTY 臟區標志位        if (invalidateCache) {            mPrivateFlags |= PFLAG_INVALIDATED; // 設置 PFLAG_INVALIDATED 標志位            mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; // 移除 PFLAG_DRAWING_CACHE_VALID 標志位        }        final AttachInfo ai = mAttachInfo;        final ViewParent p = mParent;        if (p != null && ai != null && l < r && t < b) {            // damage 表示要重繪的臟區            final Rect damage = ai.mTmpInvalRect;            damage.set(l, t, r, b);            p.invalidateChild(this, damage);        }        // ...    }}private boolean skipInvalidate() {    return (mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null &&            (!(mParent instanceof ViewGroup) || !((ViewGroup) mParent).isViewTransitioning(this));}

首先會通過 skipInvalidate 方法判斷是否要跳過 invalidate 過程,如果同時滿足以下條件則跳過:

View 不可見當前沒有運行動畫父 View 不是 ViewGroup 類型或者父 ViewGoup 不處于過渡態

接下來再判斷是否需要重繪,如果滿足以下任意一個條件則進行重繪:

View 已經繪制完成且具有邊界invalidateCache == true 且設置了 PFLAG_DRAWING_CACHE_VALID 標志位,即繪制緩存可用沒有設置 PFLAG_INVALIDATED 標志位,即沒有被重繪過fullInvalidate == true 且在 透明 和 不透明 之間發生了變化

在處理了一些標志位的邏輯后調用了父 View 的 invalidateChild 方法并將要重繪的區域 damage 傳給父 View。于是接著看 ViewGroup.invalidateChild 方法:

Java復制代碼public final void invalidateChild(View child, final Rect dirty) {    final AttachInfo attachInfo = mAttachInfo;    if (attachInfo != null && attachInfo.mHardwareAccelerated) {        // 開啟了硬件加速        onDescendantInvalidated(child, child);        return;    }    // 未開啟硬件加速    ViewParent parent = this;    if (attachInfo != null) {        // ...        do {            // ...            parent = parent.invalidateChildInParent(location, dirty);            // 重新設置臟區            // ...        } while (parent != null);    }}

可以看到這里會根據是否開啟了硬件加速而走不同的邏輯。

2、小結

調用 View.invalidate() 方法后會逐級往上調用父 View 的相關方法,最終在 Choreographer 的控制下調用 ViewRootImpl.performTraversals() 方法。由于 mLayoutRequested == false,因此只有滿足 mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null … 等條件才會執行 measure 和 layout 流程,否則只執行 draw 流程,draw 流程的執行過程與是否開啟硬件加速有關:

關閉硬件加速則從 DecorView 開始往下的所有子 View 都會被重新繪制。開啟硬件加速則只有調用 invalidate 方法的 View 才會重新繪制。

View 在繪制后會設置 PFLAG_DRAWN 標志位。

延伸閱讀1:Android中的View類簡介

Android中的View類代表用戶界面中基本的構建塊。一個View在屏幕中占據一個矩形區域、并且負責繪制和事件處理。View是所有widgets的基礎類,widgets是我們通常用于創建和用戶交互的組件,比如按鈕、文本輸入框等等。子類ViewGroup是所有布局(layout)的基礎類。layout是一個不看見的容器,里面堆放著其他的view或者ViewGroup,并且設置他們的布局屬性。

聲明:本站稿件版權均屬千鋒教育所有,未經許可不得擅自轉載。
10年以上業內強師集結,手把手帶你蛻變精英
請您保持通訊暢通,專屬學習老師24小時內將與您1V1溝通
免費領取
今日已有369人領取成功
劉同學 138****2860 剛剛成功領取
王同學 131****2015 剛剛成功領取
張同學 133****4652 剛剛成功領取
李同學 135****8607 剛剛成功領取
楊同學 132****5667 剛剛成功領取
岳同學 134****6652 剛剛成功領取
梁同學 157****2950 剛剛成功領取
劉同學 189****1015 剛剛成功領取
張同學 155****4678 剛剛成功領取
鄒同學 139****2907 剛剛成功領取
董同學 138****2867 剛剛成功領取
周同學 136****3602 剛剛成功領取
相關推薦HOT
主站蜘蛛池模板: 天天综合网7799精品 | 午夜a级理论片915影院 | 99热最新网址| 不卡视频一区 | 久久一级| 久久久久久久国产精品 | 精品国产乱码久久久久久1区2区 | 亚洲涩涩爱 | 精品视频| 干狠狠| 精品中文字幕一区二区三区 | 97国产在线| 在线观看免费的网站www | 久久aⅴ乱码一区二区三区 午夜在线播放 | 欧美激情一区二区三区蜜桃视频 | 国产精品久久久久久久久久东京 | 欧美在线视频一区二区 | 日韩精品一区二 | 在线日韩视频 | 成人国产精品一级毛片视频 | 亚洲综合一区二区三区 | 国产精品久久久久无码av | 亚洲精品国产99 | 一区二区三区视频 | 一区二区三区小视频 | 91精品国产92 | 免费观看黄色一级大片 | 亚洲欧美另类在线观看 | 青青草在线视频免费观看 | 久久九九久久精品 | 精品久久久久久久久久久久久久 | 午夜小电影 | 国产欧美在线视频 | 香蕉黄色一级片 | 99久久久国产精品 | 国产一区二区三区在线视频 | 欧美h视频| 99国产精品 | 婷婷色中文网 | 日韩中文一区二区三区 | 99这里只有精品视频 |