第3章
Android
控件
Android控件是UI的重要组成部分。常用的控件有:按钮控件Buton、ImageButon,
状态开关控件ToggleButon、Switch,单选多选按钮RadioButon、CheckBox,图片控件
ImageView,列表控件ListView,文本控件TextView、EditText,下拉控件Spinner,日期与时
间选择控件DatePicker、TimePicker。主要掌握这些控件的创建方法、类中的主要函数及事
件处理机制。
3.1 类层次关系
在讲述具体控件内容之前,熟悉一下Android中类层次关系是非常有必要的,主要包
括View与ViewGroup、ViewGroup与布局的关系等。
在Android知识体系中,View是所有UI视图的基类,ViewGroup是View的子类,
所以它也具有View的特性,但主要用来充当View的容器,将其中的View视作自己的
孩子,对它的子View进行管理。当然,它的孩子也可以是ViewGroup类型。ViewGroup(树根)和它的孩子(View和ViewGroup)以树形结构形成一个层次结构。View类有接
受和处理消息的功能,Android系统所产生的消息会在这些ViewGroup和View之间
传递。
所有布局类LinearLayout、RelativeLayout、TableLayout、GridLayout、FrameLayout
等都是ViewGroup的子类。
总之,View、ViewGroup、部分控件类、部分布局类的UML类图如图3-1所示。
View中常用的方法如下所示。
.ViwfneId(nd),这是Viw中最常用的方法,d值返回
eidViwByitie根据控件iView对象。一般来说,若进一步使用,需要将View强制转换成所需对象,形如
BunojBtofneId(d);
tob=(un)idViwByi
第3章 Android控件59
图3-1 Android基本类层次UML框图
ViewGroup中常用的方法如下所示。
.voidaddView(Viewv),父容器添加一个子组件对象。
.voidremoveAlViews(),父容器删除所有子组件。
.voidremoveView(Viewv),父容器删除子组件v。
.intgetChildCount(),返回子组件数目。
.ResourcegetResources(),获取系统资源对象。
3.2 按钮控件
2.基本按钮Bto
3.1
un
Buton是AndroidUI的重要元素,其常用方法如下所示。
.BtoCnetcx), cx是上下文对象。
un(otxt构造方法,t
t(s),对应ad:t属性,s是字符串序列,设置
按钮文本内容。
.voidsetTexCharSequencecndroitexc
t(d),d是字符串资源id,即原字符串定义在资源文件中。
.voidsetTexintresiresi
e(e),对应ad:e属性,设置文本字体大小,浮
.voidsetTextSizfloatsizndroitextSiz
点数,单位是pxe
。
(t,e),设置文本字体大小,浮点数,单位由ut决
.voidsetTextSizintunifloatsizni
定。unit是系统类TypedValue中定义的静态常量:COMPLEX_UNIT_PX,整
数0,代表单位是px;COMPLEX_UNIT_DIP,整数1,代表单位是dp;COMPLEX
_UNIT_SP,整数2,代表单位是sp。
.voidsetBackgroundColor(Colorc),对应android:background属性,设置背景
颜色。
60 Android 简明程序设计
. voidsetBackgroundResource(intresid),设置背景图片,resid是图片资源id。
. voidsetEnabled(booleanmark),对应android:enabled属性,按钮使能标志。
Button按钮常用的动作有:click,短时单击事件;longclick,长时单击事件;touch,触
摸事件。若对按钮事件进行响应,必须完成以下步骤:①注册事件;②实现事件接口,编
制响应函数。按钮事件常用的注册方法如下所示。
. voidsetOnClickListener(OnClickListenerl),注册click事件,响应方法写在实现
OnClickListener接口类中定义的方法中。
. voidsetOnLongClickListener(OnLongClickListenerl),注册longclick事件,响
应方法写在实现OnLongClickListener接口类中定义的方法中。
.voidsetOnTouchListener(OnTouchListenerl),注册click事件,响应方法写在实
现OnTouchListener接口类中定义的方法中。
【例3-1】 按钮事件响应示例:定义线性布局UI文件,一个“开始”按钮,一个
TextView组件。当单击“开始”按钮时,在TextView组件中显示“Hello”字符串。
① UI配置文件main.xml。
xml version="1.0" encoding="utf-8"? >
② 事件响应:常用的方法有4种,如下所示。
方法1:匿名内部类。
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
第3 章 Android 控件 61
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn = (Button)findViewById(R.id.mystart);
btn.setOnClickListener(new View.OnClickListener() {
//注册按钮click 事件,定义匿名内部类
public void onClick(View v) { //实现响应函数
Button srcBtn = (Button)v; //强制转换成button
srcBtn.setEnabled(false); //禁止按钮操作
TextView tv = (TextView)MainActivity.this.findViewById(R.id
.mytext); //获得TextView 对象
tv.setText("Hello"); //显示Hello
}
});
}
}
由以上代码可知,匿名内部类一般用于函数参数中,见btn.setOnClickListener()函
数内部的参数定义。OnClickListener是一个接口,内部仅定义了一个函数onClick(Viewv),
参数v是事件源对象,因此v可强制转换成Button对象,通过setEnabled(false)禁止其
使能状态。
由于在匿名内部类中函数获得外部类MainActivity中的TextView 对象,因此一定
要用如下代码:“TextViewtv = (TextView)MainActivity.this.findViewById(R.id.
mytext)”。注意,一定是MainActivity.this,而不是this。
方法2:内部类实现。
public class MainActivity extends AppCompatActivity {
class innerOper implements View.OnClickListener{
public void onClick(View v) {
Button srcBtn = (Button)v;
srcBtn.setEnabled(false);
TextView tv = (TextView)findViewById(R.id.mytext);
tv.setText("Hello");
}
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn = (Button)findViewById(R.id.mystart);
62 Android 简明程序设计
btn.setOnClickListener(new innerOper());
}
}
innerOper是内部类,且实现了OnClickListener接口,重写了onClick()函数,与界面
中“开始”按钮的click事件对应。
方法3:外部类实现。
把方法2中的类innerOper移出MainActivity类,作为外部类进行编译,会出现编译
错误:“findViewById()函数没定义”。这是因为当innerOper作为内部类时,它可以共享
外部类MainActivity的一切变量和方法,当然就能用findViewById()函数。有读者说把
MainActivity对象作为参数传到外部类中,不就解决问题了吗? 当然可以,但有更专业的
方法,那就是应用系统Context上下文对象,查看Android帮助文档,可以看出:对许多
系统类,假设抽象为XXX 类,都有XXX(Contextct)构造方法。本示例中,入口类
MainActivity是Activity的派生类,Activity是Context的派生类,因此MainActivity对
象也是Context对象。基于此,编制的按钮外部响应事件类及相关类代码如下所示。
//外部事件响应类:OuterOper.java
public class OuterOper implements View.OnClickListener {
Context ct;
OuterOper(Context ct){
this.ct = ct;
}
public void onClick(View v) {
Button srcBtn = (Button)v;
srcBtn.setEnabled(false);
MainActivity obj = (MainActivity)ct;
//将Context 对象转化为MainActivity 对象
TextView tv = (TextView)obj.findViewById(R.id.mytext);
tv.setText("Hello");
}
} //入口类:MainActivity.java
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn = (Button)findViewById(R.id.mystart);
第3 章 Android 控件 63
btn.setOnClickListener(new OuterOper(this));
}
}
方法4:配置文件方法。
在main.xml配置文件中的Button配置部分增加onClick属性,如下所示(仅列出
Button配置部分)。
然后,在MainActivity类中增加myClick(Viewv)函数,代码如下所示。
public class MainActivity extends AppCompatActivity {
public void onClick(View v) {
Button srcBtn = (Button)v;
srcBtn.setEnabled(false);
TextView tv = (TextView)findViewById(R.id.mytext);
tv.setText("Hello");
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
3.2.2 图像按钮ImageButton
为丰富AndroidUI,有时需要图像按钮。图像按钮可以给设计者更广阔的想象空
间。ImageButton与Button 最大的区别是,前者不需要文字,android:text属性对
ImageButton是不起作用的。
ImageButton类常用的函数如下所示。
.ImageButton(Contextct),构造方法,ct是上下文对象。
.voidsetImageResource(intresid),对应android:src属性,设置按钮图像,resid是
64 Android 简明程序设计
图像资源号,图像一般保存在res\drawable\目录下。
【例3-2】 图像按钮事件响应示例:定制线性布局页面,有一个图像按钮,初始时显
示图像a.png。当手按下后显示图像b.png、手抬起后显示图像a.png。
一般来说,将图像a.png、b.png保存在res\drawable\目录下。
① 界面配置文件main.xml。
xml version="1.0" encoding="utf-8"? >
由代码可知,默认初始图像按钮显示图像res\drawable\a.png,其路径用@drawable/a
描述,不带文件名后缀,不要写成a.png。
② MainActivity.java。
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final ImageButton btn = (ImageButton)findViewById(R.id.mybtn);
btn.setOnTouchListener(new View.OnTouchListener() { //注册触摸事件
public boolean onTouch(View v, MotionEvent event) {
int r = event.getAction(); //获得特征值
if(r==MotionEvent.ACTION_DOWN){ //动作是按下
btn.setImageResource(R.drawable.b); //设置b 图像
}
if(r==MotionEvent.ACTION_UP){ //动作是抬起
btn.setImageResource(R.drawable.a); //设置a 图像
}
return true;
}
});
第3 章 Android 控件 65
}
}
很明显,需对图像按钮注册touch触摸事件,通过setOnTouchListener()函数完成,
响应函数是匿名内部类中的onTouch()。touch触摸事件一般包括3部分:按住、移动、
松开,它们均属于MotionEvent对象,具体哪一个是由MotionEvent类中的getAction()
函数的返回值决定。返回值等于MotionEvent.ACTION_DOWN,表明是按住事件;返回
值等于MotionEvent.ACTION_MOVE,表明是移动事件;返回值等于MotionEvent.
ACTION_UP,表明是松开事件。
3.3 状态开关
3.3.1 ToggleButton开关
ToggleButton是一个具有选中和未选中双状态的按钮,并且需要为不同的状态设置
不同的显示文本。其常用函数如下所示。
. ToggleButton(Contextct),构造方法,ct是上下文对象。
. voidsetTextOn(CharSequenceontxt),对应android:textOn属性,设置开关打开
时的文字内容。
. voidsetTextOff(CharSequenceofftxt),对应android:textOff属性,设置开关关
闭时的文字内容。
. booleanisChecked(),对应android:checked属性,返回开关状态布尔值。
ToggleButton按钮的常用事件注册函数如下所示。
.setOnCheckedChangeListener(OnCheckedChangeListenerl),注册check变化侦
听事件,OnCheckedChangeListener是系统接口,响应函数是该接口中定义的
onCheckedChange()函数。
【例3-3】 ToggleButton示例:定制线性布局页面,有一个ToggleButton双态按钮
(打开、关闭),初始时处于“打开”状态。当不断地“按下-松开”该按钮,在手机屏幕上利用
Toast交替显示“关闭状态”“打开状态”。
① 在UI配置文件main.xml。
xml version="1.0" encoding="utf-8"? >
66 Android 简明程序设计
② MainActivity.java。
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ToggleButton btn = (ToggleButton)findViewById(R.id.mytoggle);
btn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChange-
Listener() {
public void onCheckedChanged(CompoundButton buttonView, boolean
isChecked) {
if(isChecked)
Toast.makeText(MainActivity.this,"开关打开",Toast.LENGTH_
LONG).show();
else
Toast.makeText(MainActivity.this,"开关关闭",Toast.LENGTH_
LONG).show();
}
});
}
}
其界面如图3-2所示。
图3-2 ToggleButton示例
第3 章 Android 控件 67
3.3.2 Switch开关
Switch开关也是一个双态开关,与ToggleButton相比,从界面来说,它更美观。其
常用函数及事件注册函数与ToggleButton几乎是一致的,增量的知识点如下所示。
. Switch(Contextct),构造方法,ct是上下文对象。
. voidsetShowText(booleanmark),对应android:showText。当mark为true时,
android:textOn及android:textOff设置的字符串内容才能显示在屏幕上。
.voidsetTrackResource(intresid),设置Track滑道图片资源,对应android:track
属性,resid是资源号,图片资源一般保存在res\drawable\目录下。
. voidsetThumbResource(intresid),设置Thumb滑块图片资源,对应android:
thumb属性,resid是资源号,图片资源一般保存在res\drawable\目录下。
Switch开关也可叫作滑动开关,当“关闭”时,一般滑块(Thumb)位于滑道(Track)的
左侧;当“打开”时,一般滑块位于滑道的右侧,同时将其“高亮”显示。
【例3-4】 系统Switch开关示例:定制线性布局页面,有一个Switch双态按钮(打
开、关闭),初始时处于“关闭”状态。当不断地“按下-松开”该按钮,在手机屏幕上利用
Toast交替显示“打开状态”“关闭状态”。
① 在UI配置文件main.xml。
xml version="1.0" encoding="utf-8"? >
② MainActivity.java。
public class MainActivity extends AppCompatActivity {
68 Android 简明程序设计
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Switch btn = (Switch)findViewById(R.id.myswitch);
btn.setOnCheckedChangeListener(new CompoundButton
.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean
isChecked) {
if(isChecked)
Toast.makeText(MainActivity.this,"开关打开",Toast.LENGTH_
LONG).show();
else
Toast.makeText(MainActivity.this,"开关关闭",Toast.LENGTH_
LONG).show();
}
});
}
}
其界面如图3-3所示。
图3-3 系统Switch开关示例
可以看出,Android系统默认的滑道图片形似长方形,滑块图片是一个圆形。当
Switch开关关闭时,圆形滑块位于左侧,且是白色;当Switch开关打开时,圆形滑块位于
右侧,且是红色(表示高亮)。
实际上,从此例可推出:ToggleButton开关必须在按钮上显示文字,用以区分打开、
关闭状态,而Switch开关完全可以用滑道、滑块颜色的变化表示开关的打开、关闭。读者
可以看看自己手机的设置部分,许多双态按钮都类似Switch开关,完全是由颜色变化区
分状态的。
【例3-5】 自定义Switch开关示例:主要对track及thumb参数进行设置。track有
打开、关闭状态;thumb也有打开、关闭状态,因此每个track、thumb图片资源最多定义2
个。为了方便,令track图片在打开、关闭两个状态下不变化,定义为一个资源即可,假设
第3 章 Android 控件 69
为track.jpg。thumb图片定义为2个:一个为off.jpg(对应关闭状态);一个为on.jpg(对
应打开状态)。
注意:一般来说,滑块、滑道图片的高度应一致,滑道图片的宽度要大于或等于2倍
滑块图片的宽度。
① 在res\drawable\目录下,按题意建立滑道图片track.jpg和滑块图片off.jpg、
on.jpg。
② 在UI配置文件main.xml。
xml version="1.0" encoding="utf-8"? >
配置文件中仅定义了Switch组件,其滑道、滑块图片动态设置是在程序中实现的,勿
需在此设置。
③ MainActivity.java。
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final Switch btn = (Switch)findViewById(R.id.myswitch);
btn.setTrackResource(R.drawable.track);//设置滑道图片
btn.setThumbResource(R.drawable.off); // 设 置滑块图片(初始为关闭状态)
btn.setOnCheckedChangeListener(new CompoundButton
.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean
isChecked) {
if(isChecked) {
btn.setThumbResource(R.drawable.on);
//若开关打开,则设置on 图片
70 Android 简明程序设计
Toast.makeText(MainActivity.this, "开关打开", Toast.LENGTH_
LONG).show();
}
else {
btn.setThumbResource(R.drawable.off);
//若开关关闭,则设置off 图片
Toast.makeText(MainActivity.this, "开关关闭", Toast.LENGTH_
LONG).show();
}
}
});
}
}
3.4 单选按钮和多选按钮
3.4.1 RadioButton单选按钮
RadioButton是常用的单选按钮。为方便实现单选功能,一般将RadioGroup作为父
容器,内部放置多个RadioButton子控件,这些子控件要么没有选中,要么同时只能有一
个RadioButton子控件被选中。
RadioGroup类常用的函数如下所示。
.voidsetOrientation(intmark),设置添加子组件方式,对应android:orientation属
性。当mark= RadioGroup.VERTICAL 时,垂直添加子组件;当mark=
RadioGroup.HORIZONTAL时,水平添加子组件。
. voidgetChildCount(),获得子组件数量。
.intgetCheckedRadioButtonId(),获得选中的RadioButton对象ID 值,若无选中
的单选按钮对象,则返回-1。
RadioButton类常用的函数如下所示。
. booleanisChecked(),获得布尔值,若为true,则表示选中;若为false,则表示未
选中。
【例3-6】 RadioButton示例:定义一组单选按钮选项,分别是语文、数学、英语;再定
义一个OK按钮,当单击OK按钮时,利用Toast显示选中单选按钮的内容,若没有选中
的按钮,则显示“无”。
第3 章 Android 控件 71
① 在UI布局文件main.xml。
xml version="1.0" encoding="utf-8"? >
代码中仅对RadioGroup节点定义了android:id属性,各单选RadioButton子节点没
有定义android:id属性。这是因为根据id属性值可获取父节点RadioGroup对象。当
然,可遍历其子节点对象,因此可不对各RadioButton子节点设置android:id值。
② MainActivity.java。
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
72 Android 简明程序设计
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn = (Button)findViewById(R.id.myok);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
String s = "";
RadioGroup rg = (RadioGroup)MainActivity.this.findViewById
(R.id.mygrp);
int id = rg.getCheckedRadioButtonId();
if(id==-1)
s = "你没有选中任何科目!";
else{
Button sel = (Button)MainActivity.this.findViewById(id);
s = "你选中的科目是:" +sel.getText();
}
Toast.makeText(MainActivity.this, s, Toast.LENGTH_LONG).show();
}
});
}
}
示例中获取选中单选按钮的步骤是:①根据id值获取RadioGroup对象rg;②通过
调用RadioGroup类中的getCheckedRadioButtonId(),获取选中的单选按钮id值;③根
据id值,获得选中的单选按钮对象sel;④根据sel,调用getText(),获取选中单选按钮的
文本字符串值。
当然,也可用第二种方法确定选中的单选对象,步骤是:① 根据id 值,获取
RadioGroup对象rg;②遍历rg的各RadioButton子对象,若某子对象的isChecked()函
数返回true,则该单选按钮被选中。部分关键代码如下所示。
String s = "你没有选中任何科目!";
RadioGroup rg = (RadioGroup)MainActivity.this.findViewById(R.id.mygrp);
for(int i=0; i
该文件主要定义了题目内容与选项、相邻两道题目之间的位置关系模板。模板主要
涉及以layout_ 为前缀的所有属性内容。TextView 节点是题目内容节点模板,由
android:layout_marginBottom="10dp"得出:题目内容与选项之间的间距为10dp;
RadioGroup节点定义了该题与下一道题目的间距,由android:layout_marginBottom=
“15dp”得出:两道相邻题目之间的间距是15dp。
由于TextView、RadioGroup节点都不是真实的调查问卷所需内容,因此把这两个节
点的android:visibility均设置为gone,表示此两个节点在实际界面中是隐藏的,且不占
空间,若设置成invisible,也是不显示,但占用实际空间。
本例仅对margin_bottom 属性做了设置,实际中可对padding、margin等所有需要的
以layout_为前缀的属性进行设置。这样,在程序中读一次模板参数,为所需要的UI控件
进行设置,程序会非常简洁。看下述的实际代码。
② MainActivity.java。
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView tv = (TextView)findViewById(R.id.mytext);
//获得题目内容模板对象
RadioGroup rg = (RadioGroup)findViewById(R.id.mygrp);
//获得题目选项模板对象
RadioButton rb = (RadioButton)findViewById(R.id.mybtn);
//获得单选按钮模板对象
LinearLayout parent = (LinearLayout)findViewById(R.id.myline);
//获得总的父容器对象
第3 章 Android 控件 75
//二维字符串数组模拟题目内容和选项内容
String s[][] = {{"你的性别","男","女"},
{"你最喜欢的科目? ","语文","数学","外语"},
{"你最喜欢的乐器","钢琴","笛子","手风琴","二胡"}};
for(int i=0; i
此文件中定义了名为MyRadioButton的按钮,并定义了一个属性,名字为value,类
型是字符串string。名字value必须与MyRadioButton定义的成员变量value的名称一
致。若再增加其他属性,只增加相应的节点即可。
③ 在②的基础上,完善的MyRadioButton类关键代码如下所示。
public class MyRadioButton extends RadioButton {
String value;
public MyRadioButton(Context context, AttributeSet attrs) {
super(context, attrs);
try {
//获得MyRadioButton 的属性-值集合
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.
MyRadioButton);
this.value = a.getString(R.styleable.MyRadioButton_value);
//获得value 值
a.recycle();
} catch (Exception e) {
e.printStackTrace();
}
}
public String getValue() {return value;}
第3 章 Android 控件 77
public void setValue(String value) {this.value = value;}
}
构造方法MyRadioButton()不是主动调用的,而是Android系统自动调用的。一些
关键代码的解释如下所示。
.super(context,attrs),调用基类RadioButton构造方法,将RadioButton定义的
“属性-值”集合保存在内存中。
. TypedArraya=context.obtainStyledAttributes(attrs,R.styleable.MyRadioButton);
一方面将MyRadioButton自定义的“属性-值”集合添加到attrs对象中,也就是说,
attrs包含了RadioButton及派生类MyRadioButton的“属性-值”集合;另一方面获得
MyRadioButton自定义“属性-值”集合对象(由TypedArray a 描述)。代码中,
obtainStyleAttributes()函数的第2个参数前缀R.styleable是固定的,末尾的MyRadioButton
必须与上文attrs.xml中定义的name属性值
相一致。
.this.value=a.getString(R.styleable.MyRadioButton_value)
获取MyRadioButton自定义属性value值。代码中,getString()函数的第2个参数
前缀R.styleable是固定的,MyRadioButton是控件名称,末尾的value必须与上文attrs.
xml中定义的name属性值相一致。
④ 简单测试。
前提条件是上文论述的MyRadioButton类,attrs.xml已存在。测试功能是:定义布
局文件main.xml,仅包含MyRadioButton节点,并定义value属性值,在程序中获得该属
性值并利用Toast输出该值。
//布局文件main.xml。
xml version="1.0" encoding="utf-8"? >