## Android事件处理概述
不管是桌面应用还是手机应用程序, 面对最多的就是用户,经常需要处理的就是用户动作一一也就是需要为用户动作提供响应,这种为用户动作提供响应的机制就是事件处理。
Android 提供了两套强大的事件处理机制:
* 基于监听的事件处理。
* 基于回调的事件处理。
## 基于监听的事件处理
基于监昕的事件处理是一种更“ 面向对象”的事件处理,这种处理方式与Java 的AWT、
Swing 的处理方式几乎完全相同。
### 监听的处理模型
在事件监听的处理模型中, 主要涉及如下三类对象。
* **Event Source (事件源)**: 事件发生的场所, 通常就是各个组件, 例如按钮、窗口、菜单等。
* **Event (事件)** : 事件封装了界面组件上发生的特定事情(通常就是一次用户操作) 。如果程序需要获得界面组件上所发生事件的相关信息, 一般通过Event 对象来取得。
* **Event Listener (事件监昕器)** : 负责监听事件源所发生的事件,并对各种事件做出相应的响应。
>[success]**提示**:有过JavaScript、VisualBasic等变成经验的读者都知道,时间响应的动作实际上就是一系列程序语句,通常以方法的形式组织起来。但是Java是面向对象的编程语言,方法不能独立存在,所以必须以类的形式来组织这些方法,所以事件监听器的核心就是它所包含的方法——这些方法也被称为事件处理器(EventHandler)。
Android 的事件处理机制是一种委派式(Delegation )事件处理方式: 普通组件(事件源)将整个事件处理委托给特定的对象(事件监听器): 当该事件源发生指定的事件时,就通知所委托的事件监听器,由事件监听器来处理这个事件。
每个组件均可以针对特定的事件指定一个事件监听器, 每个事件监听器也可监听一个或多个事件源。因为同一个事件源上可能发生多种事件, 委派式事件处理方式可以把事件源上所有可能发生的事件分别授权给不同的事件监听器来处理:同时也可以让一类事件都使用同一个事件监听器来处理。
>[success]提示:委派式事件处理方式明显抄袭了人类社会的分工协作,例如某个单位发生了火灾,该单位通常不会自己处理该事件,而是将该事件委派给消防局(事件监听器)处理,如果发生了打架斗殴事件,则委派给公安局(事件监听器)处理,而消防器、公安局也会同时监听多个单位的火灾、打架斗殴事件。这种委派式的处理方式将事件源和事件监听器分离,从而提供更好的程序模型,有利于提高程序的可维护性。
下图是事件流程处理示意图
![](https://img.kancloud.cn/cd/a3/cda31db233731422cd826c45f94af0d6_904x538.png)
下面示例讲解基于舰艇的事件处理模型
```
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal">
<EditText
android:id="@+id/txt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false"
android:cursorVisible="false"
android:textSize="12pt"/>
<!-- 定义一个按钮,该按钮将作为事件源 -->
<Button
android:id="@+id/bn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="单击我"/>
</LinearLayout>
```
上面的程序定义的按钮将作为事件源,接下来程序将会为该按钮绑定一个事件监听器——监听器类必须由开发者来实现
```
public class MainActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 获取应用程序中的bn按钮
Button bn = (Button) findViewById(R.id.bn);
// 为按钮绑定事件监听器
bn.setOnClickListener(new MyClickListener()); // ①
}
// 定义一个单击事件的监听器
class MyClickListener implements View.OnClickListener
{
// 实现监听器类必须实现的方法,该方法将会作为事件处理器
@Override
public void onClick(View v)
{
EditText txt = (EditText) findViewById(R.id.txt);
txt.setText("bn按钮被单击了!");
}
}
}
```
上面程序中的粗体字代码定义了一个View.OnClickListener 实现类, 这个实现类将会作为事件监听器使用。程序中①号代码用于为bn 按钮注册事件监听器。当程序中的bn 按钮被单击时, 该处理器被触发, 将看到程序中文本框内变为“ bn 按钮被单击了”。
从上面的程序可以看出,基于监听器的事件处理模型的编程步骤如下:
1. 获取普通界面组件(事件源) , 也就是被监听的对象。
2. 实现事件监听器类,该监听器类是一个特殊的Java 类,必须实现一个XxxListener 接口。
3. 调用事件源的setXxxListener 方法将事件监听器对象注册给普通组件(事件源)。
当事件源上发生指定事件时, Android 会触发事件监听器,由事件监听器调用相应的方法(事件处理器)来处理事件。
把上面的程序与上图结合起来看,可以发现基于监听的事件处理有如下规则。
* **事件源**: 就是程序中的bn 按钮,其实开发者不需要太多的额外处理,应用程序中任何组件都可作为事件源。
* **事件监听器**: 就是程序中的MyClickListener 类。监听器类必须由程序员负责实现,实现监听器类的关键就是实现处理器方法。
* **注册监听器**:只要调用事件源的setXxxListener(XxxListener)方法即可。
对于上面三件事情,事件源可以是任何界面组件,不太需要开发者参与:注册监听器也只要一行代码即可, 因此事件编程的关键就是实现事件监听器类。
所谓事件监听器,其实就是实现了特定接口的Java 类的实例。在程序中实现事件监昕器,通常有如下几种形式。
* **内部类形式**: 将事件监昕器类定义成当前类的内部类。
如上面的代码示例
* **外部类形式**: 将事件监听器类定义成一个外部类。
* **Activity 本身作为事件监听器类**: 让Activity本身实现监听器接口,并实现事件处理方法。
* **匿名内部类形式**: 使用匿名内部类创建事件监听器对象,是目前使用最广泛的事件监听器形式。
* **直接绑定到布局标签**:直接在界面布局文件中为指定标签绑定事件处理方法。
对于很多Android 界面组件标签而言,它们都支持onClick属性,该属性的属性值就是一个形如xxx(View source)方法的方法名
示例如下
```
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal">
<EditText
android:id="@+id/show"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false"
android:cursorVisible="false"/>
<!-- 在标签中为按钮绑定事件处理方法 -->
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="单击我"
android:onClick="clickHandler"/>
//②
</LinearLayout>
```
上面程序中的②处代码用于在界面布局文件中为Button 按钮绑定一个事件处理方法:clickHanlder , 这就意味着开发者需要在该界面布局对应的Activity中定义一个void clickHandler(View source )方法,该方法将会负责处理该按钮上的单击事件。下面是该界面布局
对应的Java 代码。
```
public class MainActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
// 定义一个事件处理方法
// 其中source参数代表事件源
public void clickHandler(View source)
{
EditText show = (EditText) findViewById(R.id.show);
show.setText("bn按钮被单击了");
}
}
```
## 参考文章
下面是网上的一些相关技术文章
[Android响应onClick方法的五种实现方式](https://www.jianshu.com/p/5776ac0c6e16)
在Android的开发中,对于点击事件的OnClickListener有下面四种实现方式,可以根据实际场景的需要选择合适的用法。下面以Button按钮来举例说明。
### 方法一:
**适合场景**:任何场景都通用,但对于一个Activity中要是有多个控件要实现onClick方法就会显得代码冗余。
```
Button bt_Demo = (Button)findViewById(R.id.bt_Demo);
bt_Demo.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//具体点击操作的逻辑
}
});
```
### 方法二:
**适合场景**:适合有多个同类型控件(比如Button数组)要实现onClick()方法时使用
```
Button[] demoBtns ;
……
for(Button button : demoBtns ){
button.setOnClickListener(listener);
}
private OnClickListener listener = new OnClickListener(){
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
switch(arg0.getId()){
case R.id.btn_Demo:
//具体点击操作的逻辑
break;
default:
break;
}
}
}
```
### 方法三:
**适合场景**:同方法二,两者差别不大
```
Button bt_Demo = (Button)findViewById(R.id.bt_Demo);
bt_Demo.setOnClickListener(new ButtonListener());
private class ButtonListener implements OnClickListener{
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
switch(arg0.getId()){
case R.id.btn_Demo:
//具体点击操作的逻辑
break;
default:
break;
}
}
}
```
### 方法四:
**适合场景**:适合界面上有不同类型的控件,这种方式将所有控件的onClick方法在一个方法里面实现,看起来比较简洁
在Activity中实现OnClickListener接口:
```
public class MyActivity extends Activity implements OnClickListener {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//按纽
Button btn_Demo = (Button)findViewById(R.id.bt_Demo);
bt_Demo.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.btn_Demo:
//具体点击操作的逻辑
break;
default:
break;
}
}
}
```
### 方法五:
**适合场景**:通用场景,特别是某个控件的点击方法要实现的逻辑较复杂的时候使用。
在布局文件中加上android:onClick="方法名",对于有些控件(比如TextView)需要兼容旧的SDK API,还需要加上`android:clickable="true"
`
```
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:onClick="onTextViewClick"
android:text="点击事件"
android:textSize="16sp" />
//在代码中实现onTextViewClick()方法即可。
public void onTextViewClick(View view){
//具体点击操作的逻辑
}
```
## android:onClick vs setOnClickListener的区别
[android:onClick vs setOnClickListener](https://www.cnblogs.com/graphics/p/4680073.html)
为Android Widgets添加点击事件处理函数又两种方法,一个是在Xml文件中添加onClick属性,然后在代码中添加对应的函数。另一个是直接在代码中添加setOnClickListener函数。两者什么区别呢?
两者底层并没有任何区别,但是在使用方法五,也就是直接绑定到标签这种处理方式,要注意以下几点、
1. 事件处理函数必须是**public**的。
2. 事件处理函数必须在**Activity**中定义。
3. 事件处理函数必须有一个View类型的参数。
4. **只能用在API Level 4及以后的版本**。