View體系詳解系列

? ? ? ? ? ? ? ? ? ? ? ? ? ??View體系詳解(1)

? ? 前言:看了大概一個(gè)月SystemUI的相關(guān)源碼,里面關(guān)于自定義View的知識(shí)比較多,迫使自己要去了解以前不太懂的顯示子系統(tǒng)的知識(shí),以前只知道一些粗略的view知識(shí),如它是用來(lái)顯示具體畫(huà)面的,它的載體是window,它可以復(fù)寫(xiě)事件處理函數(shù)去處理某些點(diǎn)擊事件,自定義view要實(shí)現(xiàn)onMeasure, onLayout, onDraw等,但是一直比較模糊,只是知道個(gè)大概,經(jīng)過(guò)一陣子的源碼和博客的閱讀,對(duì)view體系有了許多新認(rèn)識(shí)和領(lǐng)悟,因此記錄下來(lái)。計(jì)劃分以下幾部分:

? ? ? ? ? ? View體系詳解(1):View體系總覽

? ? ? ? ? ?View體系詳解(2):自定義View流程以及系統(tǒng)相關(guān)行為

? ? ? ? ? ? View體系詳解(3):View事件處理機(jī)制

*****寫(xiě)的不好請(qǐng)理解,由于自己知識(shí)水平和技術(shù)經(jīng)驗(yàn)所限,不可避免有錯(cuò)漏的地方,懇請(qǐng)指正。*******

? View體系總覽

? ? 1.首先我們要明確View的概念是什么?源碼在View的class文件中有一個(gè)介紹:

* This class represents the basic building block for user interface components. A View

* occupies a rectangular area on the screen and is responsible for drawing and

* event handling.

? ? ? 所以view相當(dāng)于一個(gè)可以在屏幕上面繪制內(nèi)容的有限區(qū)域,并且負(fù)責(zé)處理具體的交互事件。這也回答了我們?yōu)槭裁醋远xview的時(shí)候要經(jīng)過(guò)三個(gè)步驟:測(cè)量(Measure),布局(Layout),還有繪畫(huà)(Draw),并且當(dāng)用戶(hù)希望界面可以響應(yīng)我們的操作時(shí),我們?yōu)槭裁磿?huì)調(diào)用setOnClickListener()等函數(shù)去設(shè)置處理事件的邏輯函數(shù),這個(gè)后面細(xì)說(shuō)。


? ? 2.那么View又怎么被管理呢?我們?cè)趚ml文件中定義了view,它怎么就能顯示在屏幕上了?我們可以看看在A(yíng)S中新建一個(gè)app工程,最初的xml文件中的寫(xiě)法(部分內(nèi)容省略),然后調(diào)用代碼:setContentView(R.layout.activity_main) , 畫(huà)面就有了,一個(gè)空白界面,中間是熟悉的Hello World!字樣。

<FrameLayout

? ? android:layout_width="match_parent"

? ? android:layout_height="match_parent"

? ? tools:context=".MainActivity">

? ? ? ? ? <TextView

? ? ? ? ? ? android:layout_width="wrap_content"

? ? ? ? ? ? android:layout_height="wrap_content"

? ? ? ? ? ? android:text="Hello World!"/>

</FrameLayout>

