Android10.0 StatusBar之狀態(tài)欄

學(xué)習(xí)筆記:參考資源 https://zhuanlan.zhihu.com/p/142596265学赛、https://blog.csdn.net/Bill_xiao/article/details/108244267

一港华、StatusBar簡(jiǎn)介

Statusbar包含導(dǎo)航欄(NavigationBar, 位于左側(cè)寺枉、右側(cè)或者底部)和狀態(tài)欄(StatusBar, 位于頂部, 可下拉)兩個(gè)部分。

StatusBar頂部狀態(tài)欄由三部分組成:
??1、最左邊的一部分顯示運(yùn)營(yíng)商水醋,時(shí)間管削,通知圖標(biāo)。
??2罗心、最右邊的一部分顯示系統(tǒng)圖標(biāo)里伯,它由狀態(tài)圖標(biāo)(例如 wifi ,bt)和電池圖標(biāo)組成。
??3渤闷、中間還有一塊區(qū)域疾瓮。


status_bar 結(jié)構(gòu)圖.png

??從上圖可以比較直觀的看出來頂層樹是super_status_bar,之后會(huì)走兩個(gè)分支status_bar_container和status_bar_expanded飒箭,status_bar_container這個(gè)分支主要呈現(xiàn)的是狀態(tài)欄界面狼电,狀態(tài)欄細(xì)分左邊和右邊,左邊是通知欄弦蹂,右邊是系統(tǒng)功能的狀態(tài)圖標(biāo)顯示肩碟,status_bar_expanded這個(gè)分支主要呈現(xiàn)的下拉菜單界面,其實(shí)下拉菜單中又分快捷圖標(biāo)和短信通知欄凸椿。

二削祈、StatuBar的創(chuàng)建

??StatusBar也是繼承SystemUI,啟動(dòng)流程和SystemUI一致脑漫。并在start的時(shí)候添加創(chuàng)建StatusBar相關(guān)的view髓抑。
??我們從StatusBar的start()方法開始看。
frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java

@Override
public void start() {
    
    // 省略部分代碼......

    // 創(chuàng)建整個(gè)SystemUI視圖并添加到WindowManager中
    createAndAddWindows();//這個(gè)重點(diǎn)方法窿撬,創(chuàng)建相關(guān)的視圖

    // 省略部分代碼......
}


public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {

    // 創(chuàng)建整個(gè)SystemUI視圖

    makeStatusBarView(result);

    // 把視圖添加到Window中

    mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);  //管理狀態(tài)欄启昧,導(dǎo)航欄、面板的狀態(tài)切換

    mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());
}

根據(jù)上面代碼可知劈伴,視圖的創(chuàng)建在makeStatusbarView(result)方法中密末;

     //創(chuàng)建狀態(tài)欄握爷,即初始化通知圖標(biāo)區(qū)域
    protected void makeStatusBarView(RegisterStatusBarResult result) {
        //1.初始化資源文件:導(dǎo)航欄高度 狀態(tài)欄高度 通知面板位置和高度等
        final Context context = mContext;
        updateDisplaySize(); // populates mDisplayMetrics
        updateResources();
        updateTheme();
        //加載layout文件super_status_bar,mStatusBarWindow
        inflateStatusBarWindow(context);
       
         // ...

        FragmentHostManager.get(mStatusBarWindow)
                .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
                    CollapsedStatusBarFragment statusBarFragment =
                            (CollapsedStatusBarFragment) fragment;
                    // 把控制器中的通知容器加入到狀態(tài)欄的容器中
                    statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);

                    // ...
                }).getFragmentManager()
                .beginTransaction()
                // CollapsedStatusBarFragment實(shí)現(xiàn)了狀態(tài)欄的添加
                .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
                        CollapsedStatusBarFragment.TAG)
                .commit(); 
          // ...
    }

??注意在這里添加了CollapsedStatusBarFragment严里,這個(gè)CollapsedStatusBarFragment就是作為加載狀態(tài)欄的Fragment新啼。
??在看 CollapsedStatusBarFragment 之前,我們先看看 NotificationIconAreaController(通知圖標(biāo)區(qū)域控制器)刹碾,在 NotificationIconAreaController 的構(gòu)造函數(shù)中會(huì)調(diào)用如下方法來創(chuàng)建通知圖標(biāo)的容器燥撞,即初始化布局。

// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java

    protected void initializeNotificationAreaViews(Context context) {
        // ...

        LayoutInflater layoutInflater = LayoutInflater.from(context);
        // 實(shí)例化通知圖標(biāo)區(qū)域視圖
        mNotificationIconArea = inflater.inflate(R.layout.notification_icon_area, null);
        // 這個(gè)才是真正存放通知圖標(biāo)的父容器
        mNotificationIcons = mNotificationIconArea.findViewById(R.id.notificationIcons);

        // ...
    }

