国产睡熟迷奷白丝护士系列精品,中文色字幕网站,免费h网站在线观看的,亚洲开心激情在线

      <sup id="hb9fh"></sup>
          1. 千鋒教育-做有情懷、有良心、有品質(zhì)的職業(yè)教育機(jī)構(gòu)

            手機(jī)站
            千鋒教育

            千鋒學(xué)習(xí)站 | 隨時(shí)隨地免費(fèi)學(xué)

            千鋒教育

            掃一掃進(jìn)入千鋒手機(jī)站

            領(lǐng)取全套視頻
            千鋒教育

            關(guān)注千鋒學(xué)習(xí)站小程序
            隨時(shí)隨地免費(fèi)學(xué)習(xí)課程

            當(dāng)前位置:首頁(yè)  >  技術(shù)干貨  > Android源碼分析requestLayout和invalidate的區(qū)別?

            Android源碼分析requestLayout和invalidate的區(qū)別?

            來(lái)源:千鋒教育
            發(fā)布人:xqq
            時(shí)間: 2023-10-12 20:23:28 1697113408

            一、requestLayout和invalidate的區(qū)別

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

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

            二、requestLayout方法介紹

            1、View.requestLayout

            Java復(fù)制代碼public void requestLayout() {    if (mMeasureCache != null) mMeasureCache.clear();    if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {        // 如果處于 Layout 則將該請(qǐng)求加入 ViewRootImpl 中的任務(wù)隊(duì)列中        ViewRootImpl viewRoot = getViewRootImpl();        if (viewRoot != null && viewRoot.isInLayout()) {            if (!viewRoot.requestLayoutDuringLayout(this)) {                return;            }        }        mAttachInfo.mViewRequestingLayout = this;    }    // 添加標(biāo)志位    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);    }    // ...}

            如果此時(shí)處于 Layout 則將該請(qǐng)求加入 ViewRootImpl 中的任務(wù)隊(duì)列中,否則向上調(diào)用父 View 的 requestLayout 方法,直到 ViewRootImpl 中:

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

            ViewRootImpl.requestLayout 方法在 check 了線(xiàn)程后將 mLayoutRequested 置為 true 且調(diào)用 scheduleTraversals 方法,于是在 Vsync 信號(hào)到來(lái)后會(huì)調(diào)用 performTraversals 方法。由于 mLayoutRequested == true,因此會(huì)依次執(zhí)行 performMeasure, performLayout 以及 performDraw 方法開(kāi)始 View 的繪制流程。

            2、繪制過(guò)程

            measure:

            接下來(lái)看看 View.requestLayout 方法對(duì)整個(gè) View 樹(shù)的影響。首先看一下 View.measure 方法:

            Java復(fù)制代碼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);        // 設(shè)置 PFLAG_LAYOUT_REQUIRED 標(biāo)志位        mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;    }}

            在 View.requestLayout 方法中已經(jīng)看到給當(dāng)前 View 及其父 View 都添加了 PFLAG_FORCE_LAYOUT 標(biāo)志位,因此其 forceLayout == ture,即會(huì)執(zhí)行 onMeasure 方法測(cè)量。而對(duì)于未設(shè)置 PFLAG_FORCE_LAYOUT 標(biāo)志位的 View 則需要判斷其尺寸是否發(fā)生改變才會(huì)決定調(diào)用 onMeasure 與否。我們看到調(diào)用 onMeasure 后又設(shè)置了 PFLAG_LAYOUT_REQUIRED 標(biāo)志位。

            layout:

            接著看 View.layout 方法:

            Java復(fù)制代碼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);        // ...    }    // ...}

            由于調(diào)用 onMeasure 后設(shè)置了 PFLAG_LAYOUT_REQUIRED 標(biāo)志位,因此也會(huì)跟著執(zhí)行 onLayout 方法。另外看一下 setOpticalFrame 和 setFrame 方法,其中 setOpticalFrame 方法中最終也會(huì)調(diào)用到 setFrame 方法:

            Java復(fù)制代碼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);        // ...    }}

            因此可以看到當(dāng) View 四個(gè)頂點(diǎn)發(fā)生變化時(shí)也會(huì)調(diào)用 onLayout 方法,且會(huì)調(diào)用 View.invalidate 方法,并將 View 的寬高是否發(fā)生變化傳給 invalidateCache 參數(shù)。

            draw:

            ViewRootImpl.performDraw 會(huì)調(diào)用到 ViewRootImpl.draw 方法:

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

            dirty 是臟區(qū),在 ViewRootImpl.invalidate 方法中會(huì)調(diào)用 mDirty.set() 方法為其設(shè)置邊界值,如果上面 View 的頂點(diǎn)沒(méi)有發(fā)生變化則不會(huì)調(diào)用 invalidate 方法,則 dirty.isEmpty() 返回 true,因此整個(gè) View 樹(shù)都不會(huì)重繪。

            3、小結(jié)

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

            三、invalidate方法介紹

            1、View.invalidate

            先看一下 invalidate 這個(gè)方法:

            Java復(fù)制代碼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()) { // 判斷是否需要跳過(guò) 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(); // 重新設(shè)置 Opaque            mPrivateFlags &= ~PFLAG_DRAWN; // 移除 PFLAG_DRAWN 標(biāo)志位        }        mPrivateFlags |= PFLAG_DIRTY; // 設(shè)置 PFLAG_DIRTY 臟區(qū)標(biāo)志位        if (invalidateCache) {            mPrivateFlags |= PFLAG_INVALIDATED; // 設(shè)置 PFLAG_INVALIDATED 標(biāo)志位            mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; // 移除 PFLAG_DRAWING_CACHE_VALID 標(biāo)志位        }        final AttachInfo ai = mAttachInfo;        final ViewParent p = mParent;        if (p != null && ai != null && l < r && t < b) {            // damage 表示要重繪的臟區(qū)            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));}

            首先會(huì)通過(guò) skipInvalidate 方法判斷是否要跳過(guò) invalidate 過(guò)程,如果同時(shí)滿(mǎn)足以下條件則跳過(guò):

            View 不可見(jiàn)當(dāng)前沒(méi)有運(yùn)行動(dòng)畫(huà)父 View 不是 ViewGroup 類(lèi)型或者父 ViewGoup 不處于過(guò)渡態(tài)

            接下來(lái)再判斷是否需要重繪,如果滿(mǎn)足以下任意一個(gè)條件則進(jìn)行重繪:

            View 已經(jīng)繪制完成且具有邊界invalidateCache == true 且設(shè)置了 PFLAG_DRAWING_CACHE_VALID 標(biāo)志位,即繪制緩存可用沒(méi)有設(shè)置 PFLAG_INVALIDATED 標(biāo)志位,即沒(méi)有被重繪過(guò)fullInvalidate == true 且在 透明 和 不透明 之間發(fā)生了變化

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

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

            可以看到這里會(huì)根據(jù)是否開(kāi)啟了硬件加速而走不同的邏輯。

            2、小結(jié)

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

            關(guān)閉硬件加速則從 DecorView 開(kāi)始往下的所有子 View 都會(huì)被重新繪制。開(kāi)啟硬件加速則只有調(diào)用 invalidate 方法的 View 才會(huì)重新繪制。

            View 在繪制后會(huì)設(shè)置 PFLAG_DRAWN 標(biāo)志位。

            延伸閱讀1:Android中的View類(lèi)簡(jiǎn)介

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

            聲明:本站稿件版權(quán)均屬千鋒教育所有,未經(jīng)許可不得擅自轉(zhuǎn)載。
            10年以上業(yè)內(nèi)強(qiáng)師集結(jié),手把手帶你蛻變精英
            請(qǐng)您保持通訊暢通,專(zhuān)屬學(xué)習(xí)老師24小時(shí)內(nèi)將與您1V1溝通
            免費(fèi)領(lǐng)取
            今日已有369人領(lǐng)取成功
            劉同學(xué) 138****2860 剛剛成功領(lǐng)取
            王同學(xué) 131****2015 剛剛成功領(lǐng)取
            張同學(xué) 133****4652 剛剛成功領(lǐng)取
            李同學(xué) 135****8607 剛剛成功領(lǐng)取
            楊同學(xué) 132****5667 剛剛成功領(lǐng)取
            岳同學(xué) 134****6652 剛剛成功領(lǐng)取
            梁同學(xué) 157****2950 剛剛成功領(lǐng)取
            劉同學(xué) 189****1015 剛剛成功領(lǐng)取
            張同學(xué) 155****4678 剛剛成功領(lǐng)取
            鄒同學(xué) 139****2907 剛剛成功領(lǐng)取
            董同學(xué) 138****2867 剛剛成功領(lǐng)取
            周同學(xué) 136****3602 剛剛成功領(lǐng)取
            相關(guān)推薦HOT
            想開(kāi)發(fā)一款直播交友APP需要有哪些功能?

            一、想開(kāi)發(fā)一款直播交友APP需要的功能 1、直播功能:用戶(hù)可以選擇視頻一對(duì)一直播和語(yǔ)音一對(duì)一直播兩種方式,觀眾需要支付直播費(fèi)用,直播費(fèi)用由...詳情>>

            2023-10-12 21:47:55
            物聯(lián)網(wǎng)云平臺(tái)用什么數(shù)據(jù)庫(kù)?

            一、物聯(lián)網(wǎng)云平臺(tái)的數(shù)據(jù)庫(kù)1、關(guān)系型數(shù)據(jù)庫(kù)(RDBMS)例如MySQL、PostgreSQL、Oracle等。關(guān)系型數(shù)據(jù)庫(kù)以表格的形式存儲(chǔ)數(shù)據(jù),并使用SQL(Structur...詳情>>

            2023-10-12 21:41:25
            ACTION_CANCEL到底何時(shí)觸發(fā),滑出子View范圍會(huì)發(fā)生什么?

            一、ACTION_CANCEL在這些時(shí)候會(huì)觸發(fā)1、父view攔截事件首先要了解ViewGroup什么情況下會(huì)攔截事件,請(qǐng)看下面一段代碼:@Overridepublic boolean d...詳情>>

            2023-10-12 21:31:42
            設(shè)計(jì)移動(dòng)端表單有哪些注意事項(xiàng)?

            1、分布式表單iPhone目前主要的機(jī)型屏幕尺寸在4.7-5.8英寸,android的尺寸更多一些,但是總體上來(lái)說(shuō),移動(dòng)端的載體手機(jī)屏幕不大,能承載的信息...詳情>>

            2023-10-12 20:59:19
            數(shù)據(jù)庫(kù)應(yīng)該怎么設(shè)計(jì)比較好?

            一、數(shù)據(jù)庫(kù)的設(shè)計(jì)方法1、需求分析在設(shè)計(jì)數(shù)據(jù)庫(kù)之前,仔細(xì)分析和理解業(yè)務(wù)需求。了解數(shù)據(jù)的類(lèi)型、關(guān)系和操作模式,明確數(shù)據(jù)的目標(biāo)和用途。與相關(guān)...詳情>>

            2023-10-12 20:44:36
            快速通道