构造自定义组件

Android提供了一种高级而强大的自定义模型来构建UI,这一模型基于布局类View和ViewGroup来实现。首先,Android平台包含了一系列的预构建View和ViewGroup子类——被称为控件(widget)和布局(layout)——用以构造UI。

可用的控件包括但不限于Button、TextView、EditText、ListView、CheckBox、RadioButton、Gallery、Spinner及一些特殊用途的AutoCompleteTextView、ImageSwitcher和TextSwitecher。

可用的布局包括LinearLayout、FrameLayout、RelativeLayout等。范例请参见“常见布局对象”。

如果预构建的控件或布局无法满足需要,可以创建自有的View子类。如果只是要对现有的控件或布局作小调整,可以继承该控件或布局并覆盖其方法。

通过创建自有的View子类,将可以对于屏幕元素的外观和功能进行精确控制。要了解能够对自定义视图进行哪些控制,可以参考下面的例子:

  • 可以创建一个完全自定义渲染的View类型,例如使用2D图像渲染的有些类似于模拟电路控制的“音量控制”按键。(自己也不太清楚analog electronic control是什么,总之这个例子是说可以自定义按键的外观)
  • 可以将一组View组件结合为一个新的单独的组件,比如类似于ComboBox(一种弹出式列表和文本输入栏的结合体)的东西,或是双栏选择器(一种有左右两栏的列表,可以重新分配其中的某个项目应该属于哪一栏),等等。
  • 可以覆盖EditText组件在屏幕上的渲染方式(Notepad教程中通过这种方式创建了具有划线的记事本页面,取得了良好效果)
  • 可以捕捉例如按键等的其他事件并以自定义方式处理它们(例如在游戏中)。

下面的小节阐述了如何创建自定义View并将其用于程序之中。更为详细的参考信息,请参见View类。

基本方式

这里是对要创建自有View组件所需了解的内容的一个高度预览:

  •  用自有类继承一个View类或其子类。
  • 覆盖其中的一些方法。需要覆盖的是那些父类中以“on”开头的方法,例如,onDraw(),onMeasure()和onKeyDown()。这和在Activity或是ListActivity中覆盖那些和生命周期与功能相关on…事件相类似。
  • 使用该新类。一旦完成,自有的新类可以被用于其父类能被用于的地方。

 提示:继承的类可以在使用它们的活动中被定义。这样就限制了对其不必要的使用,很有用(因为可能会为了更广泛的用途而需要在程序中创建另一个新的公用View)。

 完全自定义的组件

完全自定义的组件可以被用来创建任何希望呈现的图形组件。也许是一个看起来像是老式类比指示条的音量指示器,或是卡拉OK机上唱歌时会有一个随着歌曲的进度弹跳着的小球的一长条的文本视图。总之,有时无论怎样组合预置组件也无法达到某种效果,因而需要另外的自定义组件。

幸运地,可以轻松地创建具有所希望的外观和行为的组件,仅有的限制只是想象力,屏幕尺寸,可用的处理器能力(要记得最终程序不得不运行于比桌面工作站性能弱很多的设备上)。

要创建一个完全自定义的组件:

  1. 也许有些出乎意料,通常要继承的视图正是View,因此创建新的组件往往以继承View类开始。
  2. 可以提供一个构造函数以从XML中获取属性和参数,也可以自己定义这些属性和参数(比如音量指示器的色彩和范围,指针的宽度和阻尼等。)
  3. 有可能需要为该组件创建自有的事件监听器,属性继承器和修改器,或是其他更为深入的功能。
  4. 通常都需要覆盖onMeasure()方法,如果希望组件显示内容,常常onDraw()方法也要覆盖。这两者都具有其默认行为,默认来说onDraw()什么也不做,而onMeasure()则总是设置尺寸为100×100——这可能不是所希望的属性。
  5. 如有需要其他的on…方法也应该被覆盖。

 继承onDraw()和onMeasure()