??這個(gè)類加載的是 R.layout.notification_icon_are.xml 布局迷帜。這里先回到 makeStatusBarView(RegisterStatusBarResult result) 方法物舒,我們看 statusBarFragment.initNotificationIconArea(mNotificationIconAreaController) ,這句話的作用就是 將 NotificationIconAreaController 自己創(chuàng)建的通知圖標(biāo)容器戏锹,加入到狀態(tài)欄視圖中冠胯。

??接下來繼續(xù)看 CollapsedStatusBarFragment,在看之前我們應(yīng)該帶著一個(gè)問題去看:這些圖標(biāo)都是動(dòng)態(tài)添加的锦针,是怎么被添加和移除的荠察。
??由 CollapsedStatusBarFragment onCreateView() 可知,加載的布局是 R.layout.status_bar奈搜。
??在該布局里包含了這么幾個(gè)關(guān)鍵的視圖:

??1悉盆、@+id/status_bar_contents 這個(gè)LinearLayout,里面包含了@+id/status_bar_left_side 馋吗,從名字來看就知道是狀態(tài)欄左邊部分焕盟。這個(gè)狀態(tài)欄左邊部分包含了時(shí)鐘@+id/clock和短信通知@+id/notification_icon_area。

??2宏粤、@+id/system_icon_area這個(gè)也是一個(gè)LinearLayout包含了
@layout/system_icons京髓,這部分就是狀態(tài)欄右邊部分,里面包含了電池圖標(biāo)和系統(tǒng)狀態(tài)圖標(biāo)等等商架。

由 statusBarFragment.initNotificationIconArea(mNotificationIconAreaController) 可知,會(huì)進(jìn)入到CollapsedStatusBarFragment 的 initNotificationIconArea 方法里

// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java

    public void initNotificationIconArea(NotificationIconAreaController
            notificationIconAreaController) {
        // 通知圖標(biāo)區(qū)域
        ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
        // 這個(gè)才是通知圖標(biāo)的父容器
        mNotificationIconAreaInner =
                notificationIconAreaController.getNotificationInnerAreaView();
        //移除自己芥玉,再添加自己
        if (mNotificationIconAreaInner.getParent() != null) {
            ((ViewGroup) mNotificationIconAreaInner.getParent())
                    .removeView(mNotificationIconAreaInner);
        }
        // 把通知圖標(biāo)父容器添加到通知圖標(biāo)區(qū)域里
        notificationIconArea.addView(mNotificationIconAreaInner);

        // 省略處理中心圖標(biāo)區(qū)域的代碼

        // 這里其實(shí)顯示了通知圖標(biāo)區(qū)域和中心圖標(biāo)區(qū)域
        showNotificationIconArea(false);
    }

三蛇摸、監(jiān)聽通知的服務(wù)端

??當(dāng)一條新通知發(fā)送后,它會(huì)存儲(chǔ)到通知服務(wù)端灿巧,也就是NotificationManagerService赶袄,那么SystemUI是如何知道新通知來臨的。這就需要SystemUI向NotificationManagerService注冊(cè)一個(gè)"服務(wù)"(一個(gè)Binder)抠藕。
??這個(gè)"服務(wù)"就相當(dāng)于客戶端 SystemUI 在服務(wù)端 NotificationManagerService 注冊(cè)的一個(gè)回調(diào)饿肺。當(dāng)有通知來臨的時(shí)候,就會(huì)通過這個(gè)"服務(wù)"通知SystemUI盾似。通過 notificationListener.registerAsSystemService()敬辣,進(jìn)行注冊(cè)。
??可以來看下這個(gè)注冊(cè)的實(shí)現(xiàn):

 /**
     * Directly register this service with the Notification Manager.
     *
     * <p>Only system services may use this call. It will fail for non-system callers.
     * Apps should ask the user to add their listener in Settings.
     *
     * @param context Context required for accessing resources. Since this service isn't
     *    launched as a real Service when using this method, a context has to be passed in.
     * @param componentName the component that will consume the notification information
     * @param currentUser the user to use as the stream filter
     * @hide
     * @removed
     */
    @SystemApi
    public void registerAsSystemService(Context context, ComponentName componentName,
            int currentUser) throws RemoteException {
        if (mWrapper == null) {  
            // 這就是要注冊(cè)的Binder,也就一個(gè)回調(diào)
            mWrapper = new NotificationListenerWrapper();
        }
        mSystemContext = context;
        INotificationManager noMan = getNotificationInterface();
         
        // ...
        
        // 向通知的服務(wù)端注冊(cè)回調(diào)
        noMan.registerListener(mWrapper, componentName, currentUser);
    }

SystemUI注冊(cè)的這個(gè)“服務(wù)”其實(shí)就是:NotificationListenerWithPlugins溉跃。

