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();
}
}
// 結果
你選擇的佈局是 : 右下角
你選擇的佈局是 : 水平居中,豎直向下
你選擇的佈局是 : 整體居中
你選擇的佈局是 : 右上角
相關文章
- 為什麼Android原始碼中都使用16進位制進行狀態管理?Android原始碼
- Java stream sorted使用 Comparator 進行多欄位排序Java排序
- Java多執行緒-執行緒狀態Java執行緒
- vue狀態管理演進Vue
- postgresql中資料表如何透過一個欄位標識資料行多種狀態?SQL
- 如何使用lerna進行多包(package)管理Package
- Android 頁面多狀態佈局管理Android
- Java執行緒的狀態Java執行緒
- Java 位域Java
- 狀態管理
- 基於 Redux + Redux Persist 進行狀態管理的 Flutter 應用示例ReduxFlutter
- Memcached 多執行緒和狀態機執行緒
- flutter狀態管理provider的基本使用FlutterIDE
- Flutter狀態管理Provider(四) Selector使用FlutterIDE
- 使用 useState 管理響應式狀態
- Java執行緒狀態轉換Java執行緒
- vuex狀態管理知識點記錄Vue
- java執行緒的五大狀態,阻塞狀態詳解Java執行緒
- 位運算-設計資料庫表的多選狀態欄位資料庫
- 前端狀態管理與有限狀態機前端
- Flutter狀態管理之Provider的理解使用FlutterIDE
- 使用 Provider 管理 Flutter 應用狀態 (下)IDEFlutter
- 使用 Provider 管理 Flutter 應用狀態 (上)IDEFlutter
- Flutter狀態管理Provider(一) 簡易使用FlutterIDE
- [譯]Flutter - 使用Provider實現狀態管理FlutterIDE
- 【Java】執行緒的 6 種狀態Java執行緒
- Java執行緒狀態及切換Java執行緒
- Java 執行緒的5種狀態Java執行緒
- java執行緒的狀態+鎖分析Java執行緒
- Java執行緒狀態及同步鎖Java執行緒
- Vuex狀態管理Vue
- Flutter | 狀態管理Flutter
- mysql slave 跟進 master 的關鍵狀態指標MySqlAST指標
- React 狀態管理:狀態與生命週期React
- 逆向分析:利用標誌位進行爆破破解
- 進階Java多執行緒Java執行緒
- 深入理解Java多執行緒與併發框(第①篇)——執行緒的狀態Java執行緒
- Pytorch:使用Tensorboard記錄訓練狀態PyTorchORB