天天看點

Android View系統源碼分析(十)—— View.setVisibility(int visibility)

View.setVisibility

該函數用于改變視圖的可視狀态,可視狀态包括GONE、VISIBLE、INVISIBLE三種。該函數内部很簡單,首先調用setFlags(),然後調用mBGDrawable.setVisible()函數來改變視圖背景圖的顯示狀态。
Android View系統源碼分析(十)—— View.setVisibility(int visibility)

點我下載下傳VSDX

View.setVisibility(int visibility)

    @RemotableViewMethod

    public void setVisibility(int visibility) {

        //設定visibility屬性位的标記。

        setFlags(visibility, VISIBILITY_MASK);

        //如果目前視圖的背景不為空,調用目前背景drawable對象的.setVisible

        if (mBGDrawable != null) mBGDrawable.setVisible(visibility == VISIBLE, false);

    }

該函數被廣泛使用,例如 View.setEnable()、View.setClickable()等很多函數都調用到該函數。

在View中使用mViewFlags和mPrivateFlags變量儲存大多數的屬性:

– mViewFlags:儲存和視圖狀态相關的屬性。

– mPrivateFlags儲存和内部邏輯相關的屬性

View.setFlags(int flags , int mask)

    void setFlags(int flags , int mask) {

        //儲存目前視圖狀态為old

        int old = mViewFlags;

        //更新視圖狀态為将要更改後的屬性。

        mViewFlags = (mViewFlags & ~mask) | (flags & mask);

        //擷取其更改的屬性位

        int changed = mViewFlags ^ old;

        //如果更改屬性位為0,表示其并沒有做任何的視圖改變,直接return。

        if (changed == 0) {

            return;

        }

        //記錄目前視圖的邏輯屬性。

        int privateFlags = mPrivateFlags;

        //判斷如果 FOCUSABLE位 有變更

        //如果 視圖變更位changed中的FOCUSABLE位區域有變更,并且記錄的目前視圖的邏輯位privateFlags表示該視圖是有邊界的(存在視圖大小的)。

        if (((changed & FOCUSABLE_MASK) != 0) &&

                ((privateFlags & HAS_BOUNDS) !=0)) {

            //如果目前視圖位的FOCUSABLE屬性位為有擷取焦點的能力,并且目前邏輯privateFlags表示該視圖已經擷取了焦點

            if (((old & FOCUSABLE_MASK) == FOCUSABLE)

                    && ((privateFlags & FOCUSED) != 0)) {

                //如果不是具備長焦點擷取能力的話,就放棄目前視圖的焦點。

                //調用View.clearFocus(),清除焦點。

                clearFocus();

                //如果目前視圖的FOCUSABLE屬性位為沒有擷取焦點的能力,并且目前的邏輯标記位無焦點。

            } else if (((old & FOCUSABLE_MASK) == NOT_FOCUSABLE)

                    && ((privateFlags & FOCUSED) == 0)) {

                //如果視圖的父視圖不為空,通知其父視圖,有新的子視圖可以獲得焦點。這裡僅僅是通知其父視圖,至于父視圖是否要把焦點配置設定給該視圖則不一定。

                if (mParent != null) mParent.focusableViewAvailable(this);

            }

        }

        //如果設定标記的flags的VISIBILITY位為VISIBLE

        if ((flags & VISIBILITY_MASK) == VISIBLE) {

            //如果VISIBILITY位有變化

            if ((changed & VISIBILITY_MASK) != 0) {

                //邏輯一标記mPrivateFlags添加 DRAWN标記。

                mPrivateFlags |= DRAWN;

                //該函數将給mAttachInfo對象中的mRecomputeGloabalAttributes變量指派為true。該變量的含義實際上是指“需要從on更新計算View樹中視圖的位置”,因為該視圖顯示查出來後一般會導緻其他視圖在界面上的位置發生變化,是以View系統需要重新計算位置。

                needGlobalAttributesUpdate(true);

                // a view becoming visible is worth notifying the parent

                // about in case nothing has focus.  even if this specific view

                // isn't focusable, it may contain something that is, so let

                // the root view try to give this focus if nothing else does.

                //如果有父視圖并且該視圖為合法視圖(也就是有大小的視圖)

                if ((mParent != null) && (mBottom > mTop) && (mRight > mLeft)) {

                    //通知其父視圖有了新的子視圖。添加的新視圖不管怎樣都需要将其申請一次擷取焦點,即使其沒有擷取焦點的能力。

                    mParent.focusableViewAvailable(this);

                }

            }

        }

        //檢查如果是GONE屬性位發生了變化

        //如果視圖的屬性要設定為GONE

        if ((changed & GONE) != 0) {

            //需要全局屬性更新,因為GONE屬性設定其視圖不見了,其他視圖的位置也會受到影響。

            needGlobalAttributesUpdate(false);

            //申請重新布局

            requestLayout();

            //重新繪制視圖。

            invalidate();

            //如果更改後的視圖屬性的VISIBILITY屬性位為GONE

            if (((mViewFlags & VISIBILITY_MASK) == GONE)) {

                //如果目前視圖有焦點的話,就将其焦點清除

                if (hasFocus()) clearFocus();

                //釋放視圖所使用的緩存,當然并不是所有的視圖都有緩存。

                destroyDrawingCache();

            }

            //如果mAttachInfo對象不為空

            if (mAttachInfo != null) {

                //将對象中的mVisibilityChanged設定為true。

                mAttachInfo.mViewVisibilityChanged = true;

            }

        }

        //檢查如果VISIBLE的屬性位發生了變化

        //如果視圖将要設定為INVISIBLE了

        if ((changed & INVISIBLE) != 0) {

            //不需要全局屬性更新

            needGlobalAttributesUpdate(false);

            //重新繪制制圖

            invalidate();

            //如果作用後的視圖屬性的VISIBILITY位為INVISIBLE 并且 目前視圖擁有焦點

            if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE) && hasFocus()) {

                // root view becoming invisible shouldn't clear focus

                //如果目前視圖不是根視圖的話

                if (getRootView() != this) {

                    //清除焦點

                    clearFocus();

                }

            }

            if (mAttachInfo != null) {

                //更改該視圖的标記對象的mViewVisibilityChanged為true。

                mAttachInfo.mViewVisibilityChanged = true;

            }

        }

        //如果更改的屬性位的VISIBILITY屬性位有改變

        if ((changed & VISIBILITY_MASK) != 0) {

            //該函數會回調onVisibilityChanged(),應用程式可以重載該函數以便執行其他操作。

            dispatchVisibilityChanged(this, (flags & VISIBILITY_MASK));

        }

        //對視圖緩存以及繪制标記的處理。

        if ((changed & WILL_NOT_CACHE_DRAWING) != 0) {

            destroyDrawingCache();

        }

        if ((changed & DRAWING_CACHE_ENABLED) != 0) {

            destroyDrawingCache();

            mPrivateFlags &= ~DRAWING_CACHE_VALID;

        }

        if ((changed & DRAWING_CACHE_QUALITY_MASK) != 0) {

            destroyDrawingCache();

            mPrivateFlags &= ~DRAWING_CACHE_VALID;

        }

        //如果DRAW_MASK标志位有改變。

        if ((changed & DRAW_MASK) != 0) {

            //如果視圖将設定為 WILL_NOT_DRAW屬性

            if ((mViewFlags & WILL_NOT_DRAW) != 0) {

                //如果視圖背景不為空

                if (mBGDrawable != null) {

                    //去掉SKIP_DRAW的标志位

                    mPrivateFlags &= ~SKIP_DRAW;

                    //添加ONLY_DRAWS_BACKGROUND标志位

                    mPrivateFlags |= ONLY_DRAWS_BACKGROUND;

                } else {

                    //如果視圖背景為空,添加跳過繪制标志位

                    mPrivateFlags |= SKIP_DRAW;

                }

            } else {

                //如果DRAW_MASK标志位沒有改變,清除SKIP_DRAW的标志位

                mPrivateFlags &= ~SKIP_DRAW;

            }

            //重新申請布局,重新繪制視圖

            requestLayout();

            invalidate();

        }

        //如果改變的标志位的 KEEP_SCREEN_ON 屬性位有改變

        if ((changed & KEEP_SCREEN_ON) != 0) {

            //如果父視圖不為空

            if (mParent != null) {

                //父視圖重新計算目前視圖的屬性。

                mParent.recomputeViewAttributes(this);

            }

        }

    }

