成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

View之invalidate,requestLayout,postInvalidate

zhangyucha0 / 1702人閱讀

摘要:因為是在線程的,所以方法的作用就是將非線程的刷新操作切換到線程,以便在線程中調(diào)用方法刷新。一句話總結(jié)方法的作用就是實現(xiàn)了消息機制,可以使我們在非線程也能調(diào)用刷新的方法。

目錄介紹

01.invalidate,requestLayout,postInvalidate區(qū)別

02.invalidate深入分析

03.postInvalidate深入分析

04.requestLayout深入分析

05.ViewRootImpl作用分析

06.這幾個方法總結(jié)

好消息

博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識點,Android技術(shù)博客,Python學(xué)習(xí)筆記等等,還包括平時開發(fā)中遇到的bug匯總,當(dāng)然也在工作之余收集了大量的面試題,長期更新維護并且修正,持續(xù)完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計N篇[近100萬字,陸續(xù)搬到網(wǎng)上],轉(zhuǎn)載請注明出處,謝謝!

鏈接地址:https://github.com/yangchong2...

如果覺得好,可以star一下,謝謝!當(dāng)然也歡迎提出建議,萬事起于忽微,量變引起質(zhì)變!

01.Java基礎(chǔ)問題(19個)

02.Java面向?qū)ο髥栴}(10個)

03.Java數(shù)據(jù)結(jié)構(gòu)問題(20個)

04.JavaIO流問題(6個)

05.java多線程問題(19)

06.Java虛擬機問題(10個)

07.Java類加載問題(8個)

08.Java反射問題(6個)

10.Java異常問題(9個)

01.requestLayout、invalidate與postInvalidate作用與區(qū)別

invalidate() postInvalidate()

共同點:都是調(diào)用onDraw()方法,然后去達到重繪view的目的

區(qū)別:invalidate()用于主線程,postInvalidate()用于子線程【通過handler發(fā)送消息,在handleMessage中((View) msg.obj).invalidate(),】

requestLayout()

也可以達到重繪view的目的,但是與前兩者不同,它會先調(diào)用onLayout()重新排版,再調(diào)用ondraw()方法。

當(dāng)view確定自身已經(jīng)不再適合現(xiàn)有的區(qū)域時,該view本身調(diào)用這個方法要求parent view(父類的視圖)重新調(diào)用他的onMeasure、onLayout來重新設(shè)置自己位置。特別是當(dāng)view的layoutparameter發(fā)生改變,并且它的值還沒能應(yīng)用到view上時,這時候適合調(diào)用這個方法requestLayout()。 requestLayout調(diào)用onMeasure和onLayout,不一定調(diào)用onDraw

02.invalidate深入分析

看看invalidate源碼,如下所示

invalidateCache為true表示全部重繪。View的invalidate方法最后調(diào)用的是invalidateInternal方法,invalidateInternal方法中的重點邏輯在源碼上添加注釋呢。

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 (mGhostView != null) {
        mGhostView.invalidate(true);
        return;
    }

    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
            || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
        if (fullInvalidate) {
            mLastIsOpaque = isOpaque();
            mPrivateFlags &= ~PFLAG_DRAWN;
        }

        mPrivateFlags |= PFLAG_DIRTY;

        if (invalidateCache) {
            mPrivateFlags |= PFLAG_INVALIDATED;
            mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
        }

        //這個地方是重點邏輯,主要分析這個
        // Propagate the damage rectangle to the parent view.
        final AttachInfo ai = mAttachInfo;
        final ViewParent p = mParent;
        if (p != null && ai != null && l < r && t < b) {
            final Rect damage = ai.mTmpInvalRect;
            damage.set(l, t, r, b);
            p.invalidateChild(this, damage);
        }

        // Damage the entire projection receiver, if necessary.
        if (mBackground != null && mBackground.isProjected()) {
            final View receiver = getProjectionReceiver();
            if (receiver != null) {
                receiver.damageInParent();
            }
        }
    }
}

看看ViewGroup中的invalidateChild方法

在ViewGroup的invalidateChild方法中有一個循環(huán),循環(huán)里面會一直調(diào)用父布局的invalidateChildInParent方法,而View和ViewGroup的最終父布局都是ViewRootImpl。

