首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >setVisibility源码解析

setVisibility源码解析

作者头像
提莫队长
发布2020-06-02 15:24:40
1.1K0
发布2020-06-02 15:24:40
举报
文章被收录于专栏:刘晓杰刘晓杰

1.问题引入

做项目的时候,AppBarLayout里面嵌套RecycleView。当没有数据的时候就提示请求出错的图片。当Fragment互相切换切回这个Fragment的时候(切的时候因为需求原因,有的时候需要沉浸式,有的时候又不需要,所以设置了Padding),重新请求数据,发现请求出错的图片往下挪了点位置。后来经过定位,是在设置Visiblity的时候引起的,而且获取同一个控件宽高的时候发现位置和padding还有点关系,注释掉padding就没有这个问题。(我看了一下setPadding源码,发现它会重绘整个view)很明显,setVisiblity和重绘有关联,需要看源码

2.源码解析

    void setFlags(int flags, int mask) {
        ......
        //保存当前视图状态为old
        int old = mViewFlags;
        //更新视图状态为将要更改后的属性。
        mViewFlags = (mViewFlags & ~mask) | (flags & mask);
        //获取其更改的属性位
        int changed = mViewFlags ^ old;
        //记录当前视图的逻辑属性。
        int privateFlags = mPrivateFlags;

        final int newVisibility = flags & VISIBILITY_MASK;
        if (newVisibility == VISIBLE) {
            if ((changed & VISIBILITY_MASK) != 0) {
                /*
                 * If this view is becoming visible, invalidate it in case it changed while
                 * it was not visible. Marking it drawn ensures that the invalidation will
                 * go through.
                 */
                mPrivateFlags |= PFLAG_DRAWN;
                invalidate(true);
                //invalidate自己,child

                needGlobalAttributesUpdate(true);

                ......
            }
        }

        //如果视图的属性要设置为GONE
        if ((changed & GONE) != 0) {
            //需要全局属性更新,因为GONE属性设置其视图不见了,其他视图的位置也会受到影响。
            needGlobalAttributesUpdate(false);
            //申请重新布局
            requestLayout();

            if (((mViewFlags & VISIBILITY_MASK) == GONE)) {
                ......
                if (mParent instanceof View) {
                    // GONE views noop invalidation, so invalidate the parent
                    ((View) mParent).invalidate(true);
                }
                mPrivateFlags |= PFLAG_DRAWN;
                //requestLayout,invalidate parent,然后设置PFLAG_DRAWN以便下次invalidate
            }
            ......
        }

        //如果视图将要设置为INVISIBLE了
        if ((changed & INVISIBLE) != 0) {
            //不需要全局属性更新
            needGlobalAttributesUpdate(false);
            /*
             * If this view is becoming invisible, set the DRAWN flag so that
             * the next invalidate() will not be skipped.
             */
            mPrivateFlags |= PFLAG_DRAWN;
            //改变标记位PFLAG_DRAWN,以便下次invalidate()
            ......
        }
        if ((changed & VISIBILITY_MASK) != 0) {
            ......

            if (mParent instanceof ViewGroup) {
                ((ViewGroup) mParent).onChildVisibilityChanged(this,
                        (changed & VISIBILITY_MASK), newVisibility);
                ((View) mParent).invalidate(true);
            } else if (mParent != null) {
                mParent.invalidateChild(this, null);
            }

            ......
        }
        ......
    }

可以做如下总结 setVisibility=View.VISIBLE ------invalidate自己,parent,child setVisibility=View.INVISIBLE ------改变标记位PFLAG_DRAWN,以便下次invalidate() setVisibility=View.GONE ------requestLayout,invalidate parent,然后设置PFLAG_DRAWN以便下次invalidate

那么,可以看出GONE的时候会requestLayout,并且全局属性更新。如果从VISIBLE切换到GONE的时候是不会有什么问题的,但是从GONE切换到VISIBLE的时候,会抢占焦点

3.问题解决

setPadding导致整个view重绘,使得原本处于屏幕中间的图不再处于正中间,网络请求结束以后,先对整个内容区域的所有控件设置GONE,在对请求出错的图片设置VISIBLE导致自身重绘到内容区域的最中间,所以会往下挪。而且往下挪的位置正好是padding的距离

4.总结

对于那些可滑动性的控件(ListView,RecycleView,ScrollView)而言,当内部控件设置为GONE和VISIBLE的时候,一定要注意重绘的问题,很可能因为重绘问题导致移位。 扩展:除了重绘的问题需要注意,同时重绘还可能导致焦点抢占的问题也需要注意,可能会出现抢占焦点导致整个RecycleView的内容往上滑动。这个问题很好解决,添加descendantFocusability属性就行

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.问题引入
  • 2.源码解析
  • 3.问题解决
  • 4.总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档