Android源码用到的Flag设置标志位机制

本文以View的setFlag方法为例,探讨Android源码中如何利用Flag进行状态管理。当View设置为gone时,系统会通过Flag调整绘制逻辑。这种机制确保了不同状态之间的逻辑互不干扰。
日常编码中我们设置状态,如果是一种情形的两种状态,可以用0或者1来区分,如果是多种情形每种情形还有多个状态,我们可以利用android这套设置标志位机制,它的原理就是利用int不同位表示不同的情形,再利用这些位所包含的二进制数来代表不同的状态,这样我们就可以用一个int类型变量来表示所有的状态了。
在分析之前先补习一下位运算符:
& 与:11为1,就是当两者都为1时才为1。   例:1100 & 1001 = 1000
| 或:两者有一个为1结果就为1.           例:1100 | 1001 = 1101
~ 非:1变0,0变1                         例:~1100 = 0011
^ 异或:左右位相同时为0,不同时为1.       例:1100 ^ 1001 = 0101

现在以View中setFlag方法为例说明这种标志机制。

    void setFlags(int flags, int mask) {
	
	... ...
	
        int old = mViewFlags;
        mViewFlags = (mViewFlags & ~mask) | (flags & mask);

        int changed = mViewFlags ^ old;
        if (changed == 0) {
            return;
        }
		
	... ...
	
	    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);

                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);
                }
            }
        }
		/* Check if the GONE bit has changed */
        if ((changed & GONE) != 0) {
            needGlobalAttributesUpdate(false);
            requestLayout();

            if (((mViewFlags & VISIBILITY_MASK) == GONE)) {
                if (hasFocus()) clearFocus();
                clearAccessibilityFocus();
                destroyDrawingCache();
                if (mParent instanceof View) {
                    // GONE views noop invalidation, so invalidate the parent
                    ((View) mParent).invalidate(true);
                }
                // Mark the view drawn to ensure that it gets invalidated properly the next
                // time it is visible and gets invalidated
                mPrivateFlags |= PFLAG_DRAWN;
            }
            if (mAttachInfo != null) {
                mAttachInfo.mViewVisibilityChanged = true;
            }
        }

        /* Check if the VISIBLE bit has changed */
        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;

            if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE)) {
                // root view becoming invisible shouldn't clear focus and accessibility focus
                if (getRootView() != this) {
                    if (hasFocus()) clearFocus();
                    clearAccessibilityFocus();
                }
            }
            if (mAttachInfo != null) {
                mAttachInfo.mViewVisibilityChanged = true;
            }
        }
	... ...
		
	}
源码中该方法有两个参数,flags代表将要设置的状态,mask代表这个状态的标志位位置。
例如:用户设置view.setVisibility  视图的显示状态分为三种visible、invisible、gone
用户设置view.focusable 视图获取焦点状态分为两种focusable、not_focusable
 一共两种情形五种状态,那么我们怎么用一个int表示呢。
 首先我们将不同情形分开,所以这里用到了mask。
     visiable = 0x00000000                        后四位    0000
     invisiable = 0x00000004                      后四位   0100
     gone = 0x00000008                              后四位   1000
      VISIBILITY_MASK = 0x0000000C      后四位   1100
比较后四位,这个VISIBILITY_MASK 就代表了标志位位置 这一组三个状态只能在这个标志位(1100)表示的数之内变化,
并且visiable+invisiable+gone = VISIBILITY_MASK

同理
NOT_FOCUSABLE = 0x00000000        后四位  0000
FOCUSABLE = 0x00000001                   后四位  0001
FOCUSABLE_MASK = 0x00000001      后四位  0001
focusable变化就两种,所以只是最后一位用0和1就能满足。
mask代表一个标志位的位置,它会记录标志会在哪里发生变化。

这里有个说明:visiable & VISIBILITY_MASK = 0000
                            invisiable & VISIBILITY_MASK  = 0100
                            gone & VISIBILITY_MASK = 1000
   一个标志与它所对应的mask进行与运算得到的就是它自己本身,一个标志与其他mask进行与运算最终结果一定是0,这也侧面解释了mask就是记录标志位的位置。
int old = mViewFlags 代表将old flag 保存起来
mViewFlags = (mViewFlags & ~mask) | (flags & mask); 
得到一个新的viewFlag,作为当前的flag。前半句 mViewFlags & ~mask 将mViewFlags表示该flag的标志位位置(mask标识的位置)置为0,flags & mask 得到当前mask标志位位置的标志,两边求位或,结果就是新的flag。
现在以一个例子说明,如果当前View状态为Visiable,我们设置setVisiable(View.GONE),即这里的old = 0000,mViewFlags = 1000
代码中 int changed = mViewFlags ^ old;    changed展现了mask标志位位置改变的位
changed = 0000|1000 = 1000 > 0 肯定大于0 所以执行下面代码判断是哪里发生了改变,如果 等于0 代表没有发生变化直接return了。
    接下来根据changed等具体验证新的flag(mViewFlags)是哪个动作。我们是从visiable变成gone的因此下面这段代码是不执行的。因为newVisiability = GONE。假如状态是由是哪个动作。我们是从visiable变成gone的因此下面这段代码是不执行的。因为newVisiability变成GONE了。

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);
                   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);
				  
				  }
		}
	}
	
        /* Check if the GONE bit has changed */
        if ((changed & GONE) != 0) {
            needGlobalAttributesUpdate(false);
            requestLayout();

            if (((mViewFlags & VISIBILITY_MASK) == GONE)) {
                if (hasFocus()) clearFocus();
                clearAccessibilityFocus();
                destroyDrawingCache();
                if (mParent instanceof View) {
                    // GONE views noop invalidation, so invalidate the parent
                    ((View) mParent).invalidate(true);
                }
                // Mark the view drawn to ensure that it gets invalidated properly the next
                // time it is visible and gets invalidated
                mPrivateFlags |= PFLAG_DRAWN;
            }
            if (mAttachInfo != null) {
                mAttachInfo.mViewVisibilityChanged = true;
            }
        }

我们跳到上面的方法,当View为gone时,要重新绘图。


这是三种标识,他们内部的状态都会在相应的标识位置发生变化,不会冲突。如下图:



总结:一个类中用一个int变量可以表示多种状态,用一个mask记录标志位位置,在它下面生成若干位表示不同的状态,在二进制数之间用位运算进行相互判断,不用分多种情况去写各个状态的代码了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值