@UiThread
public abstract class ViewGroup extends View implements ViewParent, ViewManager {

    @Override
    public final void invalidateChild(View child, final Rect dirty) {
        ViewParent parent = this;
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            //這是一個從當(dāng)前的布局View向上不斷遍歷當(dāng)前布局View的父布局,最后遍歷到ViewRootImpl的循環(huán)
            do {
                View view = null;
                if (parent instanceof View) {
                    view = (View) parent;
                }
                
                //這里調(diào)用的是父布局的invalidateChildInParent方法
                parent = parent.invalidateChildInParent(location, dirty);
            } while (parent != null);
        }
    }
    
    @Override
    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
        if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||
                (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
            if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
                        FLAG_OPTIMIZE_INVALIDATE) {
                ...
                //這里也是一些計算繪制區(qū)域的內(nèi)容
                return mParent;
            } else {
                mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;
                //這里也是一些計算繪制區(qū)域的內(nèi)容
                return mParent;
            }
        }
        return null;
    }
}

View中的invalidateChild方法和ViewGroup中的invalidateChildInParent方法最后殊途同歸,都會調(diào)用到ViewRootImpl中的方法

可以看到在ViewRootImpl中最后都會調(diào)用scheduleTraversals方法進行繪制。按照對于View的繪制過程的理解,View的繪制流程是從ViewRootImpl的performTraversals方法開始的

public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.HardwareDrawCallbacks {
    
    //如果View沒有父布局,那invalidateInternal方法就會調(diào)用這個方法
    @Override
    public void invalidateChild(View child, Rect dirty) {
        invalidateChildInParent(null, dirty);
    }

    //ViewGroup的invalidateChild方法最后會調(diào)用到這里
    @Override
    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
        checkThread();
        //如果dirty為null就表示要重繪當(dāng)前ViewRootImpl指示的整個區(qū)域
        if (dirty == null) {
            invalidate();
            return null;
        //如果dirty為empty則表示經(jīng)過計算需要重繪的區(qū)域不需要繪制
        } else if (dirty.isEmpty() && !mIsAnimating) {
            return null;
        }
        return null;
    }   
    
    private void invalidateRectOnScreen(Rect dirty) {
        final Rect localDirty = mDirty;
        ...
        if (!mWillDrawSoon && (intersected || mIsAnimating)) {
            //調(diào)用scheduleTraversals方法進行繪制
            scheduleTraversals();
        }
    }
    
    //繪制整個ViewRootImpl區(qū)域
    void invalidate() {
        mDirty.set(0, 0, mWidth, mHeight);
        if (!mWillDrawSoon) {
            //調(diào)用scheduleTraversals方法進行繪制
            scheduleTraversals();
        }
    }
}

下面我們來看看ViewRootImpl中的scheduleTraversals方法

看到scheduleTraversals方法中調(diào)用了mChoreographer.postCallback方法

Choreoprapher類的作用是編排輸入事件、動畫事件和繪制事件的執(zhí)行,它的postCallback方法的作用就是將要執(zhí)行的事件放入內(nèi)部的一個隊列中,最后會執(zhí)行傳入的Runnable,這里也就是mTraversalRunnable。

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

來看看TraversalRunnable這個類做了什么?

可以看到最終調(diào)用了performTraversals()方法進行繪制

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();


void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }

        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}

大概總結(jié)一下

invalidate方法最終調(diào)用的是ViewRootImpl中的performTraversals來重新繪制View

在自定義View時,當(dāng)需要刷新View時,如果是在UI線程中,那就直接調(diào)用invalidate方法,如果是在非UI線程中,那就通過postInvalidate方法來刷新View

03.postInvalidate深入分析

先來看看View中的postInvalidate方法

@UiThread
public class View implements Drawable.Callback, KeyEvent.Callback,AccessibilityEventSource {
    
    ...
    
    public void postInvalidate() {
        postInvalidateDelayed(0);
    }
    
    public void postInvalidate(int left, int top, int right, int bottom) {
        postInvalidateDelayed(0, left, top, right, bottom);
    }
    
