當前位置:編程學習大全網 - 源碼下載 - 安卓視覺層次的秘密

安卓視覺層次的秘密

最近連接了語音控制功能。UI的具體實現是在應用上覆蓋壹層透明的防觸摸層,防止用戶在語音狀態下點擊,但不能影響對對話框調出物理回車鍵的控制。同時,還應該防止調用非物理返回鍵的對話的操作。功能好像是上弦的。我們用壹張圖來說明壹下。

從圖中不難看出,我們要實現的語音控制層,其實是在應用視圖和視圖內部的提示框之上的壹層,同時在後退鍵彈出窗口之下。因為對Android視圖級別的探索還沒有很深入,所以我通過做這個功能對Android視圖級別的知識進行了總結和梳理。

首先,我們通過壹個層次圖來明確壹些重要的概念,比如Window、DecorView、mContentParent。

在Android中,無論是Activity、Toast、ActionBar還是Dialog,它們的視圖都是依附於窗口的。其實基本上所有的視圖都是同時通過窗口呈現的,所以窗口可以理解為視圖的載體和管理者。窗口有三種類型,即應用程序窗口、子窗口和系統窗口。應用程序類窗口對應壹個Acitivity,子窗口不能單獨存在,需要附加到特定的父窗口。例如,壹些常見對話框是子窗口。系統窗口是壹個只能通過聲明權限來創建的窗口。例如,Toast和系統狀態欄都是系統窗口。

裝飾視圖是窗口中視圖的頂視圖。實際上,DecorView是FrameLayout的壹個子類,它包含壹個帶有ActionBar和mContentParent的LinearLayout。

mContentParent這個名字可能有點陌生,但它其實就是我們經常使用的應用根布局,即android。活動中的R.id.content SetContentView實際上是通過LayoutInflater將XML布局轉換為視圖,並添加到mContentParent中。

每個活動都會持有壹個窗口,而在Android中,Window只有壹個實現類PhoneWindow,所以每個活動都會持有壹個PhoneWindow,頂層視圖DecorView會在PhoneWindow中持有。那麽Activity如何與PhoneWindow建立聯系呢?讓我們通過源代碼來探究壹下:

在活動啟動期間,執行ActivityThread的performLaunchActivity方法,其中調用活動的attach。在attach()方法中實例化活動持有的mWindow。因為Activity實現了Window的回調接口,所以當Window接受外部狀態變化時,它會回調Activity的方法。

如妳所見,在PhoneWindow中,出現了成員變量DecorView。這裏,DecorView是PhoneWindow中的壹個內部類,繼承自FrameLayout。

這是我們每次寫活動都會調用的setContentView方法,它內部調用getWindow()的setContentView,這個mWindow就是PhoneWindow。

我們看到PhoneWindow中有三個setContentView的重載方法。在setContentView(int layoutResID)中,首先判斷mcontendent父級。如果mcontendent父級為空,則第壹次調用它,並執行installDecor()方法來創建DecorView並將其添加到mcontendent父級。如果mContentParent不為空,請從mContentParent中移除該視圖。然後通過mLayoutInflater將XML轉換成視圖樹,並添加到mContentParent視圖中。添加完成後,回調通知onContentChanged,表示接口加載完成。

首先判斷mDecor是否為空。如果它是空的,則由generateDecor創建壹個DecorView。然後將DecorView的焦點獲取能力設置為focus _ after _ descendents,即先分配給子視圖進行處理。如果沒有處理所有的子視圖,它將自己處理。第壹個DecorView未加載到mContentParent中,因此mContentParent為空。調用generateLayout將setContentView的內容添加到mContentParent。

定制過Acitivity的Actionbar或者全屏的同學壹定知道requesetFeature方法需要在setContentView之前調用,這就是原因。setContentView的本質是觸發活動的resume狀態,同時觸發makeVisible方法。

GetWindow()。getAttributes()在這裏用作LayoutParams,在WindowManager中:

可以看出,活動的窗口類型是TYPE_APPLICATION,它決定了窗口層中的顯示級別。該類型的概述如下:

Dialog不屬於view,它是應用程序的子窗口,所以這就是為什麽我們不能通過添加View到mContentParent來阻止Dialog。對話框中的窗口也是由PolicyManager的makeNewWindow方法完成的。普通的對話框必須采用活動的上下文,如果采用應用的上下文,就會報錯。這是因為沒有應用程序令牌,應用程序令牌壹般只由Activity擁有。常規對話的類型是類型應用附加對話。通過不同類型的層次結構,我們可以找到WindowManager LayoutParams的屬性,如TYPE_SYSTEM_ALERT、TYPE_TOAST等,可以完全覆蓋常規對話框。兩者的區別在於,壹個是系統級對話框,壹個是Toast,系統對話框需要申請權限。因此,我們的第壹個方案是對遮擋對話框使用常規對話框,對語音提示框使用TYPE_SYSTEM_ALERT。但是,我們都知道Android有壹個無法回避的問題,那就是廠商定制。在MUI的框架層,為了“安全”起見,對用戶默認關閉了浮動窗口權限,也就是說設置了TYPE_SYSTEM_ALERT屬性的視圖默認是不能顯示的,只有在用戶手動打開權限後才能顯示。

雖然妳可以在用戶啟動時根據用戶的模型跳轉打開權限頁面,但這種作為感性開發的不完美體驗還是讓人無法接受。根據之前對Android視圖層次的研究,我們有第二種方案。應用視圖存儲在mContentParent中,與Activity同屬於TYPE_APPLICATION窗口層次結構,屬於最底層,而常規對話框的級別是TYPE _ APPLICATION _ attached _ Dialog,所以我們把常規對話框看作是頂層的壹個不顯眼的提示框,只需要考慮下面不顯眼的彈窗和語音控制層。因為語音控制層需要能夠阻止提示彈出,所以需要在彈出的上層。經過之前的學習,我們將彈出添加到mContentParent,將語音控制層添加到DecorView層,完美地解決了這個問題。mContentParent是壹個FrameLayout,應用程序視圖首先通過sentContentView添加到MContentParent。作為提示彈出,添加順序必須放在應用視圖之後,所以當提示彈出窗口再次添加到mContentParent時,會添加到應用視圖中。DecorView是mContentParent的父容器,也是FrameLayout。添加語音提示框時,mContentParent必須已經存在,所以添加時必須在mContentParent之上。

就這樣,壹個看似復雜的需求,通過探索Android源代碼,完美解決了。很多時候當我們遇到壹個很難解決的問題時,不妨試著回到問題的原點,思考問題的本質。很多時候,會有不同的發現。

  • 上一篇:珠寶行業標準化建設穩步推進
  • 下一篇:為什麽玩不了傳世私服
  • copyright 2024編程學習大全網