在上一篇(https://blog.csdn.net/u011164827/article/details/102998091)分析到SystemUI的啓動過程,現在分析StatusBar。
啓動分析
SystemUI在SystemUIApplication會啓動各個模塊,在這個地方會調用com.android.systemui.SystemBars的start方法。
frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java
@Override
40 public void start() {
41 if (DEBUG) Log.d(TAG, "start");
42 createStatusBarFromConfig();
43 }
private void createStatusBarFromConfig() {
53 if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
//取出className
54 final String clsName = mContext.getString(R.string.config_statusBarComponent);
55 if (clsName == null || clsName.length() == 0) {
56 throw andLog("No status bar component configured", null);
57 }
// 通過反射獲取該對象
58 Class<?> cls = null;
59 try {
60 cls = mContext.getClassLoader().loadClass(clsName);
61 } catch (Throwable t) {
62 throw andLog("Error loading status bar component: " + clsName, t);
63 }
64 try {
65 mStatusBar = (SystemUI) cls.newInstance();
66 } catch (Throwable t) {
67 throw andLog("Error creating status bar component: " + clsName, t);
68 }
//填充信息並啓動 StatusBar start() 方法
69 mStatusBar.mContext = mContext;
70 mStatusBar.mComponents = mComponents;
71 mStatusBar.start();
72 if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
73 }
這裏面的代碼不多,主要是通過反射獲取config_statusBarComponent
中定義的對象,並啓動該對象的start方法。config_statusBarComponent
的值有3種,默認是phone佈局,另外兩個是tv和car。
44 <!-- Component to be used as the status bar service. Must implement the IStatusBar
45 interface. This name is in the ComponentName flattened format (package/class) -->
46 <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
我們這裏分析frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
public void start() {
//由於狀態欄的窗口不屬於任何一個Activity,所以需要使用WindowManager窗口創建
656 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
657
658 mDisplay = mWindowManager.getDefaultDisplay();
659 updateDisplaySize();
660
661 Resources res = mContext.getResources();
662 mVibrateOnOpening = mContext.getResources().getBoolean(
663 R.bool.config_vibrateOnIconAnimation);
664 mVibratorHelper = Dependency.get(VibratorHelper.class);
665 mScrimSrcModeEnabled = res.getBoolean(R.bool.config_status_bar_scrim_behind_use_src);
666 mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
667
668 DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER));
669 putComponent(StatusBar.class, this);
670
671 // start old BaseStatusBar.start().
//狀態欄的存在對窗口布局有重要的影響,因此狀態欄中所發生的變化有必要通知給WMS
672 mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
673 mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
674 Context.DEVICE_POLICY_SERVICE);
675
676 mAccessibilityManager = (AccessibilityManager)
677 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
678
679 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
680
681 mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
682
683 mBarService = IStatusBarService.Stub.asInterface(
684 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
685
686 mRecents = getComponent(Recents.class);
687
688 mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
689 mLockPatternUtils = new LockPatternUtils(mContext);
690
691 mMediaManager.setUpWithPresenter(this, mEntryManager);
692
693 // Connect in to the status bar manager service
//mCommandQueue是CommandQueue類的一個實例。CommandQueue繼承自IStatusBar.Stub。因此它是IStatusBar的Bn端。在完成註冊後,這一Binder對象的Bp端將會保存在
//IStatusBarService中,因此它是IStatusBarService與BaseStatusBar進行通信的橋樑。
694 mCommandQueue = getComponent(CommandQueue.class);
695 mCommandQueue.addCallbacks(this);
696 /*
*switches存儲了一些雜項:禁用功能列表,SystemUIVisiblity,是否在導航欄中顯示虛擬的菜單鍵,輸入法窗口是否可見,輸入法是否消費BACK鍵,是否接入了實體鍵盤
*實體鍵盤是否被啓用
*/
697 int[] switches = new int[9];
698 ArrayList<IBinder> binders = new ArrayList<>();
699 ArrayList<String> iconSlots = new ArrayList<>();
700 ArrayList<StatusBarIcon> icons = new ArrayList<>();
701 Rect fullscreenStackBounds = new Rect();
702 Rect dockedStackBounds = new Rect();
703 try {
//向IStatusBarServie進行註冊,並獲取所有保存在IStatusBarService中的信息副本
704 mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
705 fullscreenStackBounds, dockedStackBounds);
706 } catch (RemoteException ex) {
707 // If the system process isn't there we're doomed anyway.
708 }
709 //創建狀態欄與導航欄的窗口
710 createAndAddWindows();
711
712 // Make sure we always have the most current wallpaper info.
713 IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
714 mContext.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter);
715 mWallpaperChangedReceiver.onReceive(mContext, null);
716
717 mLockscreenUserManager.setUpWithPresenter(this, mEntryManager);
718 mCommandQueue.disable(switches[0], switches[6], false /* animate */); //禁用某些功能
719 setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
720 fullscreenStackBounds, dockedStackBounds); //設置SystemUIVisibilty
721 topAppWindowChanged(switches[2] != 0); //設置菜單鍵的可見性
722 // StatusBarManagerService has a back up of IME token and it's restored here.
723 setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0); //根據輸入法窗口的可見性調整導航欄的樣式
724
725 // Set up the initial icon state
//依次向系統狀態去添加狀態圖標
726 int N = iconSlots.size();
727 for (int i=0; i < N; i++) {
728 mCommandQueue.setIcon(iconSlots.get(i), icons.get(i));
729 }
730
731 // Set up the initial notification state.
//初始化通知欄
732 mNotificationListener.setUpWithPresenter(this, mEntryManager);
733
734 if (DEBUG) {
735 Log.d(TAG, String.format(
736 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
737 icons.size(),
738 switches[0],
739 switches[1],
740 switches[2],
741 switches[3]
742 ));
743 }
744
745 setHeadsUpUser(mLockscreenUserManager.getCurrentUserId());
746
747 IntentFilter internalFilter = new IntentFilter();
748 internalFilter.addAction(BANNER_ACTION_CANCEL);
749 internalFilter.addAction(BANNER_ACTION_SETUP);
750 mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF,
751 null);
752
753 IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
754 Context.VR_SERVICE));
755 try {
756 vrManager.registerListener(mVrStateCallbacks);
757 } catch (RemoteException e) {
758 Slog.e(TAG, "Failed to register VR mode state listener: " + e);
759 }
760
761 IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
762 ServiceManager.getService(Context.WALLPAPER_SERVICE));
763 try {
764 wallpaperManager.setInAmbientMode(false /* ambientMode */, false /* animated */);
765 } catch (RemoteException e) {
766 // Just pass, nothing critical.
767 }
768
769 // end old BaseStatusBar.start().
770
771 // Lastly, call to the icon policy to install/update all the icons.
//最後,調用圖標策略以安裝/更新所有圖標
772 mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
773 mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
774
775 mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
776 mUnlockMethodCache.addListener(this);
777 startKeyguard();
778
779 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
780 putComponent(DozeHost.class, mDozeServiceHost);
781
782 mScreenPinningRequest = new ScreenPinningRequest(mContext);
783 mFalsingManager = FalsingManager.getInstance(mContext);
784
785 Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this);
786
787 Dependency.get(ConfigurationController.class).addCallback(this);
788 }
StatusBar.java中有大量的代碼。
mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE));
這一行獲取IStatusBarService的實例。IStatusBarService是一個系統服務,由ServerThread啓動並常駐system_seerver進程中。IStatusBarService爲那些對狀態欄感興趣的其它系統服務定義了一系列API,然而對SystemUI而言,它更像一個客戶端。因爲IStatusBarService會將操作狀態欄的請求發給SystemUI,並由後者完成請求。
這裏主要看一下createAndAddWindows方法
狀態欄的佈局
狀態欄需要顯示的信息分爲以下5種:
- 通知信息:在狀態欄左側顯示一個圖標提醒用戶,並在下拉捲簾中爲用戶顯示更加詳細的信息。
- 時間信息:顯示在狀態欄最右側的一般小型數字時鐘,是一個名爲Clock的繼承自TextView的控件。監聽了幾個和時間有關的廣播:ACTION_TIME_TICK、ACTION_TIME_CHANGED、ACTION_TIMEZONE_CHANGED、ACTION_CONFIGURAITON_CHANGED。當其中一個廣播到來時從Calendar類中獲取當前的系統時間,然後進行字符串格式化後顯示出來。時間信息的維護工作在狀態欄內部完成,外界無法通過API修改時間信息的顯示或行爲。
- 電量信息:顯示一個電池圖標,用於提示設備當前的電量情況。它是一個被BatteryController類所管理的ImageView。BatteryController通過監聽android.intent.action.ABTTERY_CHANGED廣播以從BatteryService中獲取電量信息。和時間信息一樣,外界無法干預狀態欄對電量信息的顯示行爲。
- 信號信息:顯示系統當前的WiFi、移動信號網絡的信號狀態。用戶所看到的WiFi圖標、手機信號圖標、飛行模式圖標都屬於信號信息的範圍。它們被NetworkController類維護着。NetworkController監聽了一系列與信號相關的廣播,如WIFI_STATE_CHANGED_ACTION 、WIFI_STATE_SIM_ACTION、ACTION_AIRPLANE_MODE_CHANGED等,並在這些廣播到來時顯示、更改或移除相關的圖標。注意,在Android 8.0之後,手機信號圖標不在通過ImageView而是通過自定義view實現。
系統狀態圖標區:這個區域用來顯示系統當前的狀態,比如可以展示藍牙狀態、鬧鈴等。StatusBarManagerService通過setIcon()接口爲外界提供了修改系統狀態圖標的途徑,但是它對信息的內容有很強的限制。1、系統狀態圖標無法顯示圖標以外的信息;2、系統狀態圖標對其顯示的圖標數量以及圖標鎖表示的意圖有嚴格的限制。
我們繼續分析createAndAddWindows
public void createAndAddWindows() {
addStatusBarWindow();
}
private void addStatusBarWindow() {
//創建狀態欄的控件樹
makeStatusBarView();
mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
mRemoteInputController = new RemoteInputController(mHeadsUpManager);
//通過StatusBarWindowManager.add創建狀態欄的窗口
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
/*
*獲取statusbar高度,在framework/base/core/res/res/values/diamens.xml中設置
*/
public int getStatusBarHeight() {
if (mNaturalBarHeight < 0) {
final Resources res = mContext.getResources();
mNaturalBarHeight =
res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
}
return mNaturalBarHeight;
}
// ================================================================================
// Constructing the view
// ================================================================================
protected void makeStatusBarView() {
final Context context = mContext;
//獲取屏幕參數
updateDisplaySize(); // populates mDisplayMetrics
//更新Panels資源數據,statusbar包含很多panel,在創建PhoneStatusBarView時需要更新panel數據
updateResources();
updateTheme();
inflateStatusBarWindow(context); //加載佈局
mStatusBarWindow.setService(this);
mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener()); //mStatusBarWindow的點擊事件
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController);
FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setBouncerShowing(mBouncerShowing);
setAreThereNotifications();
checkBarModes();
}).getFragmentManager()
.beginTransaction()
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG) //替換爲CollapsedStatusBarFragment
.commit();
mIconController = Dependency.get(StatusBarIconController.class);
}
/*
*加載佈局
*/
protected void inflateStatusBarWindow(Context context) {
mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
R.layout.super_status_bar, null);
}
調用流程是createAndAddWindows——>addStatusBarWindow——>makeStatusBarView
/**
87 * Adds the status bar view to the window manager.
88 *
89 * @param statusBarView The view to add.
90 * @param barHeight The height of the status bar in collapsed state.
91 */
92 public void add(View statusBarView, int barHeight) {
93
94 // Now that the status bar window encompasses the sliding panel and its
95 // translucent backdrop, the entire thing is made TRANSLUCENT and is
96 // hardware-accelerated.
97 mLp = new WindowManager.LayoutParams(
98 ViewGroup.LayoutParams.MATCH_PARENT,
99 barHeight,
100 WindowManager.LayoutParams.TYPE_STATUS_BAR,
101 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
102 | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
103 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
104 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
105 | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
106 PixelFormat.TRANSLUCENT);
107 mLp.token = new Binder();
108 mLp.gravity = Gravity.TOP;
109 mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
110 mLp.setTitle("StatusBar");
111 mLp.packageName = mContext.getPackageName();
112 mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
113 mStatusBarView = statusBarView;
114 mBarHeight = barHeight;
115 mWindowManager.addView(mStatusBarView, mLp);
116 mLpChanged = new WindowManager.LayoutParams();
117 mLpChanged.copyFrom(mLp);
118 }
狀態欄的高度是從frameworks/base/core/res/res/values/dimens.xml中獲取的,默認爲25dp。TYPE_STATUS_BAR使得PhomeWindowManager
爲狀態欄的窗口分配了較大的layer值,使其可以顯示在其它應用窗口上。FLAG_NOT_FOCUSABLE、FLAG_TOUCHABLE_WHEN_WAKING、FLAG_SPLIT_TOUCH
定義了輸入事件的響應行爲。另外當窗口創建後LayoutParams是會反生變化的。狀態欄窗口創建時高度爲25dip,flags描述爲其不可接受按鍵事件。不過當用戶
按下狀態欄導致捲簾下拉時,StatusBar會通過WindowManager.updateViewLayout()方法修改窗口的LayoutParams高度爲match_parent,即充滿整個屏幕使得捲簾
可以滿屏顯示,並且移除FLAG_NOT_FOCUSABLE,使得StatusBar可以監聽back按鈕
在inflateStatusBarWindow會初始化佈局
<!-- This is the combined status bar / notification panel window. -->
<-- StatusBarWindowView 繼承於FrameLayout -->
<com.android.systemui.statusbar.phone.StatusBarWindowView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<com.android.systemui.statusbar.BackDropView
android:id="@+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
sysui:ignoreRightInset="true"
>
<ImageView android:id="@+id/backdrop_back"
android:layout_width="match_parent"
android:scaleType="centerCrop"
android:layout_height="match_parent" />
<ImageView android:id="@+id/backdrop_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:visibility="invisible" />
</com.android.systemui.statusbar.BackDropView>
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_behind"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
<com.android.systemui.statusbar.AlphaOptimizedView
android:id="@+id/heads_up_scrim"
android:layout_width="match_parent"
android:layout_height="@dimen/heads_up_scrim_height"
android:background="@drawable/heads_up_scrim"
sysui:ignoreRightInset="true"
android:importantForAccessibility="no"/>
<FrameLayout
android:id="@+id/status_bar_container" //頂部狀態欄
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include layout="@layout/brightness_mirror" /> //亮度調節
<ViewStub android:id="@+id/fullscreen_user_switcher_stub"
android:layout="@layout/car_fullscreen_user_switcher"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include layout="@layout/status_bar_expanded" //QuickSettings和Notification欄
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
</com.android.systemui.statusbar.phone.StatusBarWindowView>
在super_status_bar.xml文件中主要定義了三個佈局頂部狀態欄、亮度調節、QuickSettings和Notification。繼續分析頂部狀態欄,頂部狀態欄是一個Fragment,佈局文件是SystemUI/res/layout/status_bar.xml
<com.android.systemui.statusbar.phone.PhoneStatusBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_height"
android:id="@+id/status_bar"
android:background="@drawable/system_bar_background"
android:orientation="vertical"
android:focusable="false"
android:descendantFocusability="afterDescendants"
>
<!--notification_lights_out 一般情況下是不可見,在SystemUIVisiblity中有一個名爲SYSTEM_UI_FLAG_LOW_PROFLE的標記
當一個應用程序希望讓客戶注意力更對集中在它所顯示的內容時,可以在其SystemUIVisiblity中添加這一標記,SYSTEM_UI_FLAG_LOW_PROFLE會使
得狀態欄與導航欄進入低識別度模式。低識別度模式下的狀態欄將不會顯示任何信息,只是在黑色背景中顯示一個灰色圓點,這個圓點即這個-->
<ImageView
android:id="@+id/notification_lights_out"
android:layout_width="@dimen/status_bar_icon_size"
android:layout_height="match_parent"
android:paddingStart="6dip"
android:paddingBottom="2dip"
android:src="@drawable/ic_sysbar_lights_out_dot_small"
android:scaleType="center"
android:visibility="gone"
/>
<!-- status_bar_contents顯示頂部狀態欄的各種信息 -->
<LinearLayout android:id="@+id/status_bar_contents"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="6dp"
android:paddingEnd="8dp"
android:orientation="horizontal"
>
<!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
<!--消息通知 -->
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/notification_icon_area"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal" />
<!--system_icon_area 繼承一個LinearLayout 除消息通知以外的信息 -->
<com.android.keyguard.AlphaOptimizedLinearLayout
android:id="@+id/system_icon_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
>
<!--顯示wifi SIM卡 電量等 -->
<include layout="@layout/system_icons" />
!--clock 顯示時間信息 -->
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:singleLine="true"
android:paddingStart="@dimen/status_bar_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_clock_end_padding"
android:gravity="center_vertical|start"
/>
</com.android.keyguard.AlphaOptimizedLinearLayout>
</LinearLayout>
<ViewStub
android:id="@+id/emergency_cryptkeeper_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout="@layout/emergency_cryptkeeper_text"
/>
</com.android.systemui.statusbar.phone.PhoneStatusBarView>
@+id/system_icon_area的寬度定義爲ware_content,而@+id/notification_icon_area的weight被設置爲1.在這種情況下system_icon_area將在狀態欄右側根據其所顯示的圖標個數調整其尺寸。而notification_icon_area則會佔用狀態欄左側的剩餘空間。這說明一個問題:系統圖標區將優先佔用狀態欄的控件進行信息的顯示。
面又嵌套了一個SystemUI/res/layout/system_icons.xml佈局,system_icons裏面包含電量、系統狀態、信號等圖標。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/system_icons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical">
<!--系統狀態圖標 -->
<com.android.keyguard.AlphaOptimizedLinearLayout
android:id="@+id/statusIcons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal"/>
<!--信號圖標區域 -->
<include layout="@layout/signal_cluster_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/signal_cluster_margin_start"/>
<!--顯示電量信息 -->
<com.android.systemui.BatteryMeterView
android:id="@+id/battery"
android:layout_height="match_parent"
android:layout_width="wrap_content"
/>
</LinearLayout>
信號圖標顯示在signal_cluster_view.xml文件中,這裏就不繼續深入分析,等到分析信號時再做研究。
現在看一下狀態欄的控件樹結構
SystemUI常見問題修改
1、刪除頂部狀態欄
framework/base/core/res/res/values/dimens.xml中將status_bar_height設置爲0dp
2、刪除下方的導航欄
將qemu.hw.mainkeys屬性設置爲0
在看一下SystemUI的全套佈局
參考資料:
Android 8.0 SystemUI(三):一說頂部 StatusBar
深入理解Android卷3