注冊(cè)成功后村刨,就會(huì)回調(diào)NotificationListenerWithPlugins中的相關(guān)方法,并會(huì)附帶所有的通知信息撰茎。

四嵌牺、通知圖標(biāo)的顯示

當(dāng)一條新的通知來臨的時(shí)候,通知的服務(wù)端會(huì)通過NotificationListenerWithPlugins中的onNotificationPosted()進(jìn)行回調(diào)龄糊,而最終會(huì)調(diào)用NotificationListener中的onNotificationPosted()逆粹。

@Override
    public void onNotificationPosted(final StatusBarNotification sbn,
            final RankingMap rankingMap) {
        if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
        if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
             // 省略部分代碼...
                    // 進(jìn)行回調(diào)
                    handler.onNotificationPosted(sbn, rankingMap);
             // 省略部分代碼 ...
        }
    }

這個(gè)回調(diào),將調(diào)到 NotificationEntryManager 類里的 onNotificationPosted()方法炫惩。

public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
            final boolean isUpdateToInflatedNotif = mActiveNotifications.containsKey(sbn.getKey());
            if (isUpdateToInflatedNotif) {
                updateNotification(sbn, rankingMap);
            } else {
                addNotification(sbn, rankingMap);
            }
        }

這里只關(guān)注 addNotification(sbn, rankingMap) 僻弹,而它內(nèi)部時(shí)調(diào)用 addNotificationInternal() 方法實(shí)現(xiàn)的。

 private void addNotificationInternal(
            StatusBarNotification notification,
            RankingMap rankingMap) throws InflationException {
       
        // 省略部分代碼 ...

        NotificationEntry entry = mPendingNotifications.get(key);

        // 省略部分代碼 ...

        // Construct the expanded view.
        if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
            mNotificationRowBinderLazy.get()
                    .inflateViews(
                            entry,
                            () -> performRemoveNotification(notification, REASON_CANCEL),
                            mInflationCallback);
        }
        // 省略部分代碼 ...
    }

首先為通知?jiǎng)?chuàng)建一個(gè)NotificationEntry實(shí)例诡必,然后再通過NotificationRowBinderImpl中的inflateViews()加載通知視圖奢方,綁定通知信息,并在通知欄添加通知視圖爸舒,以及在狀態(tài)欄添加通知圖標(biāo)蟋字。

到此獲取到了數(shù)據(jù)、圖標(biāo)后面就是更新視圖進(jìn)行顯示了扭勉,由 NotificationRowBinderImpl#inflateViews → NotificationEntryManager#onAsyncInflationFinished → StatusBarNotificationPresenter#updateNotificationViews

// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java

    public void updateNotificationViews() {
        // ...

        //  把通知視圖添加到通知面版的通知欄中
        mViewHierarchyManager.updateNotificationViews();

        // 這里不僅僅更新了通知面版的通知視圖鹊奖,也更新了狀態(tài)欄的通知圖標(biāo)
        mNotificationPanel.updateNotificationViews(reason);

        // ...
    }

最終會(huì)調(diào)用 NotificationIconAreaController#updateNotificationIcons()進(jìn)行視圖更新。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末涂炎,一起剝皮案震驚了整個(gè)濱河市忠聚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌唱捣,老刑警劉巖两蟀,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異震缭,居然都是意外死亡赂毯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門拣宰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來党涕,“玉大人,你說我怎么就攤上這事巡社√诺蹋” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵晌该,是天一觀的道長(zhǎng)肥荔。 經(jīng)常有香客問我绿渣,道長(zhǎng),這世上最難降的妖魔是什么次企? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任怯晕,我火速辦了婚禮,結(jié)果婚禮上缸棵,老公的妹妹穿的比我還像新娘舟茶。我一直安慰自己,他們只是感情好堵第,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布吧凉。 她就那樣靜靜地躺著,像睡著了一般踏志。 火紅的嫁衣襯著肌膚如雪阀捅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天针余,我揣著相機(jī)與錄音饲鄙,去河邊找鬼。 笑死圆雁,一個(gè)胖子當(dāng)著我的面吹牛忍级,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伪朽,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼轴咱,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了烈涮?” 一聲冷哼從身側(cè)響起朴肺,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎坚洽,沒想到半個(gè)月后戈稿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡讶舰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年器瘪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绘雁。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖援所,靈堂內(nèi)的尸體忽然破棺而出庐舟,到底是詐尸還是另有隱情,我是刑警寧澤住拭,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布挪略,位于F島的核電站历帚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏杠娱。R本人自食惡果不足惜挽牢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望摊求。 院中可真熱鬧禽拔,春花似錦、人聲如沸室叉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)茧痕。三九已至野来,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間踪旷,已是汗流浹背曼氛。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留令野,地道東北人舀患。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像彩掐,于是被迫代替她去往敵國(guó)和親构舟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容