onDraw()方法将传递一个Canvas,在该Canvas上可以实现任何所需内容:2D图形、其他自定义组件、个性化的文本内容或其他所能想到的。

注意:这并不适用于3D图形。如果希望使用3D图形,必须继承SurfaceView而不是View,并在另一个线程中绘制。参见GLSurfaceViewActivity范例以了解更多。

onMeasure()则稍微复杂一些。onMeasure()是组件与其容器之间的一种渲染约束。它应该被覆盖以高效而准确地报告其所包含内容的尺寸度量。由于其父级(它被传递给onMeasure()方法)限制的要求,又由于一旦完成计算后需要以测量的宽度和高度来调用setMeasuredDimension(),整件事变得有些复杂。如果没能通过一个被覆盖的onMeasure()方法来调用它的话,结果将会是在测量时抛出一个例外。

从整体上来看,实现onMeasure()大致是这样的:

  1. 被覆盖的onMeasure()方法被以宽度和高度的测量值(即widthMeasureSpec以及heightMeasureSpec参数,它们都是用来表示维度的整型值)调用,这是对于要处理的宽度和高度的测量值的一种限制。这些参数能够要求的所有限制的参考资料请见View.onMeasure(int, int)相关的文档(该参考文档同时很好的解释了整个测量操作)。
  2. 组件的onMeasure()方法应当计算出渲染该组件所需的宽度和高度的测量值。它通常应当和传入的参数相匹配,不过有时也可以选择扩大(这时,父类可以根据不同的测量值选择包括切割、滚动、抛出例外或请求onMeasure()重试等一系列操作。)
  3. 一旦宽度和高度被计算出来,就必须以它们去调用setMeasuredDimension(int width, int height)。否则将导致抛出例外。

下面是关于视图的框架可能调用的其他标准方法的一些总结:

类别 方法 描述
创建 构造函数 在通过代码创建视图时和在视图通过布局文件生成时都会需要一些构造函数。第二种情况下布局文件中定义的任何属性都需要被读取并应用。
onFinishInflate() 当一个视图及其所有子视图都通过XML生成后被调用。
布局 onMeasure(int, int) 调用以决定该视图及其所有子视图所需的尺寸。
onLayout(boolean, int, int, int, int) 当该视图需要指定其所有子视图的尺寸和位置时被调用。
onSizeChanged(int, int, int, int) 当该视图的尺寸发生变化时被调用。
绘制 onDraw(Canvas) 当该视图需要渲染其内容时被调用。
事件处理 onKeyDown(int, KeyEvent) 当一个新的按键事件发生时被调用。
onKeyUp(int, KeyEvent) 当一个按键释放事件发生时被调用。
onTrackballEvent(MotionEvent) 当一个轨迹球移动事件发生时被调用。
onTouchEvent(MotionEvent) 当一个触摸屏动作事件发生时被调用。
焦点 onFocusChanged(boolean, int, Rect) 当视图获得或是失去焦点时被调用。
onWindowFocusChanged(boolean) 当包含了该视图的窗口获得或是失去焦点时被调用。
关联(Attaching) onAttachedToWindow() 当该视图与一个窗口相关联后被调用。
onDetachedFromWindow() 当该视图与窗口接触关联后被调用。
onWindowVisibilityChanged(int) 当包含了该视图的窗口的可见性发生改变时被调用。

 一个自定义视图的范例

在API Demos中的CustomView范例提供了一个自定义View的范例。该自定义View被定义在LabelView类中。

这个LabelView范例对自定义组件的各方面进行了演示:

  • 继承View类来创建一个完全自定义的组件。
  • 支持参数并可以接受视图生成参数(在XML中定义的参数)的构造函数。其中一些参数将被传递至父类View,不过更重要的是,一部分参数将被用于在LabelView中定义的属性。
  • 一些该标签组件所需的标准公有方法,比如setText()setTextSize()setTextColor()等等。
  • 被覆盖的onMeasure()方法以决定并设置组件的渲染尺寸。(注意在LabelView中,实际的工作是由私有的measureWidth()方法完成的。)
  • 被覆盖的onDraw()方法以将标签绘制于所提供的画布(canvas)上。

