
3.3 事件处理

08 事件处理
现代的图形界面都是通过事件来实现人机交互的。事件就是用户对图形界面的操作。在Android手机和平板计算机上,事件主要包括键盘事件和触摸事件,键盘事件又包括按下、弹起等,触摸事件又包括按下、弹起、滑动等,另外还有重力感应事件等。
本节准备实现一个调查程序,界面上提供几种开发语言的选择列表,如Java、C#、Basic等,使用时除了单击相应控件进行选取之外,还可以通过在键盘上滑动进行选项的“反选”操作。当用户最终单击“确定”按钮后,系统将进行一个简短的提示,告知用户所选择的项目数量。
3.3.1 事件监听处理机制
Android中的事件处理分为基于监听接口的事件处理和基于回调机制的事件处理两种。
1.基于监听接口的事件处理
基于监听接口的事件处理涉及以下三个对象。
➢ EventSource(事件源):事件发生的场所,通常为各个组件。
➢ Event(事件):事件封装了界面组件上发生的特定事情,通常是用户的一次操作,可以通过Event对象取得具体信息。
➢ EventListener(事件监听器):负责监听事件源发生的事情,并做出响应。事件监听器是用来处理事件的对象,实现了特定的接口,根据事件不同重写不同的事件处理方法来响应事件。
View类中的EventListener是一个带有回调方法的接口,而所有的组件都继承自View类,所以不同的组件类实现该接口以完成不同的监听。常见的监听接口处理方法有以下几种。
➢ 单击事件:OnClickListener接口的public void onClick(View v)处理。
➢ 长按事件:OnLongClickListener接口的public boolean onLongClick(View v)处理。
➢ 控件焦点改变事件:OnFocusChangeListener接口的onFocusChange(View v, Boolean hasFocus)处理。
➢ 键盘事件:OnKeyListener接口的public boolean onKey(View v, int keyCode, KeyEvent event)处理。
➢ 触摸事件:OnTouchListener接口的public boolean onTouch(view v, MotionEvent event)处理。
➢ 上下文菜单显示事件:OnCreateContexMenuListener接口的public void onCreateContexMenu (ContexMenu menu, View v, ContexMenuInfo info)处理。
在Android中需要将事件源和事件监听器联系到一起,这就是事件源注册监听。一个典型的事件监听的操作过程如下。
1)在MainActivity类中定义一个成员变量用于引用需要监听的组件。
2)建立一个继承自类似OnClickListener接口的类,主要为了重写其中的事件响应方法。
3)在MainActivity类的onCreate()方法中为按钮注册监听,这样当基于该组件的事件发生后,就会调用相应重写的方法进行处理了。
例如需要为按钮添加单击事件监听,处理过程如下。
1)通常在Activity的onCreate()方法中取得按钮对象(假设按钮的id为button1)。

2)为bt按钮对象绑定单击事件监听对象,此时需要导入相应的程序包,即在代码文件头部添加import android.view.View.OnClickListener语句。

3)设置setOnClickListener()方法的参数。
该方法有一个唯一的参数是OnClickListener对象,一般采用动态的方式构建这个对象,即:new OnClickListener(){}。将上面两者结合起来,得到代码如下。

4)在新构建的对象中重写OnClick()方法,作为单击事件的响应程序。该方法有一个View类型的参数,表示被单击的按钮本身。将相应事件的执行代码安排在这个方法中,代码如下。

将上述代码合并到一起,即:

这段代码看起来比较复杂,却是事件响应的基本模式,其他控件的事件响应程序的布局基本沿用了上述方法。
除OnClickListener外,类似的监听器还有OnLongClickListener(用户长按组件)等。一般,事件监听函数以set开头,以Listener结尾;事件监听函数的参数构造函数以On开头,以Listener结尾;需要重写的事件相应程序以On开头。特别值得注意的是,需要针对不同的监听载入相应的命名空间。初学者很容易遗漏第二条语句之后的分号。
2.基于回调机制的事件处理
在回调机制中,事件源和事件监听器是统一的,或者说不需要专门指定事件监听器,当用户在组件上激发某个事件时,组件自己特定的方法将会负责处理该事件。
在使用基于回调机制处理组件上所发生的事件时,需要为该组件提供相应的事件处理方法,而Java又是一种静态语言,无法为每一个对象动态地加入方法,因此只能通过继承组件类并重写该类的事件处理方法来实现。Android中每个组件都有自己处理特定事件的回调方法,如onKeyDown()、onKeyUp()、onTouchEvent()方法等,可以通过重写这些回调方法来实现对应的事件处理。
自定义组件的一般步骤如下。
1)定义自定义组件的类名,并继承某个组件类或其子类。
2)重写父类的一些方法(回调方法)。依据业务需要重写父类的部分回调方法,比如onDraw()方法用于实现界面显示,其他方法还有onSizeChanged()、onKeyDown()、onKeyUP()等。除了重写回调方法外,通常还需要提供一个构造器,当Java代码创建该组件或依据XML布局文件载入并构建界面时都将调用该构造器。
3)使用自定义的组件。既可以通过Java代码来创建,也可通过XML布局文件创建。注意在XML布局文件中,该组件的标签是完整的包名+类名,不再是原来的类名。
代码示例如下:


3.3.2 键盘事件
一个标准的Android设备包含了多个能够触发事件的物理按键,Android中的物理按键事件处理是基于回调机制的,提供的回调方法有onKeyUp()、onKeyDown()、onKeyLongPress()。在事件处理代码中,为区分按键设备,Android为每个按键定义了唯一编码,各个物理按键对应的编码如表3-15所示。
表3-15 Android设备可用的物理按键编码

例如,以下代码处理了键盘返回键的事件响应。

3.3.3 触摸事件
用户在触摸屏上的滑动触摸事件,一般使用OnTouchListener接口定义的监听器,在监听器中重写public boolean onTouch(View v,MotionEvent event)方法可以处理用户在触摸屏上的动作事件。View类是其他Android组件的父类,在该类中,定义了setOnTouchListener()方法用来为组件设置触摸事件监听器。在监听处理方法中通过event.getAction()可以获取用户的触摸动作。Android提供了用户在屏幕上的触摸事件编码,如表3-16所示。
表3-16 屏幕触摸事件编码

例如以下代码实现当用户触摸屏幕后弹出一个消息提示的功能。

3.3.4 重力感应事件
1.Android重力感应介绍
在Android的开发中常用的基础传感器有8种,分别是加速度传感器(Accelerometer)、陀螺仪传感器(Gyroscope)、环境光照传感器(Light)、磁力传感器(Magnetic field)、方向传感器(Orientation)、压力传感器(Pressure)、距离传感器(Proximity)、温度传感器(Temperature)。随着技术的发展,Android支持的传感器种类越来越多,达20余种。但是不是每一款真机都支持这些传感器。因为很多功能用户根本不关心,所以开发商会把某些功能屏蔽掉。
重力感应是现在手机中常用的一个器件,使用的其实是加速度传感器。当手机静止时,加速度就是重力,所以一般也叫作重力传感器。这个硬件可以感应加速度的变化,转化为数据提供给系统。系统可以根据这些数据做一些事情。最常见的应用就是根据重力旋转屏幕。
2.Android重力感应系统的坐标系
如图3-15所示,Android重力感应坐标系统以屏幕的左下方为原点(在二维编程中,是以屏幕左上方为原点的,这个值得注意),箭头指向的方向为正。从-10到10,可用浮点小数表示,例如以下情形的坐标变化:

图3-15 Android重力感应坐标系统
➢ 手机屏幕向上(Z轴朝天)水平放置的时候,(x,y,z)的值分别为(0,0,10)。
➢ 手机屏幕向下(Z轴朝地)水平放置的时候,(x,y,z)的值分别为(0,0,-10)。
➢ 手机屏幕向左侧放(X轴朝天)的时候,(x,y,z)的值分别为(10,0,0)。
➢ 手机竖直(Y轴朝天)向上的时候,(x,y,z)的值分别为(0,10,0)。
其他的情形照此类推,遵循的规律是:朝天的就是正数,朝地的就是负数。利用x,y,z三个值求三角函数,就可以精确检测手机当前的状态了。
3.检测重力感应事件
首先获得一个SensorManager对象:SensorManager manager=(SensorManager) this.getSystem Service(Context.SENSOR_SERVICE)。手机中的所有传感器都是通过SensorMannager来访问的,调用上下文的getSystemService(SENSOR_SERVICE)方法就可以获得当前手机的所有传感器管理对象。然后通过这个SensorManager对象来获得一个Sensor的列表:List<Sensor> sensors =manager.getSensorList(Sensor.TYPE_ACCELEROMETER)。其中,Sensor.TYPE_ACCELEROMETER参数用于指明获取加速度感应对象。
在Android中,使用重力感应功能还需要使用SensorEventListener接口,其中有两个接口方法,onSensorChanged()和onAccuracyChanged(),一般都是在onSensorChanged()方法中做事务处理。
实际使用时,还要在SensorManager对象上注册SensorEventListener监听器,例如:manager.registerListener(listener,sensor,rate),其中listener就是SensorEventListener监听器对象,sensor是前面获取的Sensor实例,rate是指延迟时间。最后要取消重力感应时,调用manager.unregisterListener(listener)即可。
如下的代码示例实现了一个当手机重力状态发生变化时给出提示的效果。

3.3.5 实例3:调查问答
1.新建项目,增添组件
在AS中新建项目,应用程序命名为“调查问答”,项目命名为“ch03_03”,其他项取默认值。
在res\value\string.xml文件中新增字符串资源,用于界面显示,参考代码如下。

在MainActivity类中对按钮单击事件进行响应,采用基于监听接口的事件处理方式,参考代码如下。

在监听器接口OnClickListener继承的自定义类中的onClick()方法中具体完成选中项目的统计,并以Toast形式进行显示。
在MainActivity类中对屏幕触摸事件进行相应处理,采用基于回调机制的事件处理方式,参考代码如下。

在重写的onTouchEvent()方法中判定如果用户触摸完毕并已经抬起手指(MotionEvent.ACTION_UP)就对复选框进行反选操作,选中原来没有选中的项目,取消已经处于选定状态的项目。这样用户即便没有在屏幕上滑动,仅仅在屏幕上单击一下,也会执行这个“反选”操作。
2.设计布局
在res\layout\activity_main.xml中进行界面设计,此处需要一个文本显示组件,三个复选框组件,一个按钮组件,参考代码如下。


3.参考代码


选择两项的情况如图3-16所示;当在屏幕上滑动从屏幕抬起手指时进行反选,如图3-17所示。选择完毕后,单击“确定”按钮,出现提示信息。

图3-16 选择两项示例

图3-17 触摸事件进行反选