ViewParent.focusableViewAvailable(View v)

    public void focusableViewAvailable(View v);

View.destroyDrawingCache()

    public void destroyDrawingCache() {

        //如果繪制緩存不為空

        if (mDrawingCache != null) {

            //擷取緩存中的bitmap資料引用

            final Bitmap bitmap = mDrawingCache.get();

            //如果bitmap不為空,就執行回收bitmap對象

            if (bitmap != null) bitmap.recycle();

            //設定繪制緩存為空

            mDrawingCache = null;

        }

        //縮略圖緩存不為空

        if (mUnscaledDrawingCache != null) {

            //擷取縮略圖緩存的bitmap資料的引用

            final Bitmap bitmap = mUnscaledDrawingCache.get();

            //如果bitmap不為空,就執行回收bitmap對象

            if (bitmap != null) bitmap.recycle();

            //設定縮略圖繪制緩存對象為空

            mUnscaledDrawingCache = null;

        }

    }

View.clearFocus()

    public void clearFocus() {

        if (DBG) {

            System.out.println(this + " clearFocus()");

        }

        //如果目前視圖的邏輯屬性位為有焦點的

        if ((mPrivateFlags & FOCUSED) != 0) {

            //清空邏輯屬性位的焦點屬性。

            mPrivateFlags &= ~FOCUSED;

            //如果目前視圖有父視圖

            if (mParent != null) {

                //通知其父視圖清空其子視圖的焦點

                mParent.clearChildFocus(this);

            }

            //調用onFocusChanged方法

            onFocusChanged(false, 0, null);

            //重新整理繪制狀态。

            refreshDrawableState();

        }

    }

繼續閱讀