可以在范例中的custom_view_1.xml里看到一些LaberView自定义视图的用例。通常,可以看到android: namespace参数和自定义的app: namespace参数被混合使用。app: parameters是LabelView进行组织并使用的参数,被定义于该范例的R资源定义类中的风格化内部类中。

复合控制器(Compound Control)

如果不想创建一个完全自定义的组件,而是希望将一些可重用的组件组合在一起以获得一组已存在的控制功能的话,就创建一个复合组件(或称为复合控制器)吧。简言之,就是将一些更为基本的控制器(或是视图)装入一个逻辑组使之被看作为是一个单独的组件。例如,一个Combo Box可以被认为是一条单独的EditText域加上一个具有弹出式列表(PopupList)的调节按钮。如果按下按钮并从列表中进行选择,就会弹出一条EditText域,当然用户也可以直接在EditText中键入一些内容。

在Android中,其实已经有两种View可以做到这点了:Spinner和AutoCompleteTextView,不过Combo Box的概念还是可以用来做为一个简单易懂的范例的。

要创建一个复合组件:

  1. 这通常是从某种类型的Layout开始,所以需要创建一个继承了某种Layout的类。在Combo box的例子中或许应该使用一个水平方向的LinearLayout。请记得其他的布局是可以嵌套于其中的,所以复合组件可以做得相当地结构复杂。需要注意的是还可以像Activity所使用的那种方式一样通过声明式(基于XML)的方式来创建所包含的组件,或是在代码中对其进行嵌套。
  2. 在新类的构造函数中,获取父类所需的所有参数,并首先将其传递给父类的构造函数。之后可以设置其他在新的组件中所要使用的视图;在这里将创建EditText域和PopupList。请注意可能还需要向XML引入一些自有属性和参数用以被提取并使用于构造函数。
  3. 还可以为所包含的视图可能会触发的事件创建监听器,例如,一个当进行了列表选择后对EditText的内容进行更新的列表项目点击监听器(List Item Click Listener)的监听器方法。
  4. 还可以通过访问器(accessor)和修改器(modifier)创建自有的属性,例如,允许EditText的值在组件中被初始化并在需要时可以查询获取其内容。
  5. 在继承一个Layout时,不必覆盖其onDraw()onMeasure()方法,因为该布局的默认行为就能很好的实现效果。不过,如有需要还是可以对其进行覆盖。
  6. 可以覆盖其他的on…方法,例如onKeyDown(),用于某个键被按下时从一个combo box的弹出列表中选择某一个默认值。

总之,使用Layout作为自定义控制器的基础将带来一系列的好处,包括:

  • 可以通过和活动一样的声明式的XML文件来指定布局,或是可以在代码中创建视图并将其嵌入布局中。
  • onDraw()onMeasure() 方法(加上大部分其他的on…方法)的默认行为能正常工作因而不必进行覆盖。
  • 最后,可以很快捷地构建非常复杂的复合视图并重复利用它们。

复合控制器的范例

在SDK附带的API Demos项目中,有两个List范例——Views/Lists下的范例4和范例6演示了一个继承了LinearLayout的SpeechView,它构建了一个显示Speech引用的组件。范例代码中所用的相关的类是List4.javaList6.java

修改一个已有的View类型

在某些情况下可以用一种更简单的创建自定义View的方法。如果已经有了一个和所希望创建的组件非常相似的组件,可以继承该组件并覆盖那些希望改变的行为。可以像对完全自定义的组件那样对这个继承的组件进行任何处理,只不过是需要从View层级中一个更为特殊化的类开始罢了。与此同时还可以额外获得其他很多所需要的功能。