? ? ? ? 我們可以看到,外面是一層FrameLayout,查看源碼便可以發(fā)現(xiàn),它的父類(lèi)是ViewGroup,再上去的父類(lèi)便是View,由此我們可以知道View的管理是通過(guò)View樹(shù)的形式來(lái)實(shí)現(xiàn)的,比如一開(kāi)始的畫(huà)面,它的根節(jié)點(diǎn)便是FrameLayout,也就是一個(gè)布局,子節(jié)點(diǎn)便是被包含在其中的TextView,而setContentView()是封裝了xml文件解析,DecorView(activity的根view節(jié)點(diǎn))實(shí)例化,view樹(shù)生成等邏輯,這也是為什么如果你要找到xml文件中定義的TextView實(shí)例,要在該函數(shù)之后去調(diào)用findViewById(),因?yàn)橹挥姓{(diào)用setContentView()后,我們的xml文件的內(nèi)容才被解析,TextView實(shí)例才會(huì)創(chuàng)建并被賦值一個(gè)唯一的id,這樣我們才能找到它的實(shí)例。

? ? ? ? 既然如此,那么我們可以在布局里面,再嵌套別的布局么?答案是可以的,因?yàn)椴季忠彩且粋€(gè)view,不過(guò)它可以容納別的view在里面,因?yàn)椴季质抢^承自ViewGroup的,這就說(shuō)明它本身是一個(gè)根節(jié)點(diǎn)view。從這我們也可以看出這里用的是組合模式,無(wú)論單個(gè)view還是一個(gè)布局,都可以容納在他們的根節(jié)點(diǎn)布局中,層層嵌套。


? ? 3.如此又引入一個(gè)新問(wèn)題,既然布局中可以嵌套布局,那么怎么管理這些根節(jié)點(diǎn)?他們的優(yōu)先級(jí)如何管理呢?如果都讓View來(lái)處理這些,那么View的職責(zé)就十分龐大,不利于維護(hù),所以要把這部分的內(nèi)容分離出去管理,讓view去專(zhuān)注于內(nèi)容的顯示,如此一來(lái),Window就出現(xiàn)了,那么它們的層級(jí)就可以同下圖來(lái)表示。之前網(wǎng)上總是說(shuō)window是一個(gè)抽象的概念,它負(fù)責(zé)管理view,什么的,我初看十分不解,其實(shí)window就是對(duì)應(yīng)一塊surface,任何畫(huà)面的顯示都要在surface上繪畫(huà)出來(lái),所以window的概念可以理解為是上層顯示系統(tǒng)的基礎(chǔ),我們需要它來(lái)顯示畫(huà)面,那么就需要去WMS中申請(qǐng)一個(gè)window,有了Window之后,系統(tǒng)才會(huì)執(zhí)行繪制流程讓我們寫(xiě)的view去顯示出來(lái),該流程后面會(huì)細(xì)說(shuō)。

view的各個(gè)層級(jí)的關(guān)系

? ? 4.層級(jí)關(guān)系確定后,再看它們的關(guān)系,如下面的UML圖:

view的UML圖

? ? ? ? 這個(gè)類(lèi)圖可以這么理解:view通過(guò)ViewParent接口跟View樹(shù)根節(jié)點(diǎn)溝通, 而View樹(shù)根節(jié)點(diǎn)通過(guò)ViewManager這個(gè)接口與WindowManager溝通,而WindowManager又通過(guò)ViewRootImpl當(dāng)中的WindowSession類(lèi)去跟WMS溝通。

? ? ? 我們可以看到,定義了一個(gè)自定義view,或者原生的view,均需要繼承view這個(gè)父類(lèi),其中view有個(gè)重要的成員變量:ViewParent mParent 。在圖中ViewParent是一個(gè)接口,實(shí)現(xiàn)這個(gè)接口的有ViewGroup和ViewRootImpl,那么mParent是指向誰(shuí)呢?答案在代碼中可以找到。ViewParent接口定義了一系列與父節(jié)點(diǎn)溝通的接口函數(shù),子節(jié)點(diǎn)可以通過(guò)這個(gè)接口跟父節(jié)點(diǎn)溝通,既然如此,根據(jù)層次關(guān)系和類(lèi)圖,每個(gè)子view的mParent變量指向的便是自己的父節(jié)點(diǎn),也就是ViewGroup,而ViewGroup這個(gè)父節(jié)點(diǎn)的mParent變量指向的則是ViewRootImpl,這也進(jìn)一步證明了它們的層級(jí)關(guān)系。