    public void postInvalidateDelayed(long delayMilliseconds) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
        }
    }
    ...      
}

可以看到,postInvalidate方法最后調(diào)用了ViewRootImpl的dispatchInvalidateDelayed方法

ViewRootImpl中的dispatchInvalidateDelayed方法就是像ViewRootHandler發(fā)送了一個MSG_INVALIDATE消息,ViewRootHandler是ViewRootImpl中的一個內(nèi)部Handler類

//發(fā)送消息
//更多內(nèi)容:https://github.com/yangchong211
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
    Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
    mHandler.sendMessageDelayed(msg, delayMilliseconds);
}

//接收消息
final class ViewRootHandler extends Handler {
    @Override
    public String getMessageName(Message message) {
        switch (message.what) {
            case MSG_INVALIDATE:
                return "MSG_INVALIDATE";
        }
        return super.getMessageName(message);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case MSG_INVALIDATE:
            ((View) msg.obj).invalidate();
            break;
        }
    }
}

大概總結(jié)一下

postInvalidate方法調(diào)用了ViewRootImpl中的dispatchInvalidateDelayed方法向ViewRootImpl中的ViewRootHandler發(fā)送一個消息,最后調(diào)用的還是View的invalidate方法。

因為ViewRootImpl是在UI線程的,所以postInvalidate方法的作用就是將非UI線程的刷新操作切換到UI線程,以便在UI線程中調(diào)用invalidate方法刷新View。所以如果我們自定義的View本身就是在UI線程,沒有用到多線程的話,直接用invalidate方法來刷新View就可以了。而我們平時自定義View基本上都沒有開起其他線程,所以這就是我們很少接觸postInvalidate方法的原因。

一句話總結(jié)postInvalidate方法的作用就是:實現(xiàn)了消息機制,可以使我們在非UI線程也能調(diào)用刷新View的方法。

04.requestLayout深入分析

源碼如下所示

在View的requestLayout方法中,首先會設(shè)置View的標(biāo)記位,PFLAG_FORCE_LAYOUT表示當(dāng)前View要進行重新布局,PFLAG_INVALIDATED表示要進行重新繪制。

requestLayout方法中會一層層向上調(diào)用父布局的requestLayout方法,設(shè)置PFLAG_FORCE_LAYOUT標(biāo)記,最終調(diào)用的是ViewRootImpl中的requestLayout方法。

//View.class
@CallSuper
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;
    }

    //設(shè)置PFLAG_FORCE_LAYOUT標(biāo)記位,這樣就會導(dǎo)致重新測量和布局
    mPrivateFlags |= PFLAG_FORCE_LAYOUT;
    //設(shè)置PFLAG_INVALIDATED就會進行重新繪制
    mPrivateFlags |= PFLAG_INVALIDATED;

    if (mParent != null && !mParent.isLayoutRequested()) {
        //不斷調(diào)用上層View的requestLayout方法
        mParent.requestLayout();
    }
    if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
        mAttachInfo.mViewRequestingLayout = null;
    }
}

然后看看ViewRootImpl中的requestLayout方法

可以看到ViewRootImpl中的requestLayout方法中會調(diào)用scheduleTraversals方法,scheduleTraversals方法最后會調(diào)用performTraversals方法開始執(zhí)行View的三大流程,會分別調(diào)用View的measure、layout、draw方法。

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

然后再看看measure測量方法

由于requestLayout方法設(shè)置了PFLAG_FORCE_LAYOUT標(biāo)記位,所以measure方法就會調(diào)用onMeasure方法對View進行重新測量。在measure方法中最后設(shè)置了PFLAG_LAYOUT_REQUIRED標(biāo)記位,這樣在layout方法中就會執(zhí)行onLayout方法進行布局流程。

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
    if (forceLayout || needsLayout) {
        // first clears the measured dimension flag
        mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;

        int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
        if (cacheIndex < 0 || sIgnoreMeasureCache) {
            //調(diào)用onMeasure方法
            onMeasure(widthMeasureSpec, heightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        } else {
            long value = mMeasureCache.valueAt(cacheIndex);
            setMeasuredDimensionRaw((int) (value >> 32), (int) value);
            mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }

        //設(shè)置PFLAG_LAYOUT_REQUIRED標(biāo)記位,用于layout方法
        mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
    }
}

