Android UI框架提供了一套完整的繪圖工具來構建UI,以及豐富的基於這些工具的預構建的組件組合。正如第8章將介紹的,Android UI框架的圖形工具為應用提供了很多支持,以便應用可以方便地創建自己的控制按鈕或渲染出特殊的視圖。另一方面,很多應用可能只使用工具箱裡的視圖就可以良好工作。實際上,類MapActivity和類MyLocationOverlay可以支持非常複雜的應用的創建,而不需要執行任何自定義渲染。
我們之前已經使用了術語部件,但沒有顯式定義它。屏幕是由組件樹渲染的。在Android UI框架中,這些組件都是android.view.View的子類。在視圖樹中,葉子節點或接近葉子節點的節點執行了大部分的實際渲染工作,在應用UI中,這些節點通常被稱為widget。
內部節點,有時稱為容器視圖(container views),是特殊的組件,可以包含其他子組件。在Android UI框架中,容器視圖是android.view.ViewGroup的子類,它當然也是View的子類。它們通常很少執行渲染工作。相反,它們主要負責安排其子節點在屏幕上的視圖,並且當視圖形狀、方向等改變時,還保持這些節點的位置排列。這些工作實際上很複雜。
要創建複雜的顯示,還需要對應用中要使用的視圖的容器樹進行組裝。例6-1顯示了一個包含三個層次的視圖樹的應用。一個垂直的線性佈局中包含了兩個水平的線性佈局。每個水平佈局又分別包含了兩個部件。
例6-1:一棵複雜的視圖樹
package com.oreilly.android.intro; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.view.Gravity; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; public class AndroidDemo extends Activity { private LinearLayout root; @Override public void onCreate(Bundle state) { super.onCreate(state); LinearLayout.LayoutParams containerParams = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0.0F); LinearLayout.LayoutParams widgetParams = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 1.0F); root = new LinearLayout(this); root.setOrientation(LinearLayout.VERTICAL); root.setBackgroundColor(Color.LTGRAY); root.setLayoutParams(containerParams); LinearLayout ll = new LinearLayout(this); ll.setOrientation(LinearLayout.HORIZONTAL); ll.setBackgroundColor(Color.GRAY); ll.setLayoutParams(containerParams); root.addView(ll); EditText tb = new EditText(this); tb.setText(R.string.defaultLeftText); tb.setFocusable(false); tb.setLayoutParams(widgetParams); ll.addView(tb); tb = new EditText(this); tb.setText(R.string.defaultRightText); tb.setFocusable(false); tb.setLayoutParams(widgetParams); ll.addView(tb); ll = new LinearLayout(this); ll.setOrientation(LinearLayout.HORIZONTAL); ll.setBackgroundColor(Color.DKGRAY); ll.setLayoutParams(containerParams); root.addView(ll); Button b = new Button(this); b.setText(R.string.labelRed); b.setTextColor(Color.RED); b.setLayoutParams(widgetParams); ll.addView(b); b = new Button(this); b.setText(R.string.labelGreen); b.setTextColor(Color.GREEN); b.setLayoutParams(widgetParams); ll.addView(b); setContentView(root); } }
注意,該代碼保留了視圖樹的根節點的引用,在後面會用到它。
這個例子使用了三個LinearLayout視圖。LinearLayout視圖,正如其名,表示在視圖中根據定位屬性,以行形式或列形式顯示子節點。子視圖的顯示方式和其插入到LinearLayout的順序一致(和其創建的順序無關),其顯示方式對於西方讀者很熟悉:從左到右、至上而下。例如,標籤為Green的按鈕在該佈局的右下角,因為它是第二個插入到水平LinearLayout視圖中的,而這個水平線性視圖又是第二個插入到根節點即垂直的LinearLayout中的。
圖6-2顯示了從用戶角度看的可能結果。樹中的7個視圖是結構化的,如圖6-3所示。
圖6-2:用戶看到的面板
Android框架支持很方便地把數據源和代碼進行分離,這一點對於構建視圖佈局是非常有用的。前一個例子可以被例6-2所示的簡單得多的代碼以及例6-3所示的視圖佈局XML定義所替換。
圖6-3:視圖中的層次
例6-2:使用佈局資源的複雜視圖
package com.oreilly.android.intro; import android.app.Activity; import android.os.Bundle; /** * Android UI demo program */ public class AndroidDemo extends Activity { private LinearLayout root; @Override public void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.main); root = (LinearLayout) findViewById(R.id.root); } }
例6-3:一個複雜視圖佈局資源的XML定義
<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" android:id=\"@+id/root\" android:orientation=\"vertical\" android:background=\"@drawable/lt_gray\" android:layout_ android:layout_> <LinearLayout android:orientation=\"horizontal\" android:background=\"@drawable/gray\" android:layout_ android:layout_> <EditText android:id=\"@+id/text1\" android:text=\"@string/defaultLeftText\" android:focusable=\"false\" android:layout_ android:layout_ android:layout_weight=\"1\"/> <EditText android:id=\"@+id/text2\" android:text=\"@string/defaultRightText\" android:focusable=\"false\" android:layout_ android:layout_ android:layout_weight=\"1\"/> </LinearLayout> <LinearLayout android:orientation=\"horizontal\" android:background=\"@drawable/dk_gray\" android:layout_ android:layout_> <Button android:id=\"@+id/button1\" android:text=\"@string/labelRed\" android:textColor=\"@drawable/red\" android:layout_ android:layout_ android:layout_weight=\"1\"/> <Button android:id=\"@+id/button2\" android:text=\"@string/labelGreen\" android:textColor=\"@drawable/green\" android:layout_ android:layout_ android:layout_weight=\"1\"/> </LinearLayout> </LinearLayout>
該版本的代碼和第一個版本的類似,也保留了到視圖樹的根節點的引用。它保留根節點引用的方式是在XML佈局(在這個例子中,即根節點LinearLayout)中給widget打上一個標籤android:id,然後使用Activity類的findViewById方法來獲取引用。
使用資源來定義視圖樹的佈局結構是一個良好的思路。這種方式使我們能夠從代碼中分離出視圖的可視化佈局。修補屏幕佈局就不再需要重新編譯了。然而,更重要的是,你可以使用一些工具來構建自己的UI,採用這種思路你就可以使用可視化UI編輯器編寫屏幕元素。
注意:在Google I/O 2011會議上,Android工具團隊介紹了一種新的編輯器,其功能很令人興奮。它甚至可以預覽動畫和開發人員創建的視圖。大部分開發人員在對視圖進行佈局時不需要查看XML代碼,那些內嵌的代碼被查看的可能就更加渺茫了。