4.9 界面事件响应

事件是Android平台与用户交互的手段。当用户对手机进行操作时,会产生各种各样的输入事件,Android框架捕获到这些事件,进而进行处理。Android平台提供了多种用于获取用户输入事件的方式,考虑到用户事件都是在特定的用户界面中产生,因此Android选用使用特定View组件来获取用户输入事件的方式,由View组件提供事件处理的方法。这就是为什么View类内部带有处理特定事件的监听器。

4.9.1 事件监听器

监听器用于对特定事件进行监听,一旦监听到特定事件,则由监听器截获该事件,并回调自身的特定方法对事件进行处理。在本章之前的实例中,我们使用的事件处理方式都是监听器。根据用户输入方式的不同,View组件将截获的事件分为6种,对应以下6种事件监听器接口:

(1)OnClickListener接口:此接口处理的是单击事件,例如,在View上进行单击动作、在View获得焦点的情况下单击“确定”按钮或者单击轨迹球都会触发该事件。当单击事件发生时,OnClickListener接口会回调public void onClick(View v)方法对事件进行处理。其中参数v指的是发生单击事件的View组件。

(2)OnLongClickListener接口:此接口处理的是长按事件,当长时间按住某个View组件时触发该事件。其对应的回调方法为public boolean onLongClick(View v),当返回值true时,表示已经处理完此事件,若事件未处理完,则返回false,该事件还可以继续被其他监听器捕获并处理。

(3)OnFocusChangeListener接口:此接口用于处理View组件焦点改变事件。当View组件失去或获得焦点时会触发该事件,其对应的回调方法为public void onFocusChange(View v, Boolean hasFocus),其中参数v表示产生事件的事件源,hasFocus表示事件源的状态,即是否获得焦点。

(4)OnKeyListener接口:此接口用于对手机键盘事件进行监听,当View获得焦点并且键盘被敲击时会触发该事件。其对应的回调方法为public boolean onKey(View v, int keyCode, KeyEvent event),其中参数keyCode为键盘码,参数event便为键盘事件封装类的对象。

(5)OnTouchListener接口:此接口是用来处理手机屏幕事件,当在View的范围内触摸、按下、抬起、滑动等动作时都会触发该事件,并触发该接口中的回调方法,其对应的回调方法:public boolean onTouch(View v, MotionEvent event)对应的参数同上。

(6)OnCreateContextMenuListener接口:此接口用于处理上下文菜单被创建的事件,其对应的回调方法为public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo info),其中参数menu为事件的上下文菜单,参数info是该对象中封装了有关上下文菜单其他的信息。在4.5节的实例MenusDemo中,创建上下文菜单使用的是registerForContextMenu(View v)方法,其本质是为View组件v注册该接口,并实现了相应的回调方法。

4.9.2 回调事件响应

在Android框架中,除了可以使用监听器进行事件处理之外,还可以通过回调机制进行事件处理。Android SDK为View组件提供了五个默认的回调方法,如果当某个事件没有被任意一个View处理,则会在Activity中调用响应的回调方法,这些方法分别如下所示。

(1)public boolean onKeyDown(int keyCode, KeyEvent event)方法是接口KeyEvent.Callback中的抽象方法,当键盘按钮被按下时由系统调用。参数keyCode即键盘码,系统根据键盘码得知按下的是哪个按钮。参数event为按钮事件的对象,包含了触发事件的详细信息,例如事件的类型、状态等。当此方法的返回值为True时,代表已完成处理此事件,返回false表示该事件还可以被其他监听器处理。

(2)public boolean onKeyUp(int keyCode, KeyEvent event)方法也是接口KeyEvent.Callback中的抽象方法,当按钮向上弹起时被调用,参数与onKeyDown()完全相同。

(3)public boolean onTouchEvent(MotionEvent event)方法在View中定义,当用户触摸屏幕时被自动调用。参数event为触摸事件封装类的对象,封装了该事件的相关信息。当用户触摸到屏幕,屏幕被按下时,MotionEvent.getAction()的值为MotionEvent.ACTION_DOWN;当用户将触控物体离开屏幕时,MotionEvent.getAction()的值为MotionEvent.ACTION_UP;当触控物体在屏幕上滑动时,MotionEvent.getAction()的值为MotionEvent.ACTION_MOVE。onTouchEvent方法的返回值为true表示事件处理完成,返回false表示未完成。

