圖形用戶界面(GUI)是Android應(yīng)用程序開發(fā)不可或缺的一部分。其不僅能為用戶提供輸入,還能夠根據(jù)(用戶)執(zhí)行的動(dòng)作,提供相應(yīng)的反饋。因此,作為開發(fā)人員,能夠理解UI(用戶界面)是如何創(chuàng)建以及跟新的,就顯得尤為重要。
ViewTree
View 和 ViewGroup 是Android UI的基本組件, 而ViewGroup作為容器,可以包含一組View, 并且ViewGroup其本身就是View的擴(kuò)展?丛创a:
public abstract class ViewGroup extends View implements ViewParent, ViewManager{}
而各種不同的Widgets 像TextView, Button 等等 也是View的擴(kuò)展,只不過是放在各種Layout里,比如LinearLayout, RelativeLayout。而Layout卻是ViewGroup的子類。所以說一個(gè)ViewTree只不過是各種Views和ViewGrouPS放在一個(gè)Layout里組成的樹形結(jié)構(gòu)。
有例子才有真相。通過eclipse的Outline窗口,我們可以看下下面這個(gè)樹狀布局。
XML Code:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/RelativeLayout1" android:layout_width="fill_parent" android:layout_height="fill_parent" > <View android:id="@+id/WhiteView" android:layout_width="200dp" android:layout_height="300dp" android:layout_marginLeft="20dp" android:background="#ffffff" /> <TextView android:id="@+id/RedText" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="@string/hello" android:textColor="#ff0000" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/GrayView" android:layout_alignLeft="@+id/GrayView" android:layout_marginBottom="25dp" android:background="#0000ff" android:orientation="vertical" > <TextView android:id="@+id/GreenText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" android:textColor="#00ff00" android:textStyle="bold" /> <Button android:id="@+id/Button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button in LinearLayout" /> </LinearLayout> <View android:id="@+id/RedView" android:layout_width="100dp" android:layout_height="100dp" android:layout_alignParentRight="true" android:layout_above="@+id/GrayView" android:layout_marginRight="10dp" android:layout_marginBottom="-10dp" android:background="#ff0000" /> <View android:id="@+id/GrayView" android:layout_width="200dp" android:layout_height="300dp" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginRight="20dp" android:background="#cccccc" /> </RelativeLayout>
當(dāng)我們?cè)贏ctivity里,用setContentView()去設(shè)置這個(gè)view,然后運(yùn)行,我們可以看到如下圖:
仔細(xì)觀察XML文件,以及頁(yè)面渲染的View,我們會(huì)發(fā)現(xiàn):
1. ViewTree 是以一種自上而下的方式進(jìn)行遍歷實(shí)現(xiàn)。
2. Parent總是最先繪制的,其實(shí)才是Children,并且仍然遵循自上而下的方式。
所以在我們的例子中,RelativeLayout是最先繪制,接著是其孩子WhiteView,RedText 等等,
直到最終GrayView繪制,并且我們會(huì)看到后繪制的覆蓋了部分先繪制的。
為了更方便觀察這些Views是怎么繪制的,我們把手機(jī)屏幕看作下面的X,Y,Z的坐標(biāo)系。屏幕的左上角作為[0,0,0],X軸向右,Y軸向下沿著屏幕的長(zhǎng)度,Z軸延伸出屏幕。
所以說,當(dāng)我們遍歷Tree的時(shí)候,這些Views基本上就是沿著Z軸排放。這里需要注意,當(dāng)View有部分被遮擋時(shí),Android就不會(huì)再繪制這被遮擋的部分。比如上圖,灰色遮擋部分紅色,Android繪圖機(jī)制就不會(huì)再繪制那部分紅色,所以我們看到的是直接遮擋,而不是顏色的混合。
現(xiàn)在我們知道了我們?cè)赬ML里定義的Views是如何繪制的了,但是這還不夠,我們可以借助一個(gè)非常有用的工具Heirarchyviewer去更深層細(xì)的觀察頁(yè)面布局。
Heirarchyviewer 在文件夾android-sdk/tools下,在命令行下找到這個(gè)文件夾,然后執(zhí)行heirarchyviewer.bat 就可以了。
下圖是Hierarchy Viewer的截圖:
在Hierarchy Viewer里,列代表樹的深度,而每一列里行的數(shù)量則表示每一層的深度。從圖上我們能注意到RelativeLayout并不是Root級(jí)別的,而是id為content的FrameLayout的一個(gè)子元素。而實(shí)際山我們調(diào)用setContentView(View v)里的View v 就是這個(gè)content視圖。
現(xiàn)在注意下跟content同級(jí)的FrameLayout有個(gè)子TextView,實(shí)際上它既是Activity的titleBar。那么我們刪除這個(gè)TitleBar之后,View Tree又會(huì)變成什么樣子呢?
方法:在manifest文件,然后修改application的主題如下:
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
這樣再打開Hierarchy Viewer,我們就能看到下圖:
(注意:本人選用的Android是2.2的,要是用4.1的話,并不能得到下圖,中間還會(huì)多一個(gè)Id為action_menu_bar_stub的ViewStub)
這時(shí)候我們看到content FrameLayout的父元素是PhoneWindow$DecorView。
DecorView
我們知道在Android中抽象類Window定義了最上層窗口的基本外觀以及基本行為,她的實(shí)例將會(huì)被加到WindowManager中,提供一些標(biāo)準(zhǔn)的UI策略,像Background,Titlebar,以及Default key processing等等,當(dāng)然這些屬性是可以通過WindowManager.LayoutParams定制的。
而上面提到的PhoneWindow是Window抽象類的唯一實(shí)現(xiàn),即android.policy.PhoneWindow。而DecorWindow是PhoneWindow的一個(gè)私有內(nèi)部類,其實(shí)就是一個(gè)FrameLayout的擴(kuò)展。
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {}
就是這個(gè)類構(gòu)成了最上層應(yīng)用程序視圖。根據(jù)我們?cè)贛anifest中設(shè)置的Theme或者在PhoneWindow設(shè)置的Flags,來確定DecorView的Layout。所以在我們的例子中,第一張中我們有個(gè)簡(jiǎn)單的主題(在Manifest中)包含一個(gè)titlebar和contentview,于是PhoneWindow就生成了包含Title的LinearLayout,以及放置content的FrameLayout。而在第二張圖中,我們?nèi)サ袅藅itlebar主題,所以她就只生成了包含F(xiàn)rameLayout的DecorView了。
結(jié)論
最后我們來總結(jié)下,當(dāng)一個(gè)Activity被啟動(dòng)的時(shí)候,這個(gè)視圖樹(View Tree)大體是如何創(chuàng)建的呢:
1. PhoneWindow根據(jù)Manifest的主題或者是特定的PhoneWindow設(shè)置去生成一個(gè)DevorView的布局,作為跟視圖(Root View)。
2. Activity調(diào)用setContentView()方法把用戶自定義的Layout XML文件作為內(nèi)容視圖(Content View), 當(dāng)然其內(nèi)部是調(diào)用PhoneWindow的setContentView()方法。
3. 經(jīng)過上兩步,UI視圖就已經(jīng)形成了,那么當(dāng)UI每次被刷新的時(shí)候,View Tree就會(huì)像上面所說的那樣被Traverse。