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代码,那些内嵌的代码被查看的可能就更加渺茫了。