(4)public boolean onTrackballEvent(MotionEvent event)方法的功能是处理手机中轨迹球的相关事件,可以在Activity中重写,也可以在View中被重写。参数event为手机轨迹球事件封装类的对象。该方法的返回值为true表示事件处理完成,返回false表示未完成。

(5)protected void onFocusChanged(boolean gainFocus, int direction, Rect previously FocusedRect)方法只能在View中重写,当View组件焦点改变时被自动调用,参数gainFocus表示触发该事件的View是否获得了焦点,获得焦点为true,参数direction表示焦点移动的方向,参数previouslyFocusedRect是在触发事件的View的坐标系中前一个获得焦点的矩形区域。

4.9.3 界面事件响应实例

在之前的章节中,多次使用了监听器对事件进行处理,读者应该已经很熟悉了。本节通过一个实例来演示回调事件响应的处理过程,该实例EventDemo运行效果如图4.50所示。

图4.50 实例EventDemo运行效果

其布局文件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:orientation="vertical">



       <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="回调事件处理演示" />
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
       <Button
         android:id="@+id/button1"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:focusableInTouchMode="true"
         android:text="按钮1"/>
     <Button
         android:id="@+id/button2"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:focusableInTouchMode="true"
         android:text="按钮2"/>
     <Button
         android:id="@+id/button3"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:focusableInTouchMode="true"
         android:text="按钮3"/>
    </LinearLayout>



    </LinearLayout>

当用户在屏幕上做移动触摸、单击按钮等操作时,主Activity EventDemo会捕获相应事件并进行处理,在LogCat中打印相关内容,运行效果如图4.51所示。

图4.51 Activity EventDemo捕获事件

EventDemo.java代码如下:

    package introduction.android.eventDemo;



    import android.app.Activity;
    import android.content.Context;
    import android.graphics.Rect;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.KeyEvent;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.View.OnFocusChangeListener;
    import android.widget.Button;
    import android.widget.Toast;



    public class EventDemo extends Activity implements OnFocusChangeListener {
        Button[] buttons=new Button[3];
        @Override
        public void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            buttons[0]=(Button)findViewById(R.id.button1);
            buttons[1]=(Button)findViewById(R.id.button2);
            buttons[2]=(Button)findViewById(R.id.button3);
            for(Button button :buttons){
                button.setOnFocusChangeListener(this);
            }
        }
        //按钮按下触发的事件
        public boolean onKeyDown(int keyCode,KeyEvent event)
        {
            switch(keyCode)
            {
                case KeyEvent.KEYCODE_DPAD_UP:
                    DisplayInformation("按下上方向键,KEYCODE_DPAD_UP");
                    break;
                case KeyEvent.KEYCODE_DPAD_DOWN:
                    DisplayInformation("按下下方向键,KEYCODE_DPAD_UP");
                    break;
            }
            return false;
        }
        //按钮弹起触发的事件
        public boolean onKeyUp(int keyCode,KeyEvent event)
        {
            switch(keyCode)
            {
                case KeyEvent.KEYCODE_DPAD_UP:
                    DisplayInformation("松开上方向键,KEYCODE_DPAD_UP");
                    break;
                case KeyEvent.KEYCODE_DPAD_DOWN:
                    DisplayInformation("松开下方向键,KEYCODE_DPAD_UP");
                    break;
            }
            return false;



        }



        //触摸事件
        public boolean onTouchEvent(MotionEvent event)
        {
            switch(event.getAction()){
      case MotionEvent.ACTION_DOWN:
        DisplayInformation("手指正在往屏幕上按下");
        break;
      case MotionEvent.ACTION_MOVE:
        DisplayInformation("手指正在屏幕上移动");
        break;
      case MotionEvent.ACTION_UP:
        DisplayInformation("手指正在从屏幕上抬起");
        break;
      }
          return false;
        }



        //焦点事件
        @Override
        public void onFocusChange(View view, boolean arg1){
      switch(view.getId()){
      case R.id.button1:
        DisplayInformation("第一个按钮获得了焦点");
        break;
      case R.id.button2:
        DisplayInformation("第二个按钮获得了焦点");
        break;
      case R.id.button3:
        DisplayInformation("第三个按钮获得了焦点");
        break;
      }
        }



        //显示Toast
        public void DisplayInformation(String string)
        {
            //Toast.makeText(EventDemo.this,string,Toast.LENGTH_SHORT).show();
        Log.i("enentDemo",string);
        }
    }