
第2章 UI的各种事件控制
2.1 基于监听的事件响应
在认识各种Android UI控件之前,我们先来学习对程序界面上执行的各种操作作出响应,这些响应都是通过事件处理来完成的。实际上,在第1章介绍布局的时候,已经出现了部分Android的UI控件了。对于这些控件的详细介绍,将在第3章进行讲解。之所以将对UI的各种事件控制的介绍放在介绍UI控件之前,是因为在讲解各种控件的时候需要结合对具体控件的事件处理,如果不提前认识这些事件处理,就会加大学习的难度。学习事件处理时所出现的控件,大部分都是比较简单的控件,只起到显示界面的作用,难度并不大,读者在这一章只需知道有这个控件就可以了。
Android提供了基于监听器的事件处理以及键盘事件、触摸屏事件等基于回调的事件处理模式。结合这两种事件处理机制,相信读者可以开发出更多丰富多彩的功能。下面首先介绍Android基于监听机制的事件处理模式。
2.1.1 第一种响应方法
这里将用一个实例来介绍第一种响应方法,先看以下程序界面代码。

上面的界面文件非常简单,只是在一个线性布局中添加了一个TextView控件,用来显示文本,然后添加了两个Button按钮,用来与用户进行交互,三个控件自上而下排列。接下来的程序代码中,分别为两个Button按钮添加了单击事件处理。


在上面的程序代码中,实现了单击“+”按钮,界面中的文本字号变大;单击“-”按钮,界面中的文本字号变小。当字号大小大于12并小于36时,文本内容为“我会变大或变小”,并且两个按钮皆可用;当字号等于12时,文本内容为“我不会再变小了”,并且“-”按钮不可用;当字号大小为36时,文本内容为“我不会再变大了”,并且“+”按钮不可用。运行效果如图2.1所示。

图2.1 控制单击屏幕字号大小
从代码文件MainActivity.java中可以看出,程序通过findViewById句柄获得界面中的各个控件,这里使用的是R.id.text、R.id.btnAdd以及R.id.btnMin作为该句柄的参数,即传入参数为界面中各个控件所对应的ID。
接着,程序通过setOnClickListener方法为界面文件中的两个Button按钮分别设置了单击事件监听器,这个方法的参数实际上是一个View.OnClickListener类型的接口,这个接口需要onClick方法,在该方法中,则是实现事件处理的具体响应。为控件注册监听器其实是一种委托的机制。普通控件将整个事件处理委托给监听器,当指定事件发生时,就通知所委托的监听器去处理这个事件。
综上,为控件添加事件监听器,有以下两个编程要点:
使用findViewById()获取XML布局文件中的控件。
使用setOnXXXListener()为控件添加事件处理监听器。
在获取控件时需要强制转换成相应的控件类型,如上面MainActivity.java中的“btnAdd=(Button)this.findViewById(R.id.btnAdd);”将基础类型强制转换为Button类型。
实际上,在本节所介绍的这种为控件设置事件处理监听器的方式为采用匿名内部类作为事件监听器类的方式,即第一种响应方式。接下来的小节将为读者介绍第二种响应方式。
2.1.2 第二种响应方法
用户操作界面中的UI控件,除了上面所述的第一种响应方法之外,还有其他几种响应方法,本节介绍第二种响应方法,即Activity本身直接作为事件监听器。如下源代码中,在同样的布局文件和应用程序下实现同样的功能。


上面的程序让Activity类实现了OnClickListener事件监听接口,并重写了事件处理器方法onClick(View v),该方法内定义了具体的实现业务。当某个控件想要委托该实现了监听器接口的Activity作为事件处理器时,直接将this作为该控件注册监听器的方法的参数即可。如上面的程序代码“btnAdd.setOnClickListener(this);”,把当前Activity设置为“+”按钮的事件处理监听器。
为了保证不同的控件执行不同的操作,在实现onClick方法时,必须使用switch体,用各个控件的ID作为判断条件,实现不同的控件响应不同的业务。运行效果与图2.1一致,这里不再重复。
2.1.3 第三种响应方法
布局不变,本节采用第三种响应方法实现与前面实例同样的功能。实现代码如下:


在上述的代码中,定义了两个内部类,这两个内部类都实现了View.OnClickListener接口,并在onClick方法中定义了具体的业务。接着,为界面中的两个Button控件分别注册了事件处理监听器。如程序代码“btnAdd.setOnClickListener(new btnAddOnClickListener());”,将btnAddOnClickListener类的对象设置为btnAdd按钮的事件处理监听器。运行效果与第一种响应方法中的例子一致,此处不再赘述。
这种将内部类作为事件处理监听器类的方法,有两个好处:可以在当前类中复用该监听器类;该监听器类可以自由访问外部类中的所有界面组件。这两个优势其实也同样是上面的第一种响应方法(使用匿名内部类作为事件监听器类)以及第二种响应方法(使用当前Activity作为事件监听器类)所具备的。只是接下来第四种响应方法——使用外部类作为事件监听器类就没有同时具备这两个优势了。
2.1.4 第四种响应方法
在同样布局以及同一应用程序下,实现同样功能,如下代码中使用的是第四种事件响应处理方法。由于采用外部类作为事件监听器类,所以必须有一个实现了事件监听器接口View.OnClickListener的类。



上面的程序中,首先定义了一个实现了View.OnClickListener接口的外部类BtnOnClickListener,在该外部类中重写了onClick方法来处理具体业务。但由于该外部类不能直接访问Activity中的控件,所以需要通过传参方式来间接访问。接着在Activity类中为两个Button控件注册了事件处理监听器,如代码行“btnAdd.setOnClickListener(new BtnOnClickListener(text,btnAdd,btnMin));”,为“+”按钮注册了监听器,委托该监听器响应具体操作,而该监听器则为程序定义的外部类BtnOnClickListener。
运行效果与第一种响应方法中的例子一致,此处不再赘述。
实际上,这种使用顶级类来定义事件监听器类的方法比较少用,因为这样不能在事件监听器类中自由访问Activity中的控件,除非确实有多个界面共享同一个事件监听器的情况。
2.1.5 在XML界面文件中指定事件处理方法
上面讲到的四种响应方法都是在程序中为控件注册事件处理监听器,接下来要介绍的这种响应方法是在XML界面文件中为组件绑定事件处理方法。如下XML代码文件以及.java源文件,在同样的布局以及相同的应用程序下,实现同样的功能。


上面的XML代码与介绍四种响应方法时所举例子的XML界面文件的代码基本一致,唯一的区别在于本例的XML界面文件中的两个Button控件都指定了android:onClick属性,并给出属性值,而属性值则是在MainActivity类中分别定义的事件处理方法的方法名。如代码行android:onClick="btnAddClick"为“+”按钮绑定了事件处理方法btnAddClick。而在MainActivity类中定义了btnAddClick方法,该方法内则是具体的响应内容。当界面中的“+”按钮被单击时,该方法将会被激发并处理“+”按钮上的单击事件。运行效果与前面介绍的四种响应方法时所举例子的运行效果一致,此处不再赘述。
其实,大多数Android标签都支持如onClick、onLongClick等属性,这种属性的属性值则是Activity中所对应定义的事件处理方法的方法名。