例如,SDK在范例中包含了一个NotePad程序。它演示了使用Android平台的许多方面,包括继承一个EditText View来实现带有划线的记事本。这并不是一个完美的范例,且实现这个效果的API相对这个比较早期的版本可能会发生改变,不过它确实展示了一些使用原则。

如果还没有尝试过,那就将NotePad范例导入到Eclipse(或只是查看所提供的链接中的源代码)。特别是查看NoteEditor.java文件中MyEditText的定义。

其中一些要点如下。

1. 定义

该类由以下代码定义:

public static class MyEditText extends EditText

  • 它被定义为NoteEditor活动的一个内部类,不过因为它是公有的所以可以在需要时从NoteEditor外部以NoteEditor.MyEditText被使用。
  • 它是静态的,意味着它不会生成所谓的“合成方法(synthetic method)”以允许它读取其父类的数据。这同时也意味着它是一个具有独立行为的类,并没有和NoteEditor保持着高度相关性。这是创建不需要读取外部类的数据的内部类时一种比较条理清楚的做法,它使得生成的类较小且能被其他类所使用。
  • 在这里EditText被选作需要自定义的View来被它继承。当完成后,一个新的类可以用来替代通常的EditText视图。

2. 类初始化

和往常一样,super被首先调用。另外,这不是一个默认的构造函数,而是包含了一些的参数。EditText在通过XML布局文件生成时将用到这些参数,因此,新的构造函数也要获取这些参数并将其传递给父类的构造函数。

3. 覆盖方法

在该范例中,只覆盖了一个方法:onDraw() —— 不过当创建自己的自定义组件时很可能需要覆盖更多方法。

对于NotePad范例来书,覆盖onDraw()方法使得能够在EditText视图的画布(canvas)上绘制蓝线(画布被传递给了被覆盖的onDraw()方法)。super.onDraw()方法在该方法结束前会被调用。这一父类方法的调用是必需的,不过在这里,它在完成了所希望的直线的绘制后才被最后执行。

4. 使用自定义组件

现在已经有了自定义的组件,不过该如何使用呢?在NotePad范例中,自定义组件被直接从声明式的布局中使用,所以现在来看下res/layout文件夹下的note_editor.xml

*该自定义组件作为通用视图在XML中被创建,且该类被指定为使用整个包。还要注意所定义的内部类通过NoteEditor$MyEditText标识来引用,这是Java编程语言中引用内部类的标准方式。

如果自定义的View组件没有作为内部类被定义,那么还可以用XML元素名称来声明该View组件而不包括class属性。例如:

注意现在的MyEditText类是一个单独的类文件。当这个类被嵌套于NoteEditor类中时,这样的做法将会有问题。

*定义中的其他属性和参数将被传递至自定义组件的构造函数,之后被传入EditText的构造函数,即这些也是EditText所需要使用的参数。注意还可以增加自有参数,之后还会涉及这一问题。

该范例的说明至此。确实这只是个简单的例子,但它演示了关键点——可以根据需求创建任何复杂的自定义组件。

一个更为复杂的组件可能需要覆盖更多的on…方法并且引入一些自有的方法以完全自定义其属性和行为。唯一的限制就仅仅是想象力和实际需要组件去完成怎样的工作。

 返回用户界面

本页部分内容根据Android Open Source Project创作并共享的内容修改,并在知识共享 署名2.5许可协议中所述条款的限制下使用。

常见布局对象

这一节将描述一些在程序中较为常用的布局对象类型。和其他布局一样,它们是ViewGroup的子类。另请参阅“Hello Views”教程以获取使用Android View布局的更多指导。

FrameLayout

