android 【安卓13-Framework】SystemUI定制之屏蔽下拉狀態(tài)欄部分快捷按鈕
1、需求
屏蔽下拉狀態(tài)欄谷歌錄屏、省電模式、二維碼掃描器等快捷按鈕。
2、修改路徑
普及:安卓的SystemUI包提供了狀態(tài)欄、導(dǎo)航欄、通知中心等重要的用戶界面元素。 狀態(tài)欄小部件UI顯示修改路徑:frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
3、修改思路
下拉狀態(tài)欄屬于系統(tǒng)UI的一部分,位于SystemUI包下,不同安卓版本這個(gè)包的代碼有些許出入,但是萬(wàn)變不離其宗,掌握修改原理即可,下面來(lái)一步步分析如何修改以及為什么要這樣修改:
(1)修改方法
打開(kāi)這個(gè)文件:frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java,定位到這個(gè)方法: 這個(gè)方法做了兩件事,第一件事是加載“存貨”(Stock tiles),什么是存貨呢,就是安卓原生自帶的小部件,比如WiFi、藍(lán)牙、定位等快捷按鈕,第二件事是加載工程師自定義的小部件(Custom tiles),在這里我們先不研究自定義的小部件,以后有時(shí)間再研究。 既然是加載“存貨”,那這些存貨從哪里來(lái)呢,先不看這個(gè),我先把修改代碼放出來(lái),若不想研究原理可不看后面,修改代碼如下:
@Nullable
protected QSTileImpl createTileInternal(String tileSpec) {
// Stock tiles.
if (mTileMap.containsKey(tileSpec)
// We should not return a Garbage Monitory Tile if the build is not Debuggable
&& (!tileSpec.equals(GarbageMonitor.MemoryTile.TILE_SPEC) || Build.IS_DEBUGGABLE)) {
//屏蔽下拉狀態(tài)欄中的幾個(gè)tile,返回空對(duì)象
Set
multipleStrings.add("screenrecord"); //谷歌錄屏
multipleStrings.add("battery"); // 省電模式
multipleStrings.add("qr_code_scanner"); // 二維碼掃描器
if (multipleStrings.contains(tileSpec)) {
return null;
}
return mTileMap.get(tileSpec).get();
}
// Custom tiles
if (tileSpec.startsWith(CustomTile.PREFIX)) {
return CustomTile.create(
mCustomTileBuilderProvider.get(), tileSpec, mQsHostLazy.get().getUserContext());
}
// Broken tiles.
Log.w(TAG, "No stock tile spec: " + tileSpec);
return null;
}
好了,結(jié)束!
nonono,開(kāi)個(gè)玩笑,接下來(lái)講解原理,因?yàn)橛行┌姹静皇沁@樣改的,待會(huì)就知道了。
(2)修改原理
首先我們知道,谷歌的代碼寫的都是很優(yōu)雅的,想完全看懂不容易,這里我們只啃一部分,那就是下拉狀態(tài)欄那些快捷按鈕(QS tile)是如何添加的。 首先我們觀察SystemUI目錄,里面有很多子目錄,大部分都是每一個(gè)模塊細(xì)化出來(lái),比如WiFi相關(guān)UI放在一個(gè)目錄,聲音UI放一個(gè)目錄,目錄里面基本都是寫小模塊的具體實(shí)現(xiàn)。
而qs目錄就是專門負(fù)責(zé)實(shí)現(xiàn)通知欄或狀態(tài)欄這個(gè)面板的各種功能和交互效果,比如圖標(biāo)的顯示、動(dòng)畫效果、點(diǎn)擊事件的處理等。 這里我們主要看qs目錄,該目錄結(jié)構(gòu)如圖: 其中,QSContainerImpl.java 這個(gè)類作為下拉狀態(tài)欄快速面板的布局承載各種View,QSTileHost.java類是個(gè)接口實(shí)現(xiàn)類,里面實(shí)現(xiàn)了對(duì)tile的增刪改查操作: 例如,我們想移除谷歌相機(jī),同樣可以在布局初始化處調(diào)用removeTile方法:
@Override
public void removeTile(String spec) {
if (spec.startsWith(CustomTile.PREFIX)) {
// If the tile is removed (due to it not actually existing), mark it as removed. That
// way it will be marked as newly added if it appears in the future.
setTileAdded(CustomTile.getComponentFromSpec(spec), mCurrentUser, false);
}
mMainExecutor.execute(() -> changeTileSpecs(tileSpecs-> tileSpecs.remove(spec)));
}
傳入一個(gè)字符串“screenrecord”,關(guān)于怎么知道各個(gè)組件的鍵值,后面會(huì)講。 注意,調(diào)用這個(gè)方法只能移除原生自帶的一些組件鍵值,addTile(String spec) 這個(gè)方法也是只能創(chuàng)建自帶的組件。 接下來(lái)回到上面修改的實(shí)現(xiàn)類QSFactoryImpl.java里面看它是如何移除谷歌相機(jī)的,首先,這個(gè)mTileMap是一個(gè)map 集合,里面加載了所有的谷歌原生tile鍵值 我們看下這個(gè)map 是從哪里傳遞過(guò)來(lái)的, 看到是通過(guò)構(gòu)造方法傳遞,繼續(xù)找哪里調(diào)用這個(gè)構(gòu)造方法 在這個(gè)類里面,我們看到tileMap在這里傳遞了進(jìn)來(lái),看一下tileMap的結(jié)構(gòu)
val tileMap = mutableMapOf
"internet" to Provider { internetTile },
"bt" to Provider { bluetoothTile },
"dnd" to Provider { dndTile },
"inversion" to Provider { colorInversionTile },
"airplane" to Provider { airplaneTile },
"work" to Provider { workTile },
"rotation" to Provider { rotationTile },
"flashlight" to Provider { flashlightTile },
"location" to Provider { locationTile },
"cast" to Provider { castTile },
"hotspot" to Provider { hotspotTile },
"battery" to Provider { batterySaverTile },
"saver" to Provider { dataSaverTile },
"night" to Provider { nightDisplayTile },
"nfc" to Provider { nfcTile },
"dark" to Provider { darkModeTile },
"screenrecord" to Provider { screenRecordTile },
"reduce_brightness" to Provider { reduceBrightColorsTile },
"cameratoggle" to Provider { cameraToggleTile },
"mictoggle" to Provider { microphoneToggleTile },
"controls" to Provider { deviceControlsTile },
"alarm" to Provider { alarmTile },
"wallet" to Provider { quickAccessWalletTile },
"qr_code_scanner" to Provider { qrCodeScannerTile },
"onehanded" to Provider { oneHandedModeTile },
"color_correction" to Provider { colorCorrectionTile },
"dream" to Provider { dreamTile },
"font_scaling" to Provider { fontScalingTile }
)
這段代碼是 Kotlin 語(yǔ)言編寫的,它定義了一個(gè)名為 tileMap 的可變映射(MutableMap)。這個(gè)映射的鍵是字符串類型(String),而值是 Provider
明白了創(chuàng)建原理,改起來(lái)就很簡(jiǎn)單了,比如在安卓其他版本,這個(gè)實(shí)現(xiàn)類并不是這樣寫的 這里這個(gè)實(shí)現(xiàn)類是直接在當(dāng)前類根據(jù)tile標(biāo)識(shí)返回對(duì)應(yīng)的實(shí)體
@Nullable
protected QSTileImpl createTileInternal(String tileSpec) {
// Stock tiles.
switch (tileSpec) {
case "wifi":
return mWifiTileProvider.get();
case "internet":
return mInternetTileProvider.get();
case "bt":
return mBluetoothTileProvider.get();
/*case "cell":
return mCellularTileProvider.get();*/
case "dnd":
return mDndTileProvider.get();
case "inversion":
return mColorInversionTileProvider.get();
case "airplane":
return mAirplaneModeTileProvider.get();
/*case "work":
return mWorkModeTileProvider.get();
case "rotation":
return mRotationLockTileProvider.get();
case "flashlight":
return mFlashlightTileProvider.get();
case "location":
return mLocationTileProvider.get();*/
case "cast":
return mCastTileProvider.get();
case "hotspot":
return mHotspotTileProvider.get();
/*case "battery":
return mBatterySaverTileProvider.get();
case "saver":
return mDataSaverTileProvider.get();
case "night":
return mNightDisplayTileProvider.get();
case "nfc":
return mNfcTileProvider.get();
case "dark":
return mUiModeNightTileProvider.get();*/
/*case "screenrecord":
return mScreenRecordTileProvider.get();*/
/*case "reduce_brightness":
return mReduceBrightColorsTileProvider.get();
case "cameratoggle":
return mCameraToggleTileProvider.get();
case "mictoggle":
return mMicrophoneToggleTileProvider.get();*/
case "controls":
return mDeviceControlsTileProvider.get();
/*case "alarm":
return mAlarmTileProvider.get();
case "wallet":
return mQuickAccessWalletTileProvider.get();
case "qr_code_scanner":
return mQRCodeScannerTileProvider.get();
case "onehanded":
return mOneHandedModeTileProvider.get();*/
case "color_correction":
return mColorCorrectionTileProvider.get();
/*case "dream":
return mDreamTileProvider.get();*/
}
// Custom tiles
if (tileSpec.startsWith(CustomTile.PREFIX)) {
return CustomTile.create(
mCustomTileBuilderProvider.get(), tileSpec, mQsHostLazy.get().getUserContext());
}
// Debug tiles.
/*if (Build.IS_DEBUGGABLE) {
if (tileSpec.equals(GarbageMonitor.MemoryTile.TILE_SPEC)) {
return mMemoryTileProvider.get();
}
}*/
// Broken tiles.
Log.w(TAG, "No stock tile spec: " + tileSpec);
return null;
}
這種也很簡(jiǎn)單,直接注釋掉不要的tile即可,這個(gè)版本的實(shí)現(xiàn)類寫的稍微就沒(méi)那么優(yōu)雅了,但是基本原理都一樣。 好了,先寫到這里。
3、總結(jié)
系統(tǒng)下拉狀態(tài)欄包含的內(nèi)容還是很多的,目前只分析了如何移除快捷按鈕,繼續(xù)刨析還可以實(shí)現(xiàn)增加自定義的按鈕,不過(guò)得遵循谷歌的一套做法,再深入了解還可以美化狀態(tài)欄,這些就留給以后去慢慢分析了。
共勉:分享知識(shí),共同進(jìn)步!
好文推薦
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。