Android系统中Flag的位设计
在android的系统类中经常看到精妙的位设计,从而取代了繁多的boolean类型,结构紧凑
例如View中的flag
* |-------|-------|-------|-------|
* 1 PFLAG_WANTS_FOCUS
* 1 PFLAG_FOCUSED
* 1 PFLAG_SELECTED
* 1 PFLAG_IS_ROOT_NAMESPACE
* 1 PFLAG_HAS_BOUNDS
* 1 PFLAG_DRAWN
* 1 PFLAG_DRAW_ANIMATION
* 1 PFLAG_SKIP_DRAW
* 1 PFLAG_REQUEST_TRANSPARENT_REGIONS
* 1 PFLAG_DRAWABLE_STATE_DIRTY
* 1 PFLAG_MEASURED_DIMENSION_SET
* 1 PFLAG_FORCE_LAYOUT
* 1 PFLAG_LAYOUT_REQUIRED
* 1 PFLAG_PRESSED
* 1 PFLAG_DRAWING_CACHE_VALID
* 1 PFLAG_ANIMATION_STARTED
* 1 PFLAG_SAVE_STATE_CALLED
* 1 PFLAG_ALPHA_SET
* 1 PFLAG_SCROLL_CONTAINER
* 1 PFLAG_SCROLL_CONTAINER_ADDED
* 1 PFLAG_DIRTY
* 1 PFLAG_DIRTY_MASK
* 1 PFLAG_OPAQUE_BACKGROUND
* 1 PFLAG_OPAQUE_SCROLLBARS
* 11 PFLAG_OPAQUE_MASK
* 1 PFLAG_PREPRESSED
* 1 PFLAG_CANCEL_NEXT_UP_EVENT
* 1 PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH
* 1 PFLAG_HOVERED
* 1 PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK
* 1 PFLAG_ACTIVATED
* 1 PFLAG_INVALIDATED
* |-------|-------|-------|-------|
*/
/** {@hide} */
static final int PFLAG_WANTS_FOCUS = 0x00000001;
/** {@hide} */
static final int PFLAG_FOCUSED = 0x00000002;
/** {@hide} */
static final int PFLAG_SELECTED = 0x00000004;
/** {@hide} */
static final int PFLAG_IS_ROOT_NAMESPACE = 0x00000008;
/** {@hide} */
static final int PFLAG_HAS_BOUNDS = 0x00000010;
/** {@hide} */
static final int PFLAG_DRAWN = 0x00000020;
/**
* When this flag is set, this view is running an animation on behalf of its
* children and should therefore not cancel invalidate requests, even if they
* lie outside of this view's bounds.
*
* {@hide}
*/
static final int PFLAG_DRAW_ANIMATION = 0x00000040;
/** {@hide} */
static final int PFLAG_SKIP_DRAW = 0x00000080;
/** {@hide} */
static final int PFLAG_REQUEST_TRANSPARENT_REGIONS = 0x00000200;
/** {@hide} */
static final int PFLAG_DRAWABLE_STATE_DIRTY = 0x00000400;
/** {@hide} */
static final int PFLAG_MEASURED_DIMENSION_SET = 0x00000800;
/** {@hide} */
static final int PFLAG_FORCE_LAYOUT = 0x00001000;
/** {@hide} */
static final int PFLAG_LAYOUT_REQUIRED = 0x00002000;
private static final int PFLAG_PRESSED = 0x00004000;
/** {@hide} */
static final int PFLAG_DRAWING_CACHE_VALID = 0x00008000;
/**
* Flag used to indicate that this view should be drawn once more (and only once
* more) after its animation has completed.
* {@hide}
*/
static final int PFLAG_ANIMATION_STARTED = 0x00010000;
private static final int PFLAG_SAVE_STATE_CALLED = 0x00020000;
/**
* Indicates that the View returned true when onSetAlpha() was called and that
* the alpha must be restored.
* {@hide}
*/
static final int PFLAG_ALPHA_SET = 0x00040000;
/**
* Set by {@link #setScrollContainer(boolean)}.
*/
static final int PFLAG_SCROLL_CONTAINER = 0x00080000;
/**
* Set by {@link #setScrollContainer(boolean)}.
*/
static final int PFLAG_SCROLL_CONTAINER_ADDED = 0x00100000;
/**
* View flag indicating whether this view was invalidated (fully or partially.)
*
* @hide
*/
static final int PFLAG_DIRTY = 0x00200000;
/**
* Mask for {@link #PFLAG_DIRTY}.
*
* @hide
*/
static final int PFLAG_DIRTY_MASK = 0x00200000;
今天我们就看下这是如何实现的
看下面的测试代码
int a1=0x00000001;
int a2=0x00000002;
int a3=0x00000004;
int a4=0x00000008;
int a5=0x00000010;
int a6=0x00000020;
int a7=0x00000040;
int a8=0x00000080;
int a9=0x00000100;
int flag_mask=0;
Log.i("test","result1:"+Integer.toBinaryString(a1));
Log.i("test","result2:"+Integer.toBinaryString(a2));
Log.i("test","result3:"+Integer.toBinaryString(a3));
Log.i("test","result4:"+Integer.toBinaryString(a4));
Log.i("test","result5:"+Integer.toBinaryString(a5));
Log.i("test","result6:"+Integer.toBinaryString(a6));
Log.i("test","result7:"+Integer.toBinaryString(a7));
Log.i("test","result8:"+Integer.toBinaryString(a8));
Log.i("test","result9:"+Integer.toBinaryString(a9));
结果:
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result1:1
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result2:10
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result3:100
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result4:1000
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result5:10000
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result6:100000
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result7:1000000
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result8:10000000
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result9:100000000
上面这个代码是模仿了系统中的代码写法实现的,a1~a9是各种标志,这些标志代表各种功能的true,false
我们看到这些标志有个规律是按照:1,2,4,8,10,20,40,80,100 因为这个是16进制的(0x代表16进制)所以意思是数字大小是前面个的一倍计算的,这样可以保证转成2进制后,除了自己标志位那里是1,其余都是0
1、添加标志
我们经常要往flag_mask(各种标志的结果集合)里写入这个标志,该如何操作的,答案是应该用| 或位运算符
例如:
int flag_mask=0;//当前是空的 ,什么标志都没有
int a1=0x00000001;
int a2=0x00000002;
int a3=0x00000004;
int a4=0x00000008;
往里写入一个a1,a4,这两个标志:
flag_mask = flag_mask|a1;
flag_mask = flag_mask|a4;
或则直接简写
flag_mask |= a1;
flag_mask |= a4;
Log.i("test","添加标志:"+Integer.toBinaryString(flag_mask));
结果:
2021-11-03 11:42:27.785 31601-31601/com.xx.bb I/test: 添加标志:1001
这样成在1,4位分别添加了标志位
分析:
flag_mask=00000000 00000000 00000000 00000000 int是4字节,32位的
a1 =00000000 00000000 00000000 00000001
按位或操作的结果:
result =00000000 00000000 00000000 00000001
所以通过|操作符可以实现标志位的添加功能
2、提取标志
//提取标志
//提取的功能主要是用来判断某个标志位是否存在,是否已经设置了
boolean isA1Seted=(flag_mask&a1) == a1;
Log.i("test","a1是否设置:"+isA1Seted);
boolean isA4Seted=(flag_mask&a4) == a4;
Log.i("test","a4是否设置:"+isA4Seted);
运行结果:
2021-11-03 11:48:24.304 32664-32664/com.xx.bb I/test: a1是否设置:true
2021-11-03 11:48:24.304 32664-32664/com.xx.bb I/test: a4是否设置:true
分析:
flag_mask=00000000 00000000 00000000 00001001 int是4字节,32位的
a1 =00000000 00000000 00000000 00000001
a4 =00000000 00000000 00000000 00001000
通过按位于操作符&
resultA1 =00000000 00000000 00000000 00000001
resultA4 =00000000 00000000 00000000 00001000
所以要提取某个标志位,然后判断是否存在就使用&操作符
3、删除标志
//删除标志
//有的时候我们会把某个标志从mask里面删除,该怎么操作呢?
flag_mask &= ~a1;
flag_mask &= ~a4;
运行结果:
2021-11-03 11:58:59.481 1833-1833/com.xx.bb I/test: 删除后的结果flag_mask:0
Log.i("test","删除后的结果flag_mask:"+Integer.toBinaryString(flag_mask));
分析:
flag_mask=00000000 00000000 00000000 00001001 int是4字节,32位的
~a1 =11111111 11111111 11111111 11111110 按位取反的结果
~a4 =11111111 11111111 11111111 11110111 按位取反的结果
按位与&
flag_mask=00000000 00000000 00000000 00001000
flag_mask=00000000 00000000 00000000 00000000
最终结果把a1和a4都删除了
总结:系统的源代码是很好的老师,我们写代码的时候如果有很多的boolean变量逻辑,我们就可以按照这样通过位操作实现标志的方式来实现功能,这样代码高效紧凑