FrameLayout是最为简单的布局对象类型。简单说它是屏幕上的一块空白空间,之后可以在其中填充单个对象——例如,一张需要读取的图片。所有FrameLayout的子元素位于屏幕的左上角;不能为子视图指定另外的位置。后继的子视图将简单地覆盖之前的视图,部分或是完全遮住它们(除非新的那个对象是透明的)。

LinearLayout

LinearLayout根据在orientation属性中所定义的,沿着单一方向——水平或是垂直——排列子视图。所有的子视图一个接一个堆砌,因此一个垂直列表中无论子视图有多宽,每一行都只有一个子视图。而一个水平列表高度只有一行(最高的子视图的高度加上间隙)。LinearLayout能显示每一个子视图之间的边距和重力(右侧、中心或左侧)。

LinearLayout还支持为其单独的子元素设定权重(weight)。该属性给视图设定了“重要性”。允许其扩展填充父视图中的任意剩余空间。子视图可以指定一个整型权重值,之后视图组中的剩余空间会按子视图所声明的权重比来分配。默认权重是零。例如,如果有三个文本框,其中两个的权重被声明为1,另一个没有权重(0),那么这个没有权重的文本框不会扩展,只会占它内容所需的空间。另两个会按相同比例扩展,充满三个文本框被计算后所剩余的空间。如果第三个文本框权重为2(而不是0),那它就被声明为比其他两个”更为重要“,而获得总剩余空间的一半,另两个则是平分另一半。

下面的两种形式代表了含有一组元素的LinearLayout:一个按钮、一些标签和一些文本框。文本框的宽度设置为fill_parent;其他元素设置为wrap_content。重力(gravity)默认是向左(left)。两个版本哦差异在于左边的没有设置权重(默认为零),右边的一个文本框的权重被设为了1。如果Name文本框也被设为了1的话,Name和Comments文本框则会一样高。

 

linearlayout
linearlayout

Tip:为了在屏幕上创建比例合适的布局,可以创建一个容器视图组,layout_widthlayout_height属性设为fill_parent;设置子视图的高和宽为0;根据所希望的屏幕比例设置每一个子视图的相对权重(weight)。

在一个水平LinearLayout中,项目根据其文本基准线的位置排列(最上部或最左侧的第一个元素的第一条线被认为是参考线)。这样在浏览元素时就不必上下移动视线以阅读相邻元素的元素文本了。可以通过在布局XML中设置android:baselineAligned=”false”来关闭这一功能。

要查看其他的范例代码,请参阅“Hello LinearLayout”教程。

TableLayout

TableLayout将其子视图按行、列排列。TableLayout容器不显示其行、列或单元格的边线。表格的列数和拥有单元格最多的那行相同。一张表格可以有空单元格,不过单元格无法跨越列,就像HTML中一样。

TableRow对象是TableLayout的子视图(每一个TableRow定义了表中单独的一行)。每一行有零个或更多的单元格,其每一个可以被其他类型的视图所定义。因此,一行单元格可能是由多种View对象,比如ImageView或TextView,构成的。一个单元格也可以是一个ViewGroup对象(例如,可以把另一个TableLayout嵌套于一个单元格内)。

下面的范例布局有两行,每行有两个单元格。相应的截图显示了其结果,单元格边界以点线显示(作为视觉效果而添加)。

可以将竖栏隐藏、拉伸填充屏幕或是标记为可缩小的以强制缩小表格适应屏幕。参见TableLayout参考文档以了解更多细节。

参见“Hello TableLayout”教程以查看范例代码。

RelativeLayout

RelativeLayout允许子视图指定它们相对于父视图的位置或它们之间的相互位置(通过ID来指定)。因此可以将两个元素靠着右侧边缘排列,或是于屏幕中心、或偏左侧上下排列,或是其他排列方式。元素将按指定的方式排列,因此如果第一个元素位于屏幕中央,其他和该元素相关的元素也会相对于屏幕中央进行排列。同时,由于有这样的顺序,如果是通过XML指定这种视图的话,要引用的元素(为了能定位其他的视图对象)必须在在其他视图引用其ID之前就被列于XML文件之中。

