Java使用位域进行多标记(状态)管理

Android中位域的应用

在Android中,我们会经常用到或者看到以下这样的代码 :

public class ExampleUnitTest {
    @Test
    public void gravityTest(LayoutParams params) {
        // 视图在layout中右下角显示
        params.gravity = Gravity.RIGHT | Gravity.BOTTOM;
    }
    @Test
    public void intentFlagTest(Intent intent) {
        // 清空任务栈中所有旧的activity
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_CLEAR_TASK);

        // 如果activity已存在于栈中,清空该activity之上的所有任务
        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP
                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    }
    @Test
    public void windowMangerFlags(WindowManager.LayoutParams params) {
        // 不拦截视图以外的事件,在锁屏中显示
        params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
    }
}

通过一个 int字段,来添加多个 标志或者状态. 一个int字段,能够管理多个标记(状态)值.
如此神奇的操作怎样实现的呢? 答案就是通过位运算来实现.

位操作基础

java中提供的基础位运算符有 与(&),或(|),非(~),异或(^),左移<<,
右移(>>)无符号右移(>>>).

除了位非(~)是一元操作符外,其它的都是二元操作符。

下面只介绍本文中,使用到的位操作

  • 按位与

A & B : A和B对应的二进制数位都为1时,结果才为1,其他情况为0.

A     =  001101 // 13
B     =  100101 // 37
A & B =  000101 // 5
  • 按位或

A | B : A和B对应的二进制数位都为0时,结果才为0,其他情况为1.

A     =  001101 // 13
B     =  100101 // 37
A | B =  101101 // 45
  • 按位非

~A : 将a的二进制表示每一位进行取反操作,0变1,1变0.
相当于相反数 - 1

A     =  001101 // 13
~A    =  11111111111111111111111111110010 // int32位,补码表示,第一位为符号位
// 根据上诉补码转原码为
//       10000000000000000000000000001110 // -14
  • 左移操作

A << B:将A的二进制表示的每一位向左移B位,左边超出的位截掉,右边不足的位补0。
在取值范围内,移动一位相当于乘2.

A     =  001101 // 13
A << 1 = 011010 // 26

原理与实践

通常情况下,如果多个状态或者标记相互之间有关联, 如布局方向,上下左右,左上,居中 ... 等.我们可能会为每一个标记设置一个变量.

boolean left = false;
boolean right = false;
...
void setLeft(boolean b);
void setRight(boolean b);
...

这种情况下, 有4个标记相互关联,并且能产生新的标记,那么我们就需要设置4个标记变量,然后只能通过一系列的set方法来转换状态.如

v.setLeft(true);
v.setRight(true);

这样就会使得,各个状态不易维护和判断,状态越多,情况越复杂,代码会显得冗长难以维护.

像这种,独立状态(标记)之间相互组合可以产生新的状态(标记),且每个独立状态(标记)只有true或者false值的,我们可以使用位域的概念来管理这些状态.

它的核心思想就是将, int 数值看做是 二进制数位表示.如果有四个状态就可以像这样 0000,用四位二进制表示,每一个二进制位都可以表示一种状态. 然后通过 位运算,来提取或添加标记位.四位对应的组合状态有16个. 而我们,只需要通过一个int变量就能够管理这些状态.

当参与的状态(标记)越多时,如果使用单独的标记变量,就需要生成越多的变量,而用位域,这种独立状态为不管有多少个,都可以用一个变量表示.int类型最多存放32个独立状态

下面我们来看具体实现.(简单的模仿Gravity类的一部分功能)

public class Gravity {
    // 二进制表示 0001
    public static final int LEFT              = 1;
    // 二进制表示 0010
    public static final int RIGHT             = LEFT << 1;
    // 二进制表示 0100
    public static final int TOP               = LEFT << 2;
    // 二进制表示 1000
    public static final int BOTTOM            = LEFT << 3;
    // 水平居中, 二进制表示 0011
    public static final int HORIZONTAL_CENTER = LEFT | RIGHT;
    // 垂直居中, 二进制表示 1100
    public static final int VERTICAL_CENTER   = TOP | BOTTOM;
    // 居中, 二进制表示 1111
    public static final int CENTER            = HORIZONTAL_CENTER | VERTICAL_CENTER;
    // 默认左上角, 二进制表示 0101
    public static final int DEFAULT           = LEFT | TOP;

    // 存放标志位
    private int mFlags = DEFAULT;

    // 设置标记位,会清除原来的标记
    public void setFlags(int flags) {
        mFlags = flags;
    }

    // 添加标记位,在原来的基础上添加
    public void addFlags(int flags) {
        mFlags |= flags;
    }

    // 清除指定的标记
    public void clearFlags(int flags) {
        mFlags &= ~flags;
    }

    // 清除所有标记,设为默认
    public void clears() {
        mFlags = DEFAULT;
    }

    // 判断是否存在指定的标记
    public boolean hasFlags(int flags) {
        return (mFlags & flags) == flags;
    }

    // 判断是否 只有指定的标记
    public boolean onlyFlags(int flags) {
        return mFlags == flags;
    }

    public void apply() {
        String des = "左上角";
        if (hasFlags(CENTER)) {
            des = "整体居中";
        } else if (hasFlags(HORIZONTAL_CENTER)) {
            if (hasFlags(BOTTOM)) des = "水平居中,竖直向下";
            else des = "水平居中,竖直向下";
        } else if (hasFlags(VERTICAL_CENTER)) {
            if (hasFlags(RIGHT)) des = "竖直居中,水平向右";
            else des = "竖直居中,水平向左";
        } else if (hasFlags(LEFT | BOTTOM)) {
            des = "左下角";
        } else if (hasFlags(RIGHT | TOP)) {
            des = "右上角";
        } else if (hasFlags(RIGHT | BOTTOM)) {
            des = "右下角";
        }
        System.out.println("你选择的布局是 : " + des);
    }
}

具体的调用实现 :


public class Main {
    public static void main(String[] args) {
        Gravity gravity = new Gravity();

        // 设置为 右下角
        gravity.setFlags(Gravity.BOTTOM | Gravity.RIGHT);
        gravity.apply();

        // 添加 left后,变为 水平居中,竖直向下
        gravity.addFlags(Gravity.LEFT);
        gravity.apply();

        // 判断是否 水平居中, 返回为 true
        gravity.hasFlags(Gravity.HORIZONTAL_CENTER);

        // 添加top后,变为 整体居中了
        gravity.addFlags(Gravity.TOP);
        gravity.apply();

        // 删掉 bottom和left 之后,变为右上角了
        gravity.clearFlags(Gravity.BOTTOM | Gravity.LEFT);
        gravity.apply();
    }
}
// 结果
你选择的布局是 : 右下角
你选择的布局是 : 水平居中,竖直向下
你选择的布局是 : 整体居中
你选择的布局是 : 右上角
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容