日常编码中我们设置状态,如果是一种情形的两种状态,可以用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.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了。
在分析之前先补习一下位运算符:
& 与: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时,要重新绘图。
这是三种标识,他们内部的状态都会在相应的标识位置发生变化,不会冲突。如下图:
本文以View的setFlag方法为例,探讨Android源码中如何利用Flag进行状态管理。当View设置为gone时,系统会通过Flag调整绘制逻辑。这种机制确保了不同状态之间的逻辑互不干扰。
1850

被折叠的 条评论
为什么被折叠?