下面的例子展示了一个XML文件及其屏幕界面的显示结果。需要注意,引用了相对元素的那些属性(比如,layout_toLeft)是通过相对资源(@id/id)的语法来引用其ID的。

这些属性中的一部分直接被元素所支持,另一些则被其LayoutParams成员(RelativeLayout对于屏幕上所有元素的子类,这是因为所有元素都是RelativeLayout父对象的子对象)所支持。所定义的RelativeLayout参数有:widthheightbelowalignToptoLeftpadding[Bottom|Left|Right|Top]margin[Bottom|Left|Right|Top]。注意,其中一些参数是用于指定相对布局位置——它们的值必须是希望作为相对视图基准的元素的ID。例如,设置参数toLeft=”my_botton”至一个TextView将把该TextView放在ID为my_button的元素(比如在XML中先于TextView出现)的左侧。

要查看该范例代码,请参见“Hello RelativeLayout”教程。

重要视图组的总结

这些对象都有子UI元素。一些提供了可见的界面自有形式,另一些则是不可见的结构,只能用于管理其子视图。

描述
FrameLayout 用于显示单个对象的视图框架布局。
Gallery 一个可水平滚动的图像列表。
GridView 显示m列n行可滚动的表格。
LinearLayout 将子元素水平或是垂直排列为一行/列的布局。如果视窗长度超过屏幕长度,将会自动创建滚动条。
ListView 显示一列滚动列表。
RelativeLayout 允许指定子对象相互之间的位置(如:子对象A在子对象B的左侧) 或与父对象的相对位置(如:在父对象的上部)。
ScrollView 一列垂直的可滚动元素。
Spinner 在一个单行文本框中显示所绑定列表的其中一行。就像一个列表框,可以水平或是垂直下拉。
SurfaceView 提供所指定绘图表层的直接读取。 它可以持有位于屏幕表面的子对象,不过其实是为在屏幕上进行像素操作而非使用控件而设计的。
TabHost 提供了一个标签页选择器,通过监视点击行为以在标签被选择时切换屏幕内容。
TableLayout 一个行列数固定的表格布局。每一个单元格可以持有一个所选择的控件。每一行会自适应最大的列数。单元格的边界是不可见的。
ViewFlipper 在一个单行文本框中一次显示一个项目的列表。可以设置如同幻灯片一样在一定时间间隔后切换项目。
ViewSwitcher 和ViewFlipper相同。

返回用户界面

本页部分内容根据Android Open Source Project创作并共享的内容修改,并在知识共享 署名2.5许可协议中所述条款的限制下使用。

声明布局

wrap_content 使视图自动调节尺寸到内容所需的大小布局(layout)是一个活动的用户界面的结构。它定义了布局结构并包含了所有将向用户呈现的元素。可以通过两种途径声明布局:

  • 在XML中声明UI元素。Android提供了一组直观的XML词汇,它与View类及其子类(比如部件和布局所要使用的)对应。
  • 在运行时实例化布局元素。程序可以通过代码创建View和ViewGroup对象(并设置其属性)。

Android框架可灵活地使用以上一种或两种方法来声明并管理程序界面。比如,可以在XML中声明程序的默认布局,像是屏幕上显示的元素及其属性。之后可以在程序中添加代码以在运行时修改包括XML中所声明的屏幕物件的状态。

  • Eclipse的ADT插件提供了XML的布局预览——打开XML文件后选择Layout标签。
  • 还可以试着用层次查看器工具(Hierarchy View Tool)来调试布局——它显示了布局的属性值,以间距/边缘指示器来画出框架,并在通过模拟器或实机调试时渲染整个视图。
  • layoutopt工具可以快速分析布局和层次中的效率低下或其他问题。

