android.view.View类(视图类)呈现了最基本的UI构造块。一个视图占据屏幕上的一个方形区域,并且负责绘制和事件处理。
Android中控件类的扩展结构如图3-1所示。
图3-1 Android中控件类的扩展结构
View有众多的扩展者,它们大部分是在android.widget包中,这些继承者实际上就是Android系统中的“控件”。View实际上就是各个控件的基类,创建交互式的图形用户界面的基础。
View的直接继承者包括文本视图(TextView)、图像视图(ImageView)、进度条(ProgressBar)等。它们各自又有众多的继承者。每个控件除了继承父类功能之外,一般还具有自己的公有方法、保护方法、XML属性等。
在Android中使用各种控件的一般情况是在布局文件中可以实现UI的外观,然后在Java文件中实现对各种控件的控制动作。控件类的名称也是它们在布局文件XML中使用的标签名称。
2.控件通用行为和属性View是Android中所有控件类的基类,因此View中一些内容是所有控件类都具有的通用行为和属性。
提示:由于Java语言不支持多重继承,因此Android控件不可能以基本功能的“排列组合”的方式实现。在这种情况下,为了实现功能的复用,基类的功能往往做得较强,作为控件的祖先类,View所实现的功能也是最多的。
控件类经常在布局文件中使用,因此其可以使用XML属性(XMLAttributes),和Java代码经常具有对应关系。
View作为各种控件的基类,其XML属性所有控件通用,几个重要的XML属性如表3-1所示。
表3-1 View中几个重要XML属性及其对应的方法
XML属性
Java中的方法
描 述
android:id
setId(int)
控件的标识
android:visibility
setVisibility(int)
控件的可见性
android:background
setBackgroundResource(int)
控件的背景
其中,android:id表示控件的标识,通常需要在布局文件中指定这个属性。View中与控件标识相关的几个方法如下所示:
public int getId() // 获得控件的id(int类型)
public void setId(int id) // 设置控件的id(int类型)
public Object getTag() // 获得控件的tag(Object类型)
public void setTag(Object tag) // 设置控件的tag(Object类型)
对于一个控件,也就是View的继承者,整数类型id是其主要的标识。其中,getId()可以获得控件的id,而setId()可以将一个整数设置为控件的id,但是这个方法并不常用。View的id通常可以在布局文件中获得。
Object类型的标识tag是控件的一个扩展标识,由于使用了Object类型,它可以接受大部分的Java类型。
在一个View中根据id或者tag查找其孩子的方法如下所示:
public final View findViewById(int id)
public final View findViewWithTag(Object tag)
findViewById()和findViewWithTag()的目的是返回这个View树中id和tag为某个数值的View的句柄。View树的含义是View及其所有的孩子。
值得注意的是,id不是控件的唯一标识,例如布局文件中id是可以重复的,在这种重复的情况下,findViewById()的结果不能确保找到唯一的控件。
提示:作为控件的标识的id和tag可以配合使用:当id有重复的时候,可以通过给控件设置不同的tag,对其进行区分。
可见性的问题,android:visibility在布局文件中有三个数值:visible(可见,默认),invisible(不可见),gone(去除)。在Java代码中,setVisibility()能使用的枚举值与其对应,它们是:View.VISIBLE(0x0),View.INVISIBLE(0x4),View.GONE(0x8)。
参考示例程序:Visibility(ApiDemo=>Views)
源代码:com/example/android/apis/view/ visibility_1.java
布局文件:visibility_1.xml
Visibility程序的运行效果如图3-2所示。
图3-2 Visibility程序(左:可见;中:不可见;右:去除)
对于文字为View B的文本框,分别使用了visible、invisible和gone设置。invisible和gone的区别在于invisible只是不可见,但是依然占位,gone表示将控件去除,显示的效果就像没有这个控件存在。
和View形态相关的几个方法如下所示:
public void invalidate () // 使无效(重新绘制)
public void requestLayout () // 申请重新布局
public final boolean requestFocus () // 申请聚焦
这几个方法都和View的显示形态有关:invalidate()方法的功能是使得无效,用于重新绘制当前的View;requestLayout()用于更新View树,也就是由当前View的大小位置变化更新与其相关的View;requestFocus()用于申请当前的聚焦。
查找聚焦的View的方法如下所示:
public View findFocus () // 找到聚焦的View
在布局文件中,如果在一个控件的标签中使用
这里android:nextFocusUp和android:nextFocusDown分别是上下按键的时候,下一个聚焦的控件的id。
è 3.2.1 Android中的视图组在屏幕中控件的组织上,可以将各个视图(控件)组成一个视图组(ViewGroup),视图组是一个包含了其他视图的视图。
1.视图组(ViewGroup抽象类)
android.view包中ViewGroup类继承了View,因此它本身也具有View的特性。ViewGroup主要的功能在于它可以包含其他控件,作为其他控件的容器。
ViewGroup实现了android.view包中的ViewParent接口,这个类表示一个可以作为其他的View的容器的职责。
ViewGroup也实现了android.view包中的ViewManager接口,因此包含ViewManager中的以下几个方法:
public abstract void addView(View view, ViewGroup.LayoutParams params)
public abstract void removeView(View view)
public abstract void updateViewLayout(View view, ViewGroup.LayoutParams params)
addView()以View作为参数,用于将这个View增加为当前视图组的“孩子”;removeView()用于将一个View从视图组中移除;updateViewLayout()用于更新某个View的布局。
ViewGroup.OnHierarchyChangeListener是一个接口,用于监听ViewGroup中的View的层次变化。这个接口中包含了两个监听方法:
public abstract void onChildViewAdded (View parent, View child)
public abstract void onChildViewRemoved (View parent, View child)
实现一个OnHierarchyChangeListener接口后,这两个方法可以监听包括ViewGroup(包括其继承者)之中的增加和删除孩子的情况。通过ViewGroup的setOnHierarchyChangeListener()方法可以将其设置给一个ViewGroup。
ViewGroup是一个抽象类,其中包含了以下的一个抽象方法:
protected abstract void onLayout (boolean changed, int l, int t, int r, int b)
ViewGroup中的onLayout()方法将在ViewGroup为它的孩子们分配尺寸和位置的时候被调用,在这个类的实现中,需要调用每一个控件的布局方法为其布局。
提示:onLayout()在View中是一个public的方法,在ViewGroup为protected类型,并且为abstract,由于这个方法在ViewGroup中没有实现,因此ViewGroup本身不可以直接使用。
2.Android的屏幕元素体系Android UI程序的屏幕体系结构的组织遵循以下原则:
Android视图和视图组的关系如图3-12所示。
图3-12中左右两图表示的一个屏幕中View组织的结构,左图是各个视图组和控件在一个屏幕之中的示意图;右图为视图组的层次结构图(View Hierarchy),这也是Android中一种常用的表示屏幕中布局的方式。
如图3-12所示,外部最大的框表示整个屏幕,其中包含一个视图组ViewGroup0,ViewGroup0包含三个子视图,即View1、ViewGroup1、ViewGroup2。ViewGroup1本身也是视图组,包含了View2和View3;ViewGroup2本身也是视图组,包含了View4、ViewGroup3和ViewGroup4;ViewGroup4本身也是视图组,包含了View5和View6。
图3-12 视图和视图组的关系(左:屏幕示意图;右:View的层次结构图)
根据以上的原则,当屏幕需要包含多个视图时,必须组织在一个视图组中。由于视图组本身也是一个视图,因此视图组还可以包含视图组。
一个主要的限制是:在没有视图组的情况下,两个以上的视图(也包括视图组)是不能够并列的。例如,在布局文件中,类似下面的写法是不可以的。
ViewGroup是所有视图组的基类,它本身是一个不能直接使用的抽象类。在程序中使用的主要是ViewGroup的继承者。
ViewGroup的继承者大部分在android.widget包中,其直接继承者包括:AdapterView、AbsoluteLayout、FrameLayout、LinearLayout、RelativeLayout。这些继承者各自又具有一些继承者。
ViewGroup继承者的体系结构如图3-13所示。
图3-13中所示的ViewGroup继承者都是可以作为容器使用的。这些继承者可以为它们的孩子们提供不同的布局方法,用以确定孩子们相互之间的位置和尺寸关系。
这些继承者又具有一些继承者,有些继承者是不作为容器使用的,仅仅是像一个普通控件一样使用。
图3-13 视图组的继承结构
4.布局参数类在Android中每个控件在布局文件中能使用的XML属性其实有三类:
其中,布局参数是包含这个控件的容器(一个ViewGroup的继承者)所提供的参数。在Android中,每一个ViewGroup的继承者都有一个相对应的名称为{XXX}.LayoutParams的静态子类,表示这个ViewGroup的孩子们中可以使用的布局参数。
这些布局参数类的基类为ViewGroup.LayoutParams,这是一个ViewGroup的静态子类,表示ViewGroup的子对象中可以使用参数。
ViewGroup.LayoutParams包含有两个重要的XML属性:android:layout_width和android:layout_height,它们表示布局对象的宽和高。除了使用实际的尺寸数值外,还有如下两个常用的选项。
“match_parent”或者“fill_parent”:表示能匹配父视图的最大尺寸;
ViewGroup.MarginLayoutParams是ViewGroup.LayoutParams的一个继承者,主要用于定义和边缘的空白,它具有android:layout_marginTop,android:layout_marginLeft,android:layout_marginBottom,android:layout_marginRight几个XML属性,表示四个方向的边缘空白。
在ViewGroup的继承者中,都具有一个名称为
图3-14 视图组布局参数的结构
从图3-14中可见,LinearLayout的布局参数LinearLayout.LayoutParams可以被这个布局的孩子们使用,RelativeLayout的布局参数RelativeLayout.LayoutParams可以被这个布局的孩子们使用。但是这里“孩子”的含义,只包括这个容器直接的孩子。例如,图3-14中LinearLayout的孩子有一个是RelativeLayout,RelativeLayout的孩子中能使用的参数就不包括线性布局的参数LinearLayout.LayoutParams。
提示:由于LayoutParams也具有继承关系,因此LinearLayout的孩子们除了可以使用LinearLayout.LayoutParams自己的XML属性,还可以使用其祖先类ViewGroup.LayoutParams的XML属性。