国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

View之invalidate,requestLayout,postInvalidate

zhangyucha0 / 1680人閱讀

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

目錄介紹

01.invalidate,requestLayout,postInvalidate區別

02.invalidate深入分析

03.postInvalidate深入分析

04.requestLayout深入分析

05.ViewRootImpl作用分析

06.這幾個方法總結

好消息

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

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

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

01.Java基礎問題(19個)

02.Java面向對象問題(10個)

03.Java數據結構問題(20個)

04.JavaIO流問題(6個)

05.java多線程問題(19)

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

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

08.Java反射問題(6個)

10.Java異常問題(9個)

01.requestLayout、invalidate與postInvalidate作用與區別

invalidate() postInvalidate()

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

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

requestLayout()

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

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

02.invalidate深入分析

看看invalidate源碼,如下所示

invalidateCache為true表示全部重繪。View的invalidate方法最后調用的是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方法中有一個循環,循環里面會一直調用父布局的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) {
            //這是一個從當前的布局View向上不斷遍歷當前布局View的父布局,最后遍歷到ViewRootImpl的循環
            do {
                View view = null;
                if (parent instanceof View) {
                    view = (View) parent;
                }
                
                //這里調用的是父布局的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) {
                ...
                //這里也是一些計算繪制區域的內容
                return mParent;
            } else {
                mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;
                //這里也是一些計算繪制區域的內容
                return mParent;
            }
        }
        return null;
    }
}

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

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

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

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

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

看到scheduleTraversals方法中調用了mChoreographer.postCallback方法

Choreoprapher類的作用是編排輸入事件、動畫事件和繪制事件的執行,它的postCallback方法的作用就是將要執行的事件放入內部的一個隊列中,最后會執行傳入的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這個類做了什么?

可以看到最終調用了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;
        }
    }
}

大概總結一下

invalidate方法最終調用的是ViewRootImpl中的performTraversals來重新繪制View

在自定義View時,當需要刷新View時,如果是在UI線程中,那就直接調用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方法最后調用了ViewRootImpl的dispatchInvalidateDelayed方法

ViewRootImpl中的dispatchInvalidateDelayed方法就是像ViewRootHandler發送了一個MSG_INVALIDATE消息,ViewRootHandler是ViewRootImpl中的一個內部Handler類

//發送消息
//更多內容: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;
        }
    }
}

大概總結一下

postInvalidate方法調用了ViewRootImpl中的dispatchInvalidateDelayed方法向ViewRootImpl中的ViewRootHandler發送一個消息,最后調用的還是View的invalidate方法。

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

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

04.requestLayout深入分析

源碼如下所示

在View的requestLayout方法中,首先會設置View的標記位,PFLAG_FORCE_LAYOUT表示當前View要進行重新布局,PFLAG_INVALIDATED表示要進行重新繪制。

requestLayout方法中會一層層向上調用父布局的requestLayout方法,設置PFLAG_FORCE_LAYOUT標記,最終調用的是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;
    }

    //設置PFLAG_FORCE_LAYOUT標記位,這樣就會導致重新測量和布局
    mPrivateFlags |= PFLAG_FORCE_LAYOUT;
    //設置PFLAG_INVALIDATED就會進行重新繪制
    mPrivateFlags |= PFLAG_INVALIDATED;

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

然后看看ViewRootImpl中的requestLayout方法

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

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

然后再看看measure測量方法

由于requestLayout方法設置了PFLAG_FORCE_LAYOUT標記位,所以measure方法就會調用onMeasure方法對View進行重新測量。在measure方法中最后設置了PFLAG_LAYOUT_REQUIRED標記位,這樣在layout方法中就會執行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) {
            //調用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;
        }

        //設置PFLAG_LAYOUT_REQUIRED標記位,用于layout方法
        mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
    }
}

再然后看看layout方法

由于measure方法中設置了PFLAG_LAYOUT_REQUIRED標記位,所以在layout方法中onLayout方法會被調用執行布局流程。最后清除PFLAG_FORCE_LAYOUT和PFLAG_LAYOUT_REQUIRED標記位。

public void layout(int l, int t, int r, int b) {
    //由于measure方法中設置了PFLAG_LAYOUT_REQUIRED標記位,所以會進入調用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標記位
        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標記位
    mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
    mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}

05.ViewRootImpl作用分析

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

它的主要作用我的總結為如下:

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

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

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

06.這幾個方法總結

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

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

其他介紹 01.關于博客匯總鏈接

1.技術博客匯總

2.開源項目匯總

3.生活博客匯總

4.喜馬拉雅音頻匯總

5.其他匯總

02.關于我的博客

我的個人站點: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...

泡在網上的日子:http://www.jcodecraeer.com/me...

郵箱:yangchong211@163.com

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

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

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

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73845.html

相關文章

  • 03.AndroidView原理問題

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

    FrozenMap 評論0 收藏0

發表評論

0條評論

zhangyucha0

|高級講師

TA的文章

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