在XML中声明UI的优势在于可以更好地分离程序显示与控制行为的代码。UI描述不属于程序代码。意味着不必修改源代码并重编译就能修改UI。比如,可以为不同屏幕方向、不同屏幕尺寸与不同语言创建多个XML布局。另外,在XML中声明布局使UI的可视化变得更为容易,也就更容易调试。因此,本文档着眼于讲授如何通过XML声明布局。如果对于在运行时实例化View对象的话,可以参考ViewGroup和View类的内容。

通常,用以声明UI元素的XML词汇和结构、类与方法的命名紧密相关,元素名称对英语类名,属性名称对应于方法。事实上,这一对应关系非常直接以至于可以猜出XML属性对应的类方法,或猜出一个给定的XML元素对应于哪一个类。不过,注意并非所有词汇都是相同的。在一些情况下,命名上有些小差异。比如,EditText元素有一个text属性,它对应的是EditText.setText()

Tip:可以在”常用Layout对象“一文中了解更多不同的布局类型。在”Hello Views“教程中有关于如何构建不同布局的一系列教程。

编写XML

使用Android的XML词汇,可以像用HTML创建网页一样通过一系列的嵌套元素快速地设计UI布局与其中包含的屏幕元素。

每一个布局文件必须包含一个View或ViewGroup对象作为根元素。一旦定义了根元素,就可以添加其他的布局对象或是部件作为其子元素,逐渐构造出能够用于定义布局的视图层次。比如,下面是一个XML布局,它使用了垂直LinearLayout并含有一个TextView和一个Button:

在XML中声明了布局之后,以.xml后缀保存该文件与Android项目的res/layout/文件夹,这样它就能被正确编译。

为了方便,UI相关的类的API参考文档列出了与类的方法对应的可用的XML属性,以及继承属性。

要了解更多关于可用的XML元素和属性以及XML文件的格式的信息,参阅“布局资源”。

稍后将讨论这里出现的每一个属性。

载入XML资源

在编译程序时,每一个XML布局文件会被编译为一个View资源。需要在该活动的onCreate()方法中使用程序代码来载入布局资源。通过调用setContentView(),以R.layout.layout_file_name的形式传递这个引用给布局资源。比如,如果XML布局被保存为main_layout.xml,那么应该像这样将它读入活动:

活动中的onCreate()回馈方法在活动被启动时被Android框架调用(参见“应用程序基础”中的关于生命周期的讨论以了解更多)。

属性

每一个View和ViewGroup对象支持其自有的XML属性。有些属性专用于某一View对象(比如,TextView支持textSize属性),但这种属性也会被扩展于这个类的View对象所继承。一些属性对所有View对象都适用,因为它们继承于根View类(比如id属性)。同时,其他的属性被看作是“布局参数”,在View对象的ViewGroup父对象中定义用以描述View对象特定的布局位置。

ID

任何View对象都有一个整数ID与之关联,以在层次树种唯一确定该View。当程序在编译时,ID作为整数被引用,不过ID却是作为字符串被分配给布局XML文件的。这是一个对所有View都适用的XML属性(由View类定义),将被经常使用。ID在XML标签中的语法格式如下:

字符串头部的@符号表明XML语法分析器应该把ID字符串的剩余部分识别为ID资源并分析与扩展它。加号(+)意味着这是一个新的资源名,必须被创建并添加至资源中(在R.java文件里)。还有其他许多Android框架提供的ID资源。在引用Android资源ID时,不需要使用加号,但必须像这样加上android包的命名空间:

由于有了android包的命名空间,现在可以引用android.R资源类中的ID,而不仅仅是本地资源类。

为了在程序中创建视图并引用它,通常采用这样的方式:

1、在布局文件中定义一个视图/部件,之后分配其一个专有的ID:

2、然后创建该视图对象的一个实例,并从布局中获取它(通常是在onCreate()方法中):

当创建一个RelativeLayout时为视图对象定义ID是很重要的。在相对布局(relative layout)中,同层次视图可以通过引用其唯一的ID来相对地定义它们之间的布局。

