Android事件处理模型

事件处理是Android编程中非常重要的一个部分,因为Android应用在运行过程中会产生许多各种各样的事件(动作),应用必须为这些动作执行对应的响应代码;将产生的动作和响应联系起来的方式就是事件处理机制。Android平台提供了两种事件处理机制,分别是基于监听的模式和基于回调的模式。

1. 基于监听的事件处理模型

1.1 组成部分

  • 事件源:事件发生的地方,比如某个按钮、文本框等
  • 事件:事件类型,通过Event对象表达
  • 事件监听器:负责监听某种的事件类型,并执行相应的操作

基于监听的事件处理模型首先需要创建一个事件监听器,该事件监听器可以监听某一类型的事件;然后,把事件监听器注册到某个组件中;当该组件上发生了可以被该监听器监听的动作后,组件会产生一个代表该动作的Event对象,并把Event对象传递给监听器。在Android中,生成Event对象以及Event对象的传递不需要程序员介入,系统会自动去处理这一步操作。程序员需要做的就是指定事件源,并在该事件源上注册对应于某个事件类型的事件监听器,并重写相应的处理代码。

xml布局代码

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Button
            android:id="@+id/btn_click"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Click"
        />
</RelativeLayout>   

Java代码

public class MainActivity extends Activity {
    private Button mClickBtn;
    // 创建监听器,监听“点击”事件
    final View.OnClickListener listener =
            new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    L.d("You click btn_click!");
                }
            };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mClickBtn = (Button) findViewById(R.id.btn_click);

    // 为mClickBtn组件设置事件类型为‘点击’的监听器
    mClickBtn.setOnClickListener(listener);
}

}

上述代码在MainActivity中创建了一个监听器listener,该监听器负责监听“点击”事件,然后在onCreate方法中通过setOnClickListener方法将该监听器注册到mClickBtn上。

在上述代码中,监听器通过匿名内部类实现,并创建了一个该匿名内部类的对象,然后将该对象注册到事件源上,这种方式与直接在设置监听器的位置新建监听器(如下所示)相比,好处在于该监听器对象可以重用。

mClickBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        L.d("You click btn_click!");
    }
});

注意

  • 一般监听器都通过内部类实现,因为内部类可以访问到外部类中包含的组件或字段,而一般监听器都会涉及到对外部类中相关组件或字段的操作。如果通过外部类实现,还需要传递本类的引用,而且代码也不是很直观。
  • 一般在监听器中不实现业务逻辑,如果监听器确实需要处理逻辑,可以把逻辑封装在外部类的方法中,然后在监听器中调用该方法(监听器只负责简单的数据展示或数据传递,逻辑处理通过专门的方法实现,这种编码方式更符合面向对象的设计原则)

2. 基于回调的事件处理模型

上面所说的基于监听的处理方式是一种委托的事件处理方式,即将事件处理委托给专门负责处理事件的监听器;而接下来要讨论的基于回调的处理方式则是一种主动式的事件处理方式,即事件由事件源本身处理。从代码实现上来说,要添加事件处理的组件需要实现特定的接口,并重写接口中的某些方法;当对应的事件发生时,系统会回调该组件中被重写的方法。举个例子

XML布局代码

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <org.xiuyuan.androidfundemantal.widget.MyButton
        android:id="@+id/btn_click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click"
        />
</RelativeLayout>

Java代码

// 自定义MyButton类
public class MyButton extends Button {

    public MyButton(Context context, AttributeSet set) {
    super(context, set);
    }

// 设置回调函数
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        super.onKeyDown(keyCode, event);
        Log.i("MyButton", "MyButton onKeyDown");
        return false;
    }
}

public class MainActivity extends Activity {

    private Button mClickBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mClickBtn = (Button) findViewById(R.id.btn_click);

    // 设置监听器
        mClickBtn.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                Log.i("mClickBtn", "onKeyListener");
                return false;
            }
        });
    }

// 设置Activity回调函数
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        super.onKeyDown(keyCode, event);
        Log.i("MainActivity", "MainActivity onKeyDown");
        return false;
    }
}

运行上述代码后,在logcat中会打印:
onKeyListener
MyButton onKeyDown
MainActivity onKeyDown

也就是说,事件发生后被调用的先后顺序是:监听器 –> 组件的回调函数;当组件回调函数返回的值为false时,事件会继续向上传播,所以会继续调用Acitivity中的回调函数;如果监听器中的onKey方法返回true,则不会调用组件的回调函数。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。