SystemUI鎖屏布局 SystemUI狀態(tài)欄布局 SystemUIQS布局 QuickSettings布局 SystemUI通知布局 Android 12 源碼分析 —— 應用層 四(SystemUI的基本布局設計及其基本概念)
Android 12 源碼分析 —— 應用層 四(SystemUI的基本布局設計及其基本概念)
更新歷史日期內(nèi)容12023-9-11增加文中提及的漸變動畫的效果圖
在上兩篇文章中,我們介紹SystemUI的啟動過程,以及基本的組件依賴關系?;镜囊蕾囮P系請讀者一定要掌握,因為后面的文章,將會時常出現(xiàn)這些依賴關系的使用,屆時將會一筆帶過,而不會詳細說明他們的實現(xiàn)細節(jié)和原理。
接下來我們將介紹,SystemUI中各個UI部分是如何被加入屏幕中的,以及查看下拉狀態(tài)中的各個圖標的實現(xiàn)。
但是在進入真正的主題之前,我們還需要了解SystemUI中的各個UI的基本布局以及名字,只有了解了這些基本布局和名字,才能夠在許多相似名字中分清楚具體的對象是誰。
接下來我們將介紹SystemUI的基本布局設計,看看整個SystemUI的布局被大致分成了哪些內(nèi)容,以及這些內(nèi)容對應的業(yè)務是什么。
SystemUI的window劃分
在“Android 12 源碼分析 —— 應用層 二(SystemUI大體組織和啟動過程)http://t.csdn.cn/chk6Y”文章中,我們提及過,SystemUI可以通過WindowManager來顯示自己的UI。
事實上,SystemUI正是通過WindowManager來添加自己的主要的UI視圖。在SystemUI中,將以下幾個部分,分別放置在不同的Window中,然后通過WindowManager將其添加到屏幕上。
StatusBar——狀態(tài)欄NotificationShade——通知卷簾(我們常說的下拉狀態(tài)欄)NavigationBar——導航欄
注意:除了上面三個常見的window以外,還有其他的window,如GlobalActionDialog(負責顯示——關機,重啟的彈框),VolumeDialog(顯示音量調(diào)節(jié)),這些window將會在我們介紹各個子主題的時候,依次登場
可能會有讀者疑惑:鎖屏呢?不在一個單獨window下面嗎?事實上,鎖屏不在一個單獨的window中,其本身就是NotificationShade(通知卷簾,即下拉狀態(tài)欄)。換句話說,鎖屏的內(nèi)容和下拉狀態(tài)欄的內(nèi)容在同一個window中。
這里一定要注意的一點是:對應的中文命名方式,從此刻開始,將在對應的類后面用小括號標記其中文命名,之所以有這樣的標記,是因為英文名字有很多相似之處,可能不利于記憶,如:StatusBar雖然叫做狀態(tài)欄,但是在不同的Window中有不同的StatusBar(狀態(tài)欄),比如,在鎖屏狀態(tài)下有KeyguardViewStatusBar(鎖屏狀態(tài)欄);同樣地,在NotificationShade(通知卷簾,即下拉狀態(tài)欄)中本應該顯示StatusBar(狀態(tài)欄)的地方,并不叫StatusBar。而為了便于記憶,將其中文名字放在小括號中
為了便于理解,現(xiàn)在window標記出來,如下:
有了上面的直觀感受,我們將結(jié)合SystemUI的layout文件,大體的介紹其UI布局及其相關概念,有了這些概念,才能夠在紛繁復雜的源碼中,找到對應的實現(xiàn)實體。
SystemUI的命名規(guī)則和設計模式
本文主題是SystemUI的布局介紹,但依然可能會存在這樣的疑問:這些布局是什么時候被加載的?他們的加載流程是怎樣的?
在后面的文章中,我們會依次介紹這些布局的加載,以及怎么處理各種事件,但再此之前,需要明白他們之間的設計模式和命名規(guī)則。
SystemUI各個小模塊采用:MVC,MVP等設計模式,因此在他們的命名體系中,經(jīng)常以xxxView,xxxController,xxxPresenter來代表。例如,代表StatusBar(狀態(tài)欄)的StatusBarWindowView(狀態(tài)欄窗口視圖),StatusBarWindowController(狀態(tài)欄窗口控制器),而與StatusBarWindow相關的model即為StatusBar(狀態(tài)欄)
當然,SystemUI編輯歷史長,中間有幾次大的修改,并不是所有的類,都遵循這些規(guī)則,不過讀者在閱讀源碼的時候,可以按照這些思路進行思考。
而本文以介紹SystemUI布局為主題,而不關心這些布局的加載時刻和流程,但我們會用一段小提示來,簡略的概括這些布局的加載流程。在閱讀這些簡略加載流程的時候,會出現(xiàn)很多相似的名字,因此可以按照上面的命名規(guī)則來對應相應的實體
讀者可以忽略這些加載流程,因為在后續(xù)的文章中,將會詳細的介紹這些UI的加載流程和用戶操作
我們先從最復雜的NotificationShade(通知卷簾,下拉狀態(tài)欄)介紹起。
SystemUI的NotificationShade(通知卷簾,即下拉狀態(tài)欄)window的詳細布局
NotificationShade(通知卷簾,即下拉狀態(tài)欄)window的的加載流程
按照上文的說明,會先簡略介紹,這個布局的加載流程,如下:
SytemUIService啟動之后,調(diào)用SystemUIApplication的startServicesIfNeeded()啟動相應的服務(“Android 12 源碼分析 —— 應用層 二(SystemUI大體組織和啟動過程)http://t.csdn.cn/chk6Y”)啟動的服務中,有一個叫做StatusBar,它會在第一步中,被調(diào)用start()方法在start()方法中,會調(diào)用createAndAddWindows()方法createAndAddWindows()方法會調(diào)用makeStatusBarView()方法makeStatusBarView()方法會調(diào)用inflateStatusBarWindow()方法inflateStatusBarWindow()方法會調(diào)用SuperStatusBarViewFactory的getNotificationShadeWindowView()方法getNotificationShadeWindowView()方法會去加載并解析R.layout.super_notification_shade,也即super_notification_shade.xml第7步完成之后,會回到第4步中,并調(diào)用NotificationShadeWindowController的attach()方法,將對應的UI,添加到屏幕上
注意:對于上面的調(diào)用過程,讀者可以完全不用在意,因為后面的文章,將會詳細介紹
接下來分析,super_notification_shade.xml
NotificationShade(通知卷簾,即下拉狀態(tài)欄)window的頂層視圖
super_notification_shade.xml的源文件如下:
注意:為了減輕閱讀障礙,我會省略掉一些非常常見的代碼,如一些屬性的定義和賦值,省略一些非常簡單的布局視圖
android:id="@+id/scrim_behind" /> android:id="@+id/scrim_notifications"/> android:id="@+id/light_reveal_scrim" /> android:id="@+id/scrim_in_front" /> android:id="@+id/keyguard_message_area"/> 從上面的代碼結(jié)構(gòu)看,非常滴簡單,我們依次介紹如下: NotificationShadeWindowView:通知卷簾窗口,即下拉狀態(tài)欄窗口的最頂層視圖 BackDropView:幕布視圖,它會顯示最最底部的一些背景,如播放音樂的時候,顯示音樂的專輯圖片 ScrimView:總共有三個,他們主要負責遮罩,如在鎖屏狀態(tài)下,整個鎖屏界面,稍微變暗淡一些,即通過設置遮罩的透明度來實現(xiàn) LightRevealScrim:也是遮罩相關的視圖,它負責處理一些漸變的動畫,如在設置中打開息屏顯示,那么在點擊電源按鈕的時候,將會顯示對應的動畫,如下圖 status_bar_expanded.xml:即展開的狀態(tài)中的布局,這里面詳細列出了下拉狀態(tài)欄之后,應該顯示哪些內(nèi)容 brightness_mirror_container.xml:這個是調(diào)節(jié)亮度條對應的鏡像視圖,因為還有一個亮度調(diào)節(jié)條他在status_bar_expanded中,之所以有這個,是為了在調(diào)節(jié)亮度的時候,隱藏status_bar_expanded中的內(nèi)容 KeyguardMessageArea:鎖屏信息顯示區(qū)域,如輸入pin錯誤,則會在此處顯示wrong pin 除了第七點,可以直接給出參考圖以外,其他的都無法給出特別好的參考圖,因此可以使用文末推薦的布局查看工具,進行查看 在上面中出現(xiàn)了兩個include的xml文件,如果每個文件都介紹,將會消耗大量的篇幅,同時又有些詳略不得當,因此,針對一些簡單的layout文件,將會一筆帶過,如上面的brightness_mirror_container.xml文件。而這些略過的文件,在某些子主題下,將會詳細介紹,如上面的brightness_mirror_container.xml將會在調(diào)節(jié)音量的文章中,詳細介紹 因此接下來將會介紹status_bar_expanded.xml文件 status_bar_expanded.xml文件分析 status_bar_expanded.xml文件原文如下: 注意:這里依然會省略掉一些屬性定義和賦值 android:id="@+id/notification_panel"> android:id="@+id/big_clock_container" /> android:id="@+id/keyguard_qs_user_switch_stub"/> layout="@layout/keyguard_bottom_area" android:visibility="gone" /> android:id="@+id/keyguard_user_switcher_stub" /> android:id="@+id/lock_icon_view"> android:id="@+id/notification_container_parent"> android:id="@+id/qs_edge_guideline"/> android:id="@+id/notification_stack_scroller"/> android:id="@+id/ambient_indication_container" /> layout="@layout/keyguard_status_bar" android:visibility="invisible" /> android:id="@+id/preview_container"> 對上面文件的介紹如下: NotificationPanelView:整個通知欄和快速設置圖標的頂層目錄id為big_clock_container的FrameLayout:顯示一個較大的時鐘視圖兩個ViewStub:這個跟多用戶的切換視圖有關,當點擊多用戶的時候,會在這兩個ViewStub之一中,顯示切換的視圖keyguard_bottom_area.xml:顯示鎖屏底部區(qū)域,如充電中…status_bar_expanded_plugin_frame.xml:跟SystemUI的插件有關,現(xiàn)在讀者可以不用管,后面會詳細介紹其細節(jié)dock_info_bottom_area_overlay.xml:空的LockIconView:鎖屏界面下的鎖圖標NotificationsQuickSettingsContainer:通知和快速設置的容器,其中顯示通知和快速設置 keyguard_status_view.xml:顯示鎖屏相關的狀態(tài)視圖,如顯示日期dock_info_overlay.xml: 空的id為qs_frame的FrameLayout:用于快速設置的父容器Guideline:用于調(diào)整布局的視圖NotificationStackScrollLayout:用于顯示通知的父容器ambient_indication.xml:空的photo_preview_overlay.xml:空的keyguard_status_bar.xml:鎖屏狀態(tài)下的,狀態(tài)欄Button:調(diào)試使用,不用管TapAgainView:顯示,再按一次的視圖,現(xiàn)在已經(jīng)不用了 id為preview_container的FrameLayout:預覽圖片顯示視圖,如滑動相機圖片,會先顯示一個相機的圖片 注意:對于上面某些xml文件的說明為:空的,一方面,部分內(nèi)容已經(jīng)不再使用,另一方面,部分內(nèi)容將會在后續(xù)文章中出現(xiàn)。 在上面的xml文件中,細心的讀者可能已經(jīng)注意到,即鎖屏也是在NotificationShade窗口中的。為了能夠直觀的觀察上面的內(nèi)容,我們使用圖片進行說明,如下圖: 在上面的源碼中,出現(xiàn)了 keyguard_bottom_area.xmlstatus_bar_expanded_plugin_frame.xmldock_info_bottom_area_overlay.xmlkeyguard_status_view.xmldock_info_overlay.xmlambient_indication.xmlphoto_preview_overlay.xmlkeyguard_status_bar.xml 在上面文件,其中status_bar_expanded_plugin_frame.xml,dock_info_bottom_area_overlay.xml,dock_info_overlay.xml,ambient_indication.xml,photo_preview_overlay.xml一部分沒有被使用,一部分將會在后面的子主題中介紹,現(xiàn)在略過并不影響閱讀和理解 剩下的keyguard_bottom_area.xml,keyguard_status_view.xml和keyguard_status_bar.xml三個文件,簡單明了,能夠非常容易的將UI對應上,因此不在此處介紹。這并不影響本文的閱讀和理解。不過不用擔心,在后續(xù)的文章中,我們還會介紹這三個文件,看看他們的交互和初始化過程,此處為了整體行文的緊湊,將其略去 雖然我們已經(jīng)能夠?qū)㈡i屏相關的UI和代碼建立一個聯(lián)系,但是我們現(xiàn)在還不能將下拉設置中的UI和代碼建立聯(lián)系。接下來,我們處理這部分內(nèi)容。 QuickSettings(快速設置)的布局分析 在上面的代碼中,已經(jīng)介紹了id為qs_frame的FrameLayout將用來顯示快速設置,接下來看看他們是如何布局的。 注意:NotificationStackScrollLayout的布局細節(jié)將會在后文中詳解 QuickSettings的加載流程 在上文我們知道,id為qs_frame的FrameLayout并沒有使用include來加載一個layout,因為我們需要先簡單說明一下,其加載過程,然后在看其布局結(jié)構(gòu) SytemUIService啟動之后,調(diào)用SystemUIApplication的startServicesIfNeeded()啟動相應的服務(“Android 12 源碼分析 —— 應用層 二(SystemUI大體組織和啟動過程)http://t.csdn.cn/chk6Y”)啟動的服務中,有一個叫做StatusBar,它會在第一步中,被調(diào)用start()方法在start()方法中,會調(diào)用createAndAddWindows()方法createAndAddWindows()方法會調(diào)用makeStatusBarView()方法makeStatusBarView()方法會找到id為qs_frame的FrameLayout并向其中添加一個QSFragmentQSFragment會去加載qs_panel.xml文件,而這個文件,正是我們的快速設置的布局文件 qs_panel.xml文件分析 qs_panel.xml原文如下: android:id="@+id/quick_settings_container" > android:id="@+id/expanded_qs_scroll_view"> android:id="@+id/quick_settings_panel"> android:visibility="gone" /> QSContainerImpl:快速設置的父容器NonInterceptingScrollView:快速設置中各個圖標的ScrollView QSPanel:QSPanel展示里面具體的詳情qs_footer_impl.xml:快速設置底部的顯示視圖,如編輯快速設置的圖標,見后面圖片的對應關系 quick_status_bar_expanded_header.xml:快速設置中的頭部區(qū)域,見下面的圖片設置qs_detail.xml:快速設置的詳細信息qs_customize.xml:編輯快速設置的界面 為了能夠方便的對應起來,見下面的圖片 在這里插入圖片描述 在上面的圖片中我們可以看到,QSPanel內(nèi)部,將會有有各種各樣的快速設置的小圖標,而這些圖標被稱為QSTile. 我們將會在QSPanel的源碼分析中,詳細講解他們的加載過程。本文旨在介紹SystemUI的布局設計,而非對某個UI的具體細節(jié)。 介紹完了qs_frame的布局以后,我們還需要查看該window下面的最后一個布局NotificationStackScrollLayout NotificationStackScrollLayout布局分析 NotificationStackScrollLayout是一個類,它也沒有使用include來引用其他的布局文件,而其內(nèi)部則是通過其他的方式來添加布局的。 NotificationStackScrollLayout加載通知布局的流程 當系統(tǒng)有通知到來時,調(diào)用NotificationListener的onNotificationPosted()方法在onNotificationPosted方法中,會通知NotificationEntryManager的mNotifiListener的onNotificationPosted()方法在該方法中,會調(diào)用addNotification()方法,然后在addNotification()中調(diào)用addNotificationInternal()方法在addNotificationInternal()方法中,會調(diào)用NotificationRowBinderImpl的inflateViews()方法在這個方法中,會去調(diào)用RowInFlaterTask的inflate()方法在inflate()方法中,會去加載status_bar_notification_row.xml文件 接下來我們看看這個文件的內(nèi)容 status_bar_notification_row.xml文件解析 status_bar_notification_row.xml文件的原文如下: android:id="@+id/expandableNotificationRow"> 只有一項內(nèi)容,當然其內(nèi)部的內(nèi)容,我們現(xiàn)在不需要關系,ExpandableNotificationRow是我們觀察的最小布局,因為其內(nèi)部已經(jīng)涉及具體的內(nèi)容該怎么顯示了,現(xiàn)在我們只需要領略大概的布局,并明白相關的概念 ExpandableNotificationRow:一個可展開的通知 注意:對于通知的排序,優(yōu)先級等,我們將在后面的文章中,一一介紹,此處只需了解整體布局和基本概念即可 至此,NotificationShade窗口的布局介紹基本已經(jīng)完成,在這里面我們熟悉了各個UI的實體,當然也省略了一些UI的實體, 省略的部分,將會在后面文章的子主題中,詳細介紹。 接下來我們接續(xù)查看剩下的兩個window——StatusBar和NavigationBar StatusBar(狀態(tài)欄)Window的詳細布局 StatusBar(狀態(tài)欄)window的加載流程 先簡單介紹其加載流程 SytemUIService啟動之后,調(diào)用SystemUIApplication的startServicesIfNeeded()啟動相應的服務(“Android 12 源碼分析 —— 應用層 二(SystemUI大體組織和啟動過程)http://t.csdn.cn/chk6Y”)啟動的服務中,有一個叫做StatusBar,它會在第一步中,被調(diào)用start()方法在start()方法中,會調(diào)用createAndAddWindows()方法createAndAddWindows()方法會調(diào)用makeStatusBarView()方法makeStatusBarView()方法中,會調(diào)用inflateStatusBarWindow()方法在inflateStatusBarWindow()方法中,會通過StatusBarComponent的getStatusBarWindowController()方法得到StatusBarWindowController在StatusBarWindowController的構(gòu)造函數(shù)中,會調(diào)用SuperStatusBarViewFactory的getStatusBarWindow()方法而SuperStatusBarViewFactory的getStatusBarWindow()方法將會去加載super_status_bar.xml文件在第四步的createAndAddWindows()方法中,會調(diào)用StatusBarWindowController的attach()方法,將其添加到屏幕上 接下來看看StatusBar(狀態(tài)欄)的布局文件 StatusBar(狀態(tài)欄)Window的頂層視圖 super_status_bar.xml的源文件如下: android:id="@+id/status_bar_launch_animation_container"/> android:id="@+id/status_bar_container" /> 非常,非常的清爽??! StatusBarWindow:表示了狀態(tài)欄的最頂層視圖id為status_bar_launch_animation_container的FrameLayout:在這里面顯示動畫,例如:從StatusBar(狀態(tài)欄)中啟動一個Activity時的動畫顯示區(qū)域id為status_bar_contianer的FrameLayout:這就是現(xiàn)實狀態(tài)欄內(nèi)容的主要區(qū)域 接下來我們查看其詳細細節(jié) StatusBar(狀態(tài)欄)的布局分析 上面id為status_bar_contianer是一個FrameLayout,我們來看看其如何加載布局的。先簡單介紹其布局加載流程 StatusBar(狀態(tài)欄)布局加載流程 SytemUIService啟動之后,調(diào)用SystemUIApplication的startServicesIfNeeded()啟動相應的服務(“Android 12 源碼分析 —— 應用層 二(SystemUI大體組織和啟動過程)http://t.csdn.cn/chk6Y”)啟動的服務中,有一個叫做StatusBar,它會在第一步中,被調(diào)用start()方法在start()方法中,會調(diào)用createAndAddWindows()方法createAndAddWindows()方法會調(diào)用makeStatusBarView()方法makeStatusBarView()方法,會將CollapsedStatusBarFragment放入id為status_bar_container的FrameLayout中而CollapsedStatusBarFragment會在其onCreateView的時候,加載并初始化status_bar.xml文件 status_bar.xml文件分析 status_bar.xml文件原文如下: android:id="@+id/status_bar"> android:id="@+id/notification_lights_out"/> android:id="@+id/status_bar_left_side"> android:id="@+id/operator_name"/> android:id="@+id/clock"/> android:id="@+id/notification_icon_area"/> android:id="@+id/cutout_space_view" /> android:id="@+id/centered_icon_area"/> android:id="@+id/emergency_cryptkeeper_text"/> PhoneStatusBarView:狀態(tài)欄內(nèi)容的父容器當狀態(tài)欄被請求以SYSTEM_UI_FLAG_LOW_PROFILE模式顯示時,這個視圖負責顯示一個小點id為status_bar_content的LinearLayout:顯示狀態(tài)欄內(nèi)容 heads_up_status_bar_layout.xml:處在StatusBar中的部分提示信息id為status_bar_left_side的LinearLayout:狀態(tài)欄左邊的顯示區(qū)域Space:填充中間的空白區(qū)域AlphaOptimizedFrameLayout:中心圖標顯示區(qū)域AlphaOptimizedLinearLayout:系統(tǒng)圖標顯示區(qū)域,如電量百分比 id為emergency_cryptkeeper_text的ViewStub:顯示只能撥打緊急呼救電話 從上面的布局文件可以看到,StatusBar(狀態(tài)欄)大致分成了三個區(qū)域,左,中,右,現(xiàn)在只需要了解其中大體布局即可,即能夠在閱讀源碼的時候,知道源碼中的View指的是哪一部分區(qū)域即可。在后面的文章中,我們將詳細介紹這些布局的內(nèi)容和實現(xiàn)細節(jié)。 至此,我們了解了,SystemUI中,最重要的兩個Window的布局。剩下最后一個Window即NavigationBar,因為其布局的簡單,我們將在分析Android導航手勢的時候,一并介紹 本文,只需要掌握如下幾個概念即可,因為后面的系列文章,還會反復提及: SystemUI的Window的劃分SystemUI的各個UI的名字如,NotificationShadeView,QS等等知道對應的View的Controller的名字,大概會是什么樣子,如NotificationShadeWindowView的Controller,應該叫做NotificationShadeWindowViewController,反之亦然 因為最近身體欠佳,加之已經(jīng)開學,學業(yè)繁重,行文倉促,難免有些錯誤,忘看到的讀者給予提醒,謝謝先。 補充:布局查看工具 讀者可使用下面的工具,進行相應的布局查看 老版本DDMS,的hierarchy viewAndroid Studio的YALI(通過插件市場安裝)Android Studio的Legacy Layout Inspector(通過插件市場安裝) 至于Android Studio自帶的Layout Inspector就不推薦使用了。 文章鏈接
本文內(nèi)容根據(jù)網(wǎng)絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權,聯(lián)系刪除。