? ? ? 管理view的責(zé)任,交給ViewManager這個(gè)接口,里面就三個(gè)函數(shù),分別對(duì)應(yīng)著add,update,remove,由WindowManagerImpl去實(shí)現(xiàn),于是我們獲取到了WindowManager的服務(wù)引用后,通過(guò)下面一段代碼就可以把我們一個(gè)view顯示出來(lái):

? ? ? ? WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

? ? ? ? WindowManager.LayoutParams lp = new WindowManager.LayoutParams();? //布局參數(shù)是用來(lái)定義這個(gè)window的屬性的

? ? ? ? TextView view = new TextView(this);

? ? ? ? view.setText("this is text view");

? ? ? ? wm.addView(view, lp);

? ? ? 我們可以在類(lèi)圖中看到,ViewGroup也實(shí)現(xiàn)了ViewManager的接口,但是它跟WindowManager的表現(xiàn)是不一樣的,它的實(shí)現(xiàn)是管理view樹(shù)的接口,如在addView實(shí)現(xiàn)函數(shù)中,它是把子類(lèi)加入到了自己的view樹(shù)結(jié)構(gòu)里,這其中并沒(méi)有跟WMS的交互。

下面通過(guò)一個(gè)例子來(lái)縷一縷他們的關(guān)系吧!


先看界面,這是我自己的手機(jī),我寫(xiě)的一個(gè)apk推到手機(jī)中,非常簡(jiǎn)單,就是一個(gè)activity,其中有三個(gè)TextView對(duì)象,分別為1,2,3,還自定義了一個(gè)繼承于FrameLayout的MyView,里面啥也沒(méi)干。


實(shí)例

代碼如下,其中MyView為FrameLayout子類(lèi),我在xml文件中用它作為根布局,里面嵌套了一個(gè)TextView,也就是view3.

public class MainActivity extends AppCompatActivity {

? ? MyView myView;

? ? TextView view1;

? ? TextView view2;

? ? TextView view3;

? ? @Override

? ? protected void onCreate(Bundle savedInstanceState) {

? ? ? ? super.onCreate(savedInstanceState);

? ? ? ? setContentView(R.layout.activity_main);

? ? ? ? myView = findViewById(R.id.MyView);

? ? ? ? view3 = findViewById(R.id.text);

? ? ? ? view1 = new TextView(this);

? ? ? ? view1.setText("this is view1");

? ? ? ? view1.setTextColor(Color.RED);

? ? ? ? myView.addView(view1);? ? ? ?

? ? ? ? view2 = new TextView(this);

? ? ? ? view2.setText("this is view2");

? ? ? ? view2.setTextColor(Color.GREEN);

? ? ? ? WindowManager wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE);

? ? ? ? WindowManager.LayoutParams lp = new WindowManager.LayoutParams(300, 200, 2, 0, 0);

? ? ? ? wm.addView(view2, lp);

? ? }

}

? xml文件如下:

<?xml version="1.0" encoding="utf-8"?>

<com.android.myview.MyView

? ? android:id="@+id/MyView"

? ? xmlns:android="http://schemas.android.com/apk/res/android"

? ? xmlns:app="http://schemas.android.com/apk/res-auto"

? ? xmlns:tools="http://schemas.android.com/tools"

? ? android:layout_width="match_parent"

? ? android:layout_height="match_parent"

? ? tools:context=".MainActivity">

? ? <TextView

? ? ? ? android:id="@+id/text"

? ? ? ? android:layout_width="wrap_content"

? ? ? ? android:layout_height="wrap_content"

? ? ? ? android:text="this is view3"

</com.android.myview.MyView>

? ? 問(wèn)題如下:

? 1. 這個(gè)activity里,有幾個(gè)窗口?

? ? 2.view1,2,3,分別屬于哪個(gè)窗口?

? ? 3.都有addView的調(diào)用,那么它們的區(qū)別是什么呢?

如果都回答正確,那么view體系的層級(jí)關(guān)系以及類(lèi)之間的關(guān)系,也就清楚了。

答案如下:

