天天看点

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();

        }

    }

继续阅读