第7章 Android事件处理
(教学录像:37分钟)
用户在使用手机、平板电脑时,总是通过各种操作来与软件进行交互,较常见的方式包括键盘操作、触摸操作和手势等。在Android中,这些操作都将转换为对应的事件进行处理,本章就对Android中事件处理进行介绍。
通过阅读本章,您可以:
★ 了解事件处理的机制
★ 掌握键盘事件处理
★ 掌握触摸事件处理
★ 掌握手势的创建与识别
7.1 事件处理概述
教学录像:光盘\TM\lx\7\ 事件处理概述.exe
在前面的章节中,简单地介绍了Android中各种常用的控件,它们组成了应用程序界面。此外,还应当学习如何处理用户对这些控件的操作,如单击按钮等,这就是本章的核心内容。
现在的图形界面应用程序,都是通过事件来实现人机交互的。事件就是用户对图形界面的操作。在Android手机和平板电脑上,主要包括键盘事件和触摸事件两大类。键盘事件包括按下、弹起等,触摸事件包括按下、弹起、滑动、双击等。
在Android控件中,提供了事件处理的相关方法。例如在View类中,提供了onTouchEvent()方法来处理触摸事件。但是,仅有重写这个方法才能完成事件处理显然并不实用。这种方式主要适用于重写控件的场景。除了onTouchEvent()方法,还可以使用setOnTouchListener()方法为控件设置监听器来处理触摸事件,这在日常开发中更加常用。
7.2 处理键盘事件
教学录像:光盘\TM\lx\7\ 处理键盘事件.exe
7.2.1 物理按键简介
对于一个标准的Android设备,包含了多个能够触发事件的物理按键,如图7.1所示。
图7.1 带有物理键盘的Android模拟器
说明:模拟器Skin使用内置的HVGA。
各个可用的物理按键能够触发的事件及其说明如表7.1所示。
表7.1 Android设备可用物理按键及其触发事件
物理按键 | KeyEvent | 说 明 |
电源键 | KEYCODE_POWER | 启动或唤醒设备,将界面切换到锁定的屏幕 |
后退键 | KEYCODE_BACK | 返回到前一个界面 |
菜单键 | KEYCODE_MENU | 显示当前应用的可用菜单 |
Home键 | KEYCODE_HOME | 返回到Home界面 |
查找键 | KEYCODE_SEARCH | 在当前应用中启动搜索 |
相机键 | KEYCODE_CAMERA | 启动相机 |
音量键 | KEYCODE_VOLUME_UP KEYCODE_VOLUME_DOWN | 控制当前上下文音量,如音乐播放器、手机铃声、通话音量等 |
方向键 | KEYCODE_DPAD_CENTER KEYCODE_DPAD_UP KEYCODE_DPAD_DOWN KEYCODE_DPAD_LEFT KEYCODE_DPAD_RIGHT | 某些设备中包含方向键,用于移动光标等 |
键盘键 | KEYCODE_0, …, KEYCODE_9, KEYCODE_A, …, KEYCODE_Z | 数字0~9、字母A~Z等按键 |
Android中控件在处理物理按键事件时,提供的回调方法有onKeyUp()、onKeyDown()和onKeyLongPress()。
7.2.2 范例1:屏蔽后退键
例7.1 在Eclipse中创建Android项目,名称为7.1,屏蔽物理键盘中的后退键。(实例位置:光盘\TM\sl\7\7.1)
编写ForbiddenBackActivity,重写onCreate()方法来加载布局文件,重写onKeyDown()方法来拦截用户单击后退按钮事件,代码如下:
- public class ForbiddenBackActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main); //设置页面布局
- }
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- return true; //屏蔽后退键
- }
- return super.onKeyDown(keyCode, event);
- }
- }
运行程序后,显示如图7.2所示的界面。单击后退键,可以看到应用程序并未退出。
图7.2 屏蔽物理按键
7.2.3 范例2:提示音量增加事件
例7.2 在Eclipse中创建Android项目,名称为7.2,当用户单击增加音量键时显示提示信息。(实例位置:光盘\TM\sl\7\7.2)
编写VolumeUpMessageActivity类,它继承了Activity类。重写onCreate()方法来加载布局文件,重写onKeyDown()方法,当音量增加键被按下时显示提示信息,代码如下:
- public class VolumeUpMessageActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main); //设置页面布局
- }
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
- Toast.makeText(this, "音量增加", Toast.LENGTH_LONG).show(); //提示音量增加
- return false;
- }
- return super.onKeyDown(keyCode, event);
- }
- }
运行程序后,显示如图7.3所示的界面。单击音量增加键,屏幕下方显示音量增加信息。
注意:当单击音量增加键时,onKeyDown()方法的返回值是false,这并没有屏蔽该键的功能。
图7.3 显示音量增加信息
7.3 处理触摸事件
教学录像:光盘\TM\lx\7\处理触摸事件.exe
目前,主流的手机都以较大的屏幕取代了外置键盘,平板电脑也没有提供键盘,这些设备都需要通过触摸来操作,下面介绍一下Android中如何实现触摸事件的处理。
7.3.1 范例1:按钮触摸事件
对于触摸屏上的按钮,可以使用OnClickListener和OnLongClickListener监听器分别处理用户短时间单击和长时间单击(按住按钮一段时间)事件。
例7.3 在Eclipse中创建Android项目,名称为7.3,当用户短时间单击按钮和长时间单击按钮时,显示不同的提示信息。(实例位置:光盘\TM\sl\7\7.3)
编写TouchEventActivity类,它继承了Activity类。重写onCreate()方法来加载布局文件,使用findViewById()方法获得布局文件中定义的按钮,并为其增加OnClickListener和OnLongClickListener事件监听器,代码如下:
- public class TouchEventActivity extends Activity {
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main); //设置页面布局
- Button button = (Button) findViewById(R.id.button); //获得按钮控件
- button.setOnClickListener(new OnClickListener() {
- public void onClick(View v) { //处理用户短时间单击按钮事件
- Toast.makeText(TouchEventActivity.this, getText(R.string.short_click),
- Toast.LENGTH_SHORT).show();
- }
- });
- button.setOnLongClickListener(new OnLongClickListener() {
- public boolean onLongClick(View v) { //处理用户长时间单击按钮事件
- Toast.makeText(TouchEventActivity.this, getText(R.string.long_click),
- Toast.LENGTH_SHORT).show();
- return true;
- }
- });
- }
- }
运行程序后,短时间单击按钮,显示如图7.4所示的提示信息。
图7.4 显示短时间单击按钮信息
长时间单击按钮,显示如图7.5所示的提示信息。
图7.5 显示长时间单击按钮信息
View类是其他Android控件的父类。在该类中,定义了setOnTouchListener()方法用来为控件设置触摸事件监听器,下面演示该监听器的用法。
7.3.2 范例2:检测触摸事件
例7.4 在Eclipse中创建Android项目,名称为7.4,当用户触摸屏幕时显示提示信息。(实例位置:光盘\TM\sl\7\7.4)
编写ScreenTouchEventActivity类,它继承了Activity类并实现了OnTouchListener接口。重写onCreate()方法来定义线性布局管理器,并为其增加触摸事件监听器及设置背景图片,重写onTouch()方法来处理触摸事件,显示提示信息,代码如下:
- public class ScreenTouchEventActivity extends Activity implements OnTouchListener {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState); //调用父类构造方法
- LinearLayout layout = new LinearLayout(this); //定义线性布局
- layout.setOnTouchListener(this); //设置触摸事件监听器
- layout.setBackgroundResource(R.drawable.background); //设置背景图片
- setContentView(layout); //使用布局
- }
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- Toast.makeText(this, "发生触摸事件", Toast.LENGTH_LONG).show();
- return true;
- }
- }
运行程序后,触摸屏幕,显示如图7.6所示的提示信息。
图7.6 显示触摸事件信息
7.4 手势的创建与识别
教学录像:光盘\TM\lx\7\手势的创建与识别.exe
前面介绍的触摸事件比较简单,下面介绍一下如何在Android中创建和识别手势。目前有很多款手机都支持手写输入,其原理就是根据用户输入的内容,在预先定义的词库中查找最佳的匹配项供用户选择。在Android中,也需要先定义类似的词库。
7.4.1 手势的创建
下面请读者运行自己的模拟器,进入到应用程序界面,如图7.7所示。
图7.7 应用程序界面
在图7.7中,单击Gestures Builder应用,如图7.8所示。
图7.8 Gestures Builder程序界面
在图7.8中,单击Add gesture增加手势,如图7.9所示。在Name栏中输入该手势所代表的字符,在Name栏下方画出对应的手势。单击Done按钮完成手势的增加。
图7.9 增加手势界面
类似的,继续增加数字1、2、3所对应的手势,如图7.10所示。
图7.10 显示当前已经存在的手势
7.4.2 手势的导出
在创建完手势后,需要将保存手势的文件导出,以便在自己开发的应用程序中使用。打开Eclipse并切换到DDMS视图。在File Explorer中找到\mnt\sdcard\gestures文件,如图7.11所示。将该文件导出,使用默认名称。
图7.11 导出保存手势的文件
7.4.3 手势的识别
例7.5 在Eclipse中创建Android项目,名称为7.5,实现识别用户输入手势的功能。(实例位置:光盘\TM\sl\7\7.5)
(1)在res文件夹中创建子文件夹,名称为raw。将前面导出的手势文件复制到该文件夹中。
(2)修改layout文件夹中的main.xml文件,添加一个GuestOverlayView控件来接收用户的手势。修改完成后,main.xml文件代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:background="@drawable/background"
- android:orientation="vertical" >
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:text="@string/title"
- android:textColor="@android:color/black"
- android:textSize="20dp" />
- <android.gesture.GestureOverlayView
- android:id="@+id/gestures"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1.0" />
- </LinearLayout>
(3)创建GesturesRecognitionActivity类,它继承了Activity类并实现了OnGesturePerformedListener接口。在onCreate()方法中,加载raw文件夹中的手势文件,接着获得布局文件中定义的GestureOverlayView控件。在onGesturePerformed()方法的实现中,获得得分最高的预测结果并提示,该类代码如下:
- public class GesturesRecognitionActivity extends Activity implements OnGesturePerformedListener {
- private GestureLibrary library;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- library = GestureLibraries.fromRawResource(this, R.raw.gestures); //加载手势文件
- if (!library.load()) { //如果加载失败则退出
- finish();
- }
- GestureOverlayView gesture = (GestureOverlayView) findViewById(R.id.gestures);
- gesture.addOnGesturePerformedListener(this); //增加事件监听器
- }
- @Override
- public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
- ArrayList<Prediction> gestures = library.recognize(gesture); //获得全部预测结果
- int index = 0; //保存当前预测的索引号
- double score = 0.0; //保存当前预测的得分
- for (int i = 0; i < gestures.size(); i++) { //获得最佳匹配结果
- Prediction result = gestures.get(i); //获得一个预测结果
- if (result.score > score) {
- index = i;
- score = result.score;
- }
- }
- Toast.makeText(this, gestures.get(index).name, Toast.LENGTH_LONG).show();
- }
- }
运行程序后,绘制手势,如图7.12所示。
图7.12 用户绘制的手势
在手势绘制完成后,显示提示信息,如图7.13所示。
图7.13 手势对应的信息
7.5 经典范例
7.5.1 查看手势对应分值
例7.6 在Eclipse中创建Android项目,名称为7.6,实现显示用户绘制的手势所对应的分值。(实例位置:光盘\TM\sl\7\7.6)
(1)在res文件夹中创建子文件夹,名称为raw,将自定义的手势文件复制到该文件夹中。
说明:这里使用的手势文件仅包含0~9十个数字,用户可以自己制作。
(2)修改layout文件夹中的main.xml文件,添加一个GuestOverlayView控件来接收用户的手势;添加一个标签显示结果。修改后的main.xml文件代码,请参见光盘。
(3)创建GesturesGuessActivity类,它继承了Activity类并实现了OnGesturePerformedListener接口。在onCreate()方法中,加载raw文件夹中的手势文件,接着获得布局文件中定义的GestureOverlayView控件。在onGesturePerformed()方法的实现中,获得所有手势所对应的分值并进行显示,该类代码如下:
- public class GestureGuessActivity extends Activity implements OnGesturePerformedListener {
- private GestureLibrary library;
- private TextView resultTV;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- library = GestureLibraries.fromRawResource(this, R.raw.gestures); //加载手势文件
- resultTV = (TextView) findViewById(R.id.prediction);
- if (!library.load()) { //如果加载失败则退出
- finish();
- }
- GestureOverlayView gesture = (GestureOverlayView) findViewById(R.id.gestures);
- gesture.addOnGesturePerformedListener(this); //增加事件监听器
- }
- @Override
- public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
- ArrayList<Prediction> gestures = library.recognize(gesture); //获得全部预测结果
- Collections.sort(gestures, new Comparator<Prediction>() { //将预测结果进行排序
- @Override
- public int compare(Prediction lhs, Prediction rhs) {
- return lhs.name.compareTo(rhs.name); /使用结果对应的字符串来排序
- }
- });
- StringBuilder results = new StringBuilder(); //保存全部结果
- NumberFormat formatter = new DecimalFormat("#00.00"); //定义格式化样式
- for (int i = 0; i < gestures.size(); i++) { //遍历全部结果
- Prediction result = gestures.get(i);
- results.append(result.name + ": " + formatter.format(result.score) + "\n");
- }
- resultTV.setText(results); /显示结果
- }
- }
运行程序后,绘制手势,如图7.14所示。
图7.14 用户绘制的手势
在手势绘制完成后,显示得分信息,如图7.15所示。
图7.15 手势得到的分值
7.5.2 使用手势输入数字
例7.7 在Eclipse中创建Android项目,名称为7.7,利用用户绘制的手势在编辑框中输入数字。(实例位置:光盘\TM\sl\7\7.7)
(1)在res文件夹中创建子文件夹,名称为raw。将自定义的手势文件复制到该文件夹中。
说明:这里使用的手势文件仅包含0~9十个数字,用户可以自己制作。
(2)修改layout文件夹中的main.xml文件,添加一个编辑框显示结果;添加一个GuestOverlayView控件来接收用户的手势,修改后的main.xml文件代码请参见光盘。
(3)创建NumberInputActivity类,它继承了Activity类并实现了OnGesturePerformedListener接口。在onCreate()方法中,加载raw文件夹中的手势文件,接着获得布局文件中定义的GestureOverlayView控件。在onGesturePerformed()方法的实现中,获得最佳匹配进行显示,该类代码如下:
- public class NumberInputActivity extends Activity implements OnGesturePerformedListener {
- private GestureLibrary library;
- private EditText et;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- library = GestureLibraries.fromRawResource(this, R.raw.gestures); //加载手势文件
- et = (EditText) findViewById(R.id.editText);
- if (!library.load()) { //如果加载失败则退出
- finish();
- }
- GestureOverlayView gesture = (GestureOverlayView) findViewById(R.id.gestures);
- gesture.addOnGesturePerformedListener(this); //增加事件监听器
- }
- @Override
- public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
- ArrayList<Prediction> gestures = library.recognize(gesture); //获得全部预测结果
- int index = 0; //保存当前预测的索引号
- double score = 0.0; //保存当前预测的得分
- for (int i = 0; i < gestures.size(); i++) { //获得最佳匹配结果
- Prediction result = gestures.get(i); //获得一个预测结果
- if (result.score > score) {
- index = i;
- score = result.score;
- }
- }
- String text = et.getText().toString(); //获得编辑框中已经包含的文本
- text += gestures.get(index).name; //获得最佳匹配
- et.setText(text); //更新编辑框
- }
- }
运行程序后,绘制手势,如图7.16所示。
图7.16 用户绘制的手势
在手势绘制完成后,显示最佳匹配信息,如图7.17所示。
图7.17 手势对应的字符
7.6 小 结
本章重点介绍了Android中常见的事件处理方式,通过与前面介绍的常用控件结合,就可以实现Android应用程序的外部骨架。本章介绍的内容几乎在各个应用程序中都会使用,请读者务必熟练掌握。
7.7 实践与练习
编写Android程序,显示用户触摸持续的时间。(答案位置:光盘\TM\sl\7\7.8)
编写Android程序,显示用户触摸的位置。(答案位置:光盘\TM\sl\7\7.9)