SystemUI系列之StatusBar

在上一篇(https://blog.csdn.net/u011164827/article/details/102998091)分析到SystemUI的啓動過程,如今分析StatusBar。java

啓動分析

SystemUI在SystemUIApplication會啓動各個模塊,在這個地方會調用com.android.systemui.SystemBars的start方法。android

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。ide

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中有大量的代碼。ui

mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE));this

這一行獲取IStatusBarService的實例。IStatusBarService是一個系統服務,由ServerThread啓動並常駐system_seerver進程中。IStatusBarService爲那些對狀態欄感興趣的其它系統服務定義了一系列API,然而對SystemUI而言,它更像一個客戶端。由於IStatusBarService會將操做狀態欄的請求發給SystemUI,並由後者完成請求。spa

這裏主要看一下createAndAddWindows方法.net

狀態欄的佈局

狀態欄須要顯示的信息分爲如下5種:3d

  • 通知信息:在狀態欄左側顯示一個圖標提醒用戶,並在下拉捲簾中爲用戶顯示更加詳細的信息。
  • 時間信息:顯示在狀態欄最右側的通常小型數字時鐘,是一個名爲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()接口爲外界提供了修改系統狀態圖標的途徑,可是它對信息的內容有很強的限制。一、系統狀態圖標沒法顯示圖標之外的信息;二、系統狀態圖標對其顯示的圖標數量以及圖標鎖表示的意圖有嚴格的限制。

咱們繼續分析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常見問題修改

一、刪除頂部狀態欄

framework/base/core/res/res/values/dimens.xml中將status_bar_height設置爲0dp

二、刪除下方的導航欄

將qemu.hw.mainkeys屬性設置爲0

在看一下SystemUI的全套佈局

參考資料:

Android 8.0 SystemUI(三):一說頂部 StatusBar

系統服務-SystemUI9.0

深刻理解Android卷3