五、INVISIBLE、GONE、VISIBLE的实现逻辑

本文深入探讨了Android中View的可见性控制,包括GONE、INVISIBLE和VISIBLE的实现原理。通过分析FrameLayout的onMeasure和onLayout方法,揭示了GONE如何在测量阶段被过滤以及在布局时不占用空间。同时,详细解释了INVISIBLE在测量阶段与VISIBLE相同,但在布局阶段依然占据位置。通过对View的setVisibility源码分析,理解了如何切换这三个状态并影响视图的绘制和更新。文章强调了自定义ViewGroup时处理GONE事件的重要性,并指出INVISIBLE的控制不在ViewGroup层面,而是在View的可见性设置中完成。

INVISIBLE、GONE、VISIBLE这三个变量,应该是我们最常用的了,有没有思考系统是怎么实现的呢?

ViewGroup

首先要明确一点,通常使用的View都是放在ViewGroup以及ViewGroup子类的,大小都是在父控件的onMeasure和onLayout来进行确定。我们从最简单的FrameLayout原来来看看,它是怎么实现View的GONE操作的。

onMeasure阶段:
   @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   
   
        int count = getChildCount();
		//判断自己是不是EXACTLY,对应的就是判断自己是不是Match_Parent
        final boolean measureMatchParentChildren =
                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
                MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
        mMatchParentChildren.clear();

        int maxHeight = 0;
        int maxWidth = 0;
        int childState = 0;
		//获取最大宽度与高度
        for (int i = 0; i < count; i++) {
   
   
            final View child = getChildAt(i);
            //需要测量所有子view||不为GONE时,才需要测量
            if (mMeasureAllChildren || child.getVisibility() != GONE) {
   
   
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                maxWidth = Math.max(maxWidth,
                        child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                maxHeight = Math.max(maxHeight,
                        child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                childState = combineMeasuredStates(childState, child.getMeasuredState());
                if (measureMatchParentChildren) {
   
   
                    if (lp.width == LayoutParams.MATCH_PARENT ||
                            lp.height == LayoutParams.MATCH_PARENT) {
   
   
                        mMatchParentChildren.add(child);
                    }
                }
            }
        }
		//加上pandding
        maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
        maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
		//.....
		//设置好FrameLayout的高度和宽度.此时FrameLayout的measureHeight与MeasureWidth已经有值了
        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                resolveSizeAndState(maxHeight, heightMeasureSpec,
                        childState << MEASURED_HEIGHT_STATE_SHIFT));
         //.....               
        }
    }

上面的onMeasure其实就做一件事,就是把FrameLayout的maxWidth和maxHeight算出来。在计算时过滤掉mMeasureAllChildren = false是的child是gone的情况,那么问题来了,这个mMeasureAllChildren是做什么的呢?看源码的赋值,我们找到方法

  /**
     * Sets whether to consider all children, or just those in
     * the VISIBLE or INVISIBLE state, when measuring. Defaults to false.
     *
     * @param measureAll true to consider children marked GONE, false otherwise.
     * Default value is false.
     *
     * @attr ref android.R.styleable#FrameLayout_measureAllChildren
     */
    @android.view.RemotableViewMethod
    public void setMeasureAllChildren(boolean measureAll) {
   
   
        mMeasureAllChildren = measureAll;
    }

看注释的意思是,当为true时,为GONE的所有children也会被测量。所以,也并不是说GONE了,我们就不会去测量这个child。得是FrameLayout这个measureAllChildren的属性为false时,GONE才不会去测量。为了验证我们的阅读的源码。我写出了以下例子:
假设我们有如下xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:measureAllChildren="true"
    android:background="@color/black"
    tools:context=".MainActivity">

    
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值