? ? 1. 2個(gè)窗口,即兩個(gè)surface,白色背景的為一個(gè)窗口,這里叫它window1,黑色背景的也為一個(gè)窗口,叫它window2。

? ? 2.view1是通過(guò)MyView的函數(shù)addView()添加的,其中MyView繼承于FrameLayout,F(xiàn)rameLayout繼承于ViewGroup,所以MyView對(duì)象是頂層View,view1自然是依附在window1里面,這里的addView操作是把view1這個(gè)view添加到MyView的這個(gè)view樹(shù)中去,這里并沒(méi)有新的窗口產(chǎn)生。而view2雖然也是一個(gè)TextView,它甚至都不是一個(gè)ViewGroup的子類(lèi),僅僅是單個(gè)view,但是它也可以作為一個(gè)根節(jié)點(diǎn)去通過(guò)WindowManager的addView()接口把自己添加到一個(gè)新窗口中,也就是我們看到的黑色背景window2,所以view2是依附在window2中。至于view3自然也是屬于window1的,因?yàn)樗x在xml文件中,是屬于MyView的子view,它是嵌套在MyView中的。

? ? 3.ViewGroup和WindowManager中都實(shí)現(xiàn)了addView接口,但是它們的邏輯不一樣,也是因?yàn)樗鼈兯幵诘哪K不同導(dǎo)致。ViewGroup是屬于View的,它是View樹(shù)的根節(jié)點(diǎn),調(diào)用addView()是把子view添加到自己的View樹(shù)中,然后重新繪制,這樣view就顯示出來(lái)了,但是沒(méi)有新窗口產(chǎn)生,還是原來(lái)ViewGroup所在的窗口,而WindowManager的addView()則是通過(guò)把傳入的view添加到一個(gè)新創(chuàng)建的window中去顯示,也就是說(shuō)它不屬于任何的ViewGroup,這是它們的本質(zhì)區(qū)別,由此我們也可以看到接口編程的作用,一個(gè)接口,不同的實(shí)現(xiàn)有不同的行為。


總結(jié):

(1). view是通過(guò)view樹(shù)的方式管理的,view樹(shù)里面可以有多個(gè)節(jié)點(diǎn),view樹(shù)的根節(jié)點(diǎn)為頂層view,下面為子view,共同的載體為window,window為一個(gè)surface,即一張畫(huà)布,有了畫(huà)布我們才能把view的內(nèi)容畫(huà)出來(lái),但是view的層級(jí)關(guān)系,如何繪制出來(lái)等,window并不關(guān)心,這使得它們各司其職。

? (2). ViewGroup實(shí)現(xiàn)了ViewParent和ViewManager,ViewRootImpl實(shí)現(xiàn)了ViewParent,WindowManager則實(shí)現(xiàn)了ViewManger,從接口的實(shí)現(xiàn)可以看出它們?nèi)呤莢iew的管理者,但是管理的方式不同:ViewGroup管理的對(duì)象為自己的子view,ViewRootImpl管理的是ViewGroup對(duì)象,子view的parent是ViewGroup,而ViewGroup的parent則為ViewRootImpl。? 而WindowManager管理的是ViewRootImpl,ViewRootImpl的實(shí)例會(huì)在WindowManager模塊中被創(chuàng)建起來(lái)并管理,所以view的層級(jí)關(guān)系為:window > viewroot? > viewgroup > view。

? ? ? 知道了view的體系架構(gòu)以后,接下來(lái)把調(diào)用流程梳理清楚,比如什么時(shí)候觸發(fā)繪制流程,把一個(gè)新的子view添加到view樹(shù)中會(huì)發(fā)生什么,調(diào)用WindowManager的addView()后系統(tǒng)如何創(chuàng)建window,如何實(shí)現(xiàn)一個(gè)自定義view等等,這將會(huì)在:《View體系詳解(2):自定義View流程以及系統(tǒng)相關(guān)行為 》? 中去講解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容