在整棵层次树中ID不需要是唯一的,但在被搜索的这部分中它需要是唯一的(不过由于通常会搜索整棵树,所以最好尽可能设定唯一的ID)。

布局参数

被命名为layout_XXXX的XML布局属性为View定义了其布局参数以适合于其所在的ViewGroup。

每一个ViewGroup类有一个扩展于ViewGroup.LayoutParams的嵌套类。这个子类包含了定义每一个子视图适合于视图组的尺寸和位置的正确类型。如下图所示,父视图组为每一个子视图(包括子视图组)定义了布局参数。

注意每一个LayoutParams子类都有其自有的赋值语法。每一个子元素必须定义适合于其父元素的LayoutParams,不过可以为它的子元素定义不同的参数。

所有的视图组包含有宽度和高度参数(layout_widthlayout_height),每一个试图都需要定义它们两者。许多LayoutParams还包含可选的边距等参数。

可以通过精确的单位来制定宽度和高度,不过往往不这样做。更常见的做法是用一下常量之一来设置宽度或高度:

  • wrap_content 使视图自动调节尺寸到内容所需的大小
  • fill_parent (在API Level 8中重命名为match_parent)使视图变为其父视图所允许的最大尺寸

通常,不推荐用绝对单位比如像素来定义布局的宽度和高度。改用相对度量方式比如密度无关像素单位(dp),wrap_content,或fill_parent是更好的方式,因为这样可以帮助确保程序可以在不同屏幕尺寸的设备上正确显示。可用的度量方式在“可用资源”文档中被定义。

布局位置

一个视图占有一块矩形区域。视图有一个通过一对左侧顶部坐标表示的位置,以及通过宽度和高度表示的两个维度。位置和维度的单位是像素。

可以通过调用getLeft()和getTop()方法来获得一个视图的位置。前者将返回视图所占矩形区域的左侧或者说X坐标,后者返回该矩形的顶部或者说Y坐标。这两种方法返回的位置都与其父视图相关。比如,如果getLeft()返回20,就意味着该视图处于其直接父视图左侧边缘起向右20像素的位置。

另外,有一些便利的方法可以用来避免不必要的计算,即getRight() 和getBottom()。这些方法返回视图矩形区域的右侧和底部坐标。比如,调用getRight()就类似于进行了getLeft() + getWidth()的计算。

尺寸、间隙和边距

视图的尺寸由宽度和高度表示。一个视图实际上有两组宽度和高度的数值。

第一组是测量宽度(measured width)测量高度(measured height)。它们订立了一个视图在其父视图内的大小。可以通过调用getMeasuredWidth()和getMeasuredHeight()来获取它们的值。

第二组是宽度高度,有时候也被称为绘制宽度绘制高度。这两个维度是视图在绘制和布局后在屏幕上的实际尺寸。它们的值有可能和测量宽度和高度不同。宽度和高度可以通过调用getWidth()和getHeight()获得。

为了测量一个视图的尺寸,需要考虑它的间隙(padding)。可以用像素表示视图上下左右的间隙。可以用一定像素的间隙来代替视图的内容。比如说,值为2的左侧间隙可以将视图的内容自左侧边缘向右推2个像素。可以用setPadding(int, int, int, int)方法来设定间隙,而通过getPaddingLeft()、getPaddingTop()、getPaddingRight()和getPaddingBottom()方法可以获得间隙的值。

尽管一个视图可以定义间隙,不过它不支持任何类型的边距(margin)。但视图组支持边距。参考ViewGroup和ViewGroup.MarginLayoutParams以获得进一步的信息。

关于维度的更多信息,请参见“维度的值”。

← 返回“用户界面”

本页部分内容根据Android Open Source Project创作并共享的内容修改,并在知识共享 署名2.5许可协议中所述条款的限制下使用。