快速設定面板上快捷開關的加載流程,包括圖標等的加載和點擊事件等的處理過程,以及創建一個快捷開關的主要過程(以增加一個鎖屏開關為例)。本文所討論的Android版本為5.1。
-
快捷開關的加載流程
資源模塊的加載
-
config.xml參數配置
config.xml是程序的一些配置信息。在創建快捷開關時需要按照參數配置文件config.xml來加載快捷開關信息。參考源碼QSTileHost.java的實現,主要過程:
(1) QSTileHost類在構造函數里調用recreateTiles() 方法;
(2) 在recreateTiles() 方法里loadTileSpecs();
(3) loadTileSpecs() 方法里通過mContext.getResources().getString(R.string.quick_settings _tiles_default) 從config.xml中加載快捷開關規 格”quick_settings_tiles_default” 這個配置項便確定了默認情況下快速設定面板上顯示哪些開關以及開關的顯示順序;
(4) 接下來創建快捷開關createTile() ,通過從config.xml文件中獲取的配置信息實例化每個快捷開關;
(5) 最后將創建的快捷開關保存在成員變量mTiles列表里,通過調用QSTileHost.getTiles() 可以獲取到所有的快捷開關。
-
字符和圖標的加載
字符資源保存在各個value文件夾下的String.xml文件內。圖標使用的是矢量圖片,保存在drawable文件夾下。字符和圖標的加載在每個快捷開關類文件的handleUpdateState() 方 法里進行。
每個快捷開關類都需要繼承QSTile<TState extends State>類(詳細的說明在2.3節討論)。這里的TState是快捷開關的狀態,會作為參數傳到handleUpdateState() 方法。在 handleUpdateState() 方法里將字符和圖標資源賦值給state.label和state.icon即可。
-
動畫的加載
動畫資源與圖標一起保存在drawable文件夾下,一般都是打開和關閉成對存在的。動畫的加載是通過在每個快捷開關類里實例化AnimationIcon類,資源id將作為參數,然后會在點 擊事件處理中調用。
PhoneStatusBar創建view
前面提到在QSTileHost.createTile() 方法里創建了快捷開關,並且可以通過調用getTiles() 方法得到所有的快捷開關,接下來看快捷開關加載到快速設定面板的實現。參考 PhoneStatusBar. makeStatusBarView() 方法的實現,主要完成的部分:
(1) 首先初始化快捷開關面板mQSPanel,mQSPanel判空后實例化QSTileHost保存在對象qsh上;
(2) 通過調用mQSPanel.setTiles(qsh.getTiles()) 將每個快捷開關添加到mQSPanel上。另外,在配置有變化時,在PhoneStatusBar.updateResources() 方法里通過調用 mQSPanel. updateResources() 更新快捷開關面板;在解鎖屏時,在PhoneStatusBar. hideKeyguard () 方法里通過調用mQSPanel. refreshAllTiles () 刷新快捷開關面板。
用戶交互的處理
對用戶動作的監聽響應是在每個快捷開關類里處理。
前面提到,每個快捷開關類都需要繼承QSTile<TState extends State>類。
QSTile<TState extends State> implements Listenable是快捷開關的基類,通過繼承它來創建一個快捷開關。需要實現兩個抽象方法handleClick() 和handleUpdateState() 。所以可以看出QSTile主要負責點擊事件的處理和快捷開關狀態的管理。消息的傳遞通過Handler機制完成。
(1) 動作的監聽響應:
1) 在handleClick() 方法里處理點擊事件;打開/關閉的動畫也在這里調用;
2) 部分開關需要重寫handleSecondaryClick() 方法,例如Wi-Fi和藍牙開關,在handleSecondaryClick() 方法里打開詳情頁面;
3) 還有開關需要重寫handleLongClick() ,例如反色和熱點開關,在這里打開詢問是否要隱藏的對話框;
4) 在setListening() 方法里添加回調監聽,接受action和注冊廣播等。
(2) 狀態的管理:
1) 狀態管理通過一個由Host提供的looper來進行。每個快捷開關在handleUpdateState()中更新狀態。回調影響狀態要通過快捷開關的工作looper調用refreshState() 來觸 發另一個狀態更新。
2) 狀態類有三種,State類以及繼承自State類的BooleanState類和SignalState類。需要判斷開關與否的狀態的快捷開關繼承QSTile<QSTile.BooleanState>,包括飛行 模式、反色、手電筒、熱點、定位、自動旋轉、藍牙和屏幕投射開關等;還需要判斷連接等狀態的快捷開關繼承QSTile<QSTile.SignalState>,例如Wi-Fi和移動數據網 絡開關;其他直接繼承QSTile<QSTile.State>。
(3) 另外,Wi-Fi和藍牙開關需要重寫supportsDualTargets() 方法和getDetailAdapter() 方法。因為這兩個開關是繪制在一排兩個開關的布局上而且需要顯示詳情頁面。
-
創建一個快捷開關
以增加一個鎖屏的快捷開關為例。
資源模塊的增加
通過2.1節的討論可以知道,需要修改config.xml文件、String.xml文件和添加矢量圖xml文件或添加一張png圖片。鎖屏開關點擊時不需要動畫,因此不添加動畫xml文件
(1) 在\frameworks\base\packages\SystemUI\res\values\config.xml里找到 "quick_settings_ tiles_default",添加lockscreen,用“,”隔開:
<string name="quick_settings_tiles_default" translatable="false">
wifi,bt,inversion,cell,airplane,rotation,flashlight,location,cast,hotspot,lockscreen
</string>
(2) 在\frameworks\base\packages\SystemUI\res\values\string.xml里添加:
<string name="quick_settings_lockscreen_label">
"lockscreen"
</string>
(3) 在\frameworks\base\packages\SystemUI\res\values values-zh-rCN\string.xml里添加:
<string name="quick_settings_lockscreen_label">"鎖屏"</string>
其他語言在相應的values文件夾下對應的string.xml文件里添加。
(4) 在\frameworks\base\packages\SystemUI\res\drawable-hdpi文件夾里添加圖片ic_qs_locks- creen.png,也可以在drawable文件夾下添加矢量圖xml文件;
創建LockScreenTile類
在/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles目錄下創建LockScreenTile.java。
(1) 新建鎖屏快捷開關類LockScreenTile extends QSTile<QSTile.BooleanState>,繼承QSTile類;
(2) newTileState() 方法直接返回一個BooleanState的實例;
@Override
protected BooleanState newTileState() {
return new BooleanState();
}
(3) handleClick() 方法里響應點擊事件進行鎖屏,調用PowerManager.goToSleep() 方法;
@Override
protected void handleClick() {
setEnabled(!mState.value);
}
private void setEnabled(boolean enable) {
if (enable) {
final PowerManager pmr = (PowerManager) mContext
.getSystemService(Context.POWER_SERVICE);
pmr.goToSleep(SystemClock.uptimeMillis());
}
}
(4) handleUpdateState() 方法里設置鎖屏開關的狀態,包括圖標和字符;
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
state.icon = ResourceIcon.get(R.drawable.ic_qs_lockscreen);
state.visible = true;
state.label = mContext.getString(R.string.quick_settings_lockscreen_label);
}
(5) 實例化LockScreenTile,在QSTileHost. createTile() 方法里添加:
private QSTile<?> createTile(String tileSpec) {
if (tileSpec.equals("wifi"))
return new WifiTile(this);
else if (tileSpec.equals("bt"))
return new BluetoothTile(this);
……
else if (tileSpec.equals("lockscreen"))
return new LockScreenTile(this);
else if (tileSpec.startsWith(IntentTile.PREFIX))
return IntentTile.create(this, tileSpec);
else
throw new IllegalArgumentException("Bad tile spec: "+ tileSpec);
}