再然后看看layout方法

由于measure方法中設(shè)置了PFLAG_LAYOUT_REQUIRED標(biāo)記位,所以在layout方法中onLayout方法會被調(diào)用執(zhí)行布局流程。最后清除PFLAG_FORCE_LAYOUT和PFLAG_LAYOUT_REQUIRED標(biāo)記位。

public void layout(int l, int t, int r, int b) {
    //由于measure方法中設(shè)置了PFLAG_LAYOUT_REQUIRED標(biāo)記位,所以會進入調(diào)用onLayout方法進行布局流程
    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
        onLayout(changed, l, t, r, b);

        if (shouldDrawRoundScrollbar()) {
            if(mRoundScrollbarRenderer == null) {
                mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
            }
        } else {
            mRoundScrollbarRenderer = null;
        }

        //取消PFLAG_LAYOUT_REQUIRED標(biāo)記位
        mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnLayoutChangeListeners != null) {
            ArrayList listenersCopy =
                    (ArrayList)li.mOnLayoutChangeListeners.clone();
            int numListeners = listenersCopy.size();
            for (int i = 0; i < numListeners; ++i) {
                listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
            }
        }
    }

    //取消PFLAG_FORCE_LAYOUT標(biāo)記位
    mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
    mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}

05.ViewRootImpl作用分析

鏈接WindowManager和DecorView的紐帶,另外View的繪制也是通過ViewRootImpl來完成的。

它的主要作用我的總結(jié)為如下:

A:鏈接WindowManager和DecorView的紐帶,更廣一點可以說是Window和View之間的紐帶。

B:完成View的繪制過程,包括measure、layout、draw過程。

C:向DecorView分發(fā)收到的用戶發(fā)起的event事件,如按鍵,觸屏等事件。

06.這幾個方法總結(jié)

requestLayout方法會標(biāo)記PFLAG_FORCE_LAYOUT,然后一層層往上調(diào)用父布局的requestLayout方法并標(biāo)記PFLAG_FORCE_LAYOUT,最后調(diào)用ViewRootImpl中的requestLayout方法開始View的三大流程,然后被標(biāo)記的View就會進行測量、布局和繪制流程,調(diào)用的方法為onMeasure、onLayout和onDraw。

invalidate方法我們分析過,它的過程和requestLayout方法方法很像,但是invalidate方法沒有標(biāo)記PFLAG_FORCE_LAYOUT,所以不會執(zhí)行測量和布局流程,而只是對需要重繪的View進行重繪,也就是只會調(diào)用onDraw方法,不會調(diào)用onMeasure和onLayout方法。

其他介紹 01.關(guān)于博客匯總鏈接

1.技術(shù)博客匯總

2.開源項目匯總

3.生活博客匯總

4.喜馬拉雅音頻匯總

5.其他匯總

02.關(guān)于我的博客

我的個人站點:www.yczbj.org,www.ycbjie.cn

github:https://github.com/yangchong211

知乎:https://www.zhihu.com/people/...

簡書:http://www.jianshu.com/u/b7b2...

csdn:http://my.csdn.net/m0_37700275

喜馬拉雅聽書:http://www.ximalaya.com/zhubo...

開源中國:https://my.oschina.net/zbj161...

泡在網(wǎng)上的日子:http://www.jcodecraeer.com/me...

郵箱:[email protected]

阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV

segmentfault頭條:https://segmentfault.com/u/xi...

掘金:https://juejin.im/user/593943...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/73845.html

相關(guān)文章

  • 03.AndroidView原理問題

    摘要:這種自定義控件在原生控件提供的方法外,可以自己添加一些方法。從頂層父到子遞歸調(diào)用方法,方法又回調(diào)。 目錄介紹 3.0.0.1 View的繪制需要經(jīng)過哪些過程?有哪些常用回調(diào)方法?View的繪制流程的詳細流程是怎樣的? 3.0.0.2 View繪制流程,當(dāng)一個TextView的實例調(diào)用setText()方法后執(zhí)行了什么?請說一下原理…… 3.0.0.3 requestLayout()、...

    FrozenMap 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<