基于监听的事件处理——事件和事件监听器

     当外部动作在Android组件上执行操作时,系统会自动生成事件对象,这个事件对象会作为参数传给事件源上注册的事件监听器。

     事件监听的处理模型涉及三个成员:事件源、事件和事件监听器,其中事件源最容易创建,任意界面组件都看作为事件源;事件的产生无须程序员关心,它是由系统自动产生的;所以实现事件监听器是整个事件处理的核心。

     Android对事件监听模型做了进一步进化:如果事件源触发的事件足够简单、事件里封装的事件比较有限,那就无须封装事件对象、将事件对象传入事件监听器。

     但对于键盘事件、触摸屏事件等,此时程序需要获取事件发生的详细信息:例如键盘事件需要获取是哪个键触发的事件;触摸屏事件需要获取事件发生的位置等,对于这种包含更多信息的事件,Android同样会将事件信息封装成XxxEvent对象,并把该对象作为参数传入事件处理器。

     实例:控制飞机移动

     下面以一个简单的飞机游戏为例来介绍键盘事件的监听。游戏中的飞机会随用户单击键盘的动作而移动;单击不同的键盘,飞机向不同的方向移动。

     为了实现该程序,先开发一个自定义View,该View负责绘制游戏的飞机,该View类的代码如下。

package com.example.studyevent;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;

public class PlaneView extends View {
    public float currentX;
    public float currentY;
    Bitmap plane;
    public PlaneView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        //定义飞机图片
        plane=BitmapFactory.decodeResource(context.getResources(), R.drawable.plane);
        setFocusable(true);
    }
    @Override
    public void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        //创建画笔
        Paint p=new Paint();
        //绘制飞机
        canvas.drawBitmap(plane, currentX, currentY, p);
    }
}

     上面的PlaneView足够简单,因为这个程序只需要绘制玩家自己控制的飞机,没有增加”敌机“,所以比较简单。如果游戏中还需要增加”敌机“,那么还需要增加数据里控制敌机的坐标,并会在View上绘制敌机。

     该游戏几乎不需要界面布局,该游戏直接使用PlaneView做为Activity显示的内容,并为该PlaneView增加键盘事件监听器即可。下面是该Activty代码。

    

package com.example.studyevent;

import android.os.Bundle;
import android.app.Activity;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.Window;
import android.view.WindowManager;

public class PlaneGameActivity extends Activity {
     //定义飞机的移动速度
    private int speed=10;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //去掉窗口标题
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        //全屏显示
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        //创建PlaneView组件
        final PlaneView planeView=new PlaneView(this);
        setContentView(planeView);
        planeView.setBackgroundResource(R.drawable.back);
        //获取窗口管理器
        WindowManager windowManager=getWindowManager();
        Display display=windowManager.getDefaultDisplay();
        DisplayMetrics metrics=new DisplayMetrics();
        //获取屏幕宽和高
        display.getMetrics(metrics);
        //设置飞机的初始位置
        planeView.currentX=metrics.widthPixels/2;
        planeView.currentY=metrics.heightPixels-40;
        //为draw组件键盘事件绑定监听器
        planeView.setOnKeyListener(new OnKeyListener(){

            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                // TODO Auto-generated method stub
                //获取由哪个触发的事件
                switch(event.getKeyCode())
                {
                 //控制飞机下降
                case KeyEvent.KEYCODE_S:
                    planeView.currentY+=speed;
                    break;
                    //控制飞机上移
                case KeyEvent.KEYCODE_W:
                    planeView.currentY-=speed;
                    break;
                case KeyEvent.KEYCODE_A:
                    planeView.currentX-=speed;
                    break;
                case KeyEvent.KEYCODE_D:
                    planeView.currentX+=speed;
                break;
                
                }
                //通知planeView组件重绘
                planeView.invalidate();
                return true;
            }});
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.plane_game, menu);
        return true;
    }

}

   上面程序中的粗体字代码就是控制飞机移动的关键代码——由于程序需要根据用户按下的键来确定飞机的移动方向,所以上面的程序先调用KeyEvent(事件对象)的getKeyCode()来获取触发事件的键,然后根据不同的键来改变游戏中飞机的坐标。

    正如前面提到的:如果事件发生时有比较多的信息需要传给事件监听器,那么就需要将事件信息封装成Event对象,该Event对象将作为参数传入事件处理函数。

    运行上面的程序将看到下图所示效果。

 对于上图所示的”游戏“,当用户按下模拟器右边的4个方向键时,将可以看到”游戏“中的飞机可以上、下、左、右自动移动。

     在基于监听的事件处理模型中,事件监听器必须实现事件监听器接口,Android为不同的界面组件提供了不同的监听器接口,这些接口通常以内部类的形式存在。以View类为例,它包含了如下几个内部接口。

  • View.OnClickListener:单击事件的事件监听器必须实现的接口。
  • View.OnCreateContextMenuListener:创建上下文文菜单事件的事件监听器必须实现的接口。
  • View.OnFocusChangeListener:焦点改变事件的事件监听器必须实现的接口。
  • View.OnKeyListener:按键事件的事件监听器必须实现的接口。
  • View.OnLongClickListener:长单击事件的事件监听器必须实现的接口。
  • View.OnTouchListener:触摸屏事件的事件监听器必须实现的接口。

   通过上面的介绍不难看出,所谓事件监听器,其实就是实现了特定接口的Java类的实例。在程序中实现事件监听器,通常有如下几种形式。

  •  内部类形式:将事件监听器类定义成当前类的内部类。
  •  外部类形式:将事件监听器类定义成一个外部类。
  •  Actviy本身作为事件监听器类:让Activity本身实现监听器接口,并实现事件处理方法。
  •  匿名内部类形式:使用匿名内部类创建事件监听器对象。 

              

    

   

原文地址:https://www.cnblogs.com/wolipengbo/p/3406621.html