章节索引 :

基于监听的事件处理机制

在前面的章节我们都是以开发布局为主,涉及到的逻辑非常少,这样安排是因为编写 UI 会更加直观,写完即能看到效果,可以增加我们的学习兴趣,并能够对 Android 开发有一个直观的感受。在我们设计出精美的 UI 之后,需要让它服务于我们的应用程序,这就需要有事件处理机制了,让各个 View 进行操作的时候它会执行相应的逻辑,完成我们给它分配的任务。

1. 事件处理

事件对应一个行为,它通常发生在用户和App进行交互的时候,比如输入文字、点击按钮、手势等等。Android系统将事件处理设计成了一种先进先出(FIFO)的队列形式,所以我们可以按照用户操作的顺序来依次处理用户事件

2. 事件监听

在系统发生了一个事件之后,我们如何接收到这个事件呢?这就需要在事件发生之前提前向系统注册一个事件监听器,告诉 Android 系统我关心那些事件,那么系统就会在事件发生的相应时间点给你一个回调通知,常见的事件监听器有以下几个:

  • OnClickListener:
    用来监听控件的点击事件,即在用户点击某个 View 的时候回调此接口。(这也是开发过程中最最最常见的接口,一定要牢牢掌握!
  • OnLongClickListener:
    顾名思义,在 View 被长按的时候回调
  • OnFocusChangeListener:
    当控件的焦点发生变化的时候回调
  • OnKeyListener:
    当用户点击手机上的按键的时候回调此接口,通常可以用来拦截按键事件,然后针对特殊场景做特殊处理
  • OnTouchListener:
    当用户触摸屏幕的时候回调,此接口会发生在OnClickListener回调的前面,所以我们可以在Touch事件进行一些更早期的预处理事务。
  • OnMenuItemClickListener:
    当用户点击菜单的时候调用

以上就是 Android 系统提供的常用事件处理监听器,其中最为常见的就是OnClickListener,未来的开发中会大量的使用到,所以必须掌握。所以接下来会以OnClickListener为例子来演示如何完成事件处理,其他的监听器使用方式也都大同小异。

3. 事件处理方式

事件处理要经过以下 4 大步骤:

  1. 注册监听器
  2. 用户进行相应操作,系统将事件入队
  3. 事件经过系统层层分发,最终回调步骤 1 中注册的接口
  4. 执行回调中的逻辑,完成事件处理

Android 中所有的事件处理都会经过以上 4 个步骤,但是具体的处理方式会有所不同,接下来介绍一下几种不同的处理方式,最终达到的效果是每次点击 Button 的时候弹出一个 Toast,如下图:

图片描述

3.1 声明内部类

通过新增内部类的形式实现OnClickListener接口,代码如下:


package com.emercy.myapplication;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = findViewById(R.id.bottom);
        button.setOnClickListener(new EventHandle());
    }

    private class EventHandle implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this, "Button被点击了", Toast.LENGTH_LONG).show();
        }
    }
}

3.2 匿名内部类

匿名内部类的写法会比较简单直接,但是缺点是只能用一次,并且代码会集中在方法体内,如果处理逻辑过于复杂会导致方法代码冗余。所以通常在只需要使用一次并且内部逻辑不太复杂的时候使用。


package com.emercy.myapplication;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = findViewById(R.id.button);
        // 创建匿名内部类绑定点击监听器
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 在回调中处理点击事件
                Toast.makeText(MainActivity.this, "Button被点击了", Toast.LENGTH_LONG).show();
            }
        });
    }
}

3.3 外部类

如果你的事件处理逻辑需要在多个类中使用,那么以上两种方式都无法满足,这时候就需要声明一个外部类来实现OnClickListener接口了:

package com.emercy.myapplication;

import android.content.Context;
import android.view.View;
import android.widget.Toast;

public class EventHandle implements View.OnClickListener {

    Context mContext;

    public EventHandle(Context context) {
        mContext = context;
    }

    @Override
    public void onClick(View v) {
        // 点击回调中处理事件
        Toast.makeText(mContext.getApplicationContext(), "Button被点击了", Toast.LENGTH_LONG).show();
    }
}

由于需要弹 Toast,所以这里在构造器中传入了一个 Context 对象,这样一来 MainActivity 就可以更整洁一些了:


package com.emercy.myapplication;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = findViewById(R.id.button);
        // 绑定点击监听器
        button.setOnClickListener(new EventHandle(this));
    }
}

3.4 Activity 自身实现接口

我们也可以让 Activity 去实现 OnClickListener接口,这样就可以直接在 Activity 中覆写 OnClick方法,将所有的逻辑都封装在了 Activity 内部:


package com.emercy.myapplication;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = findViewById(R.id.button);
        // 直接绑定Activity即可
        button.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        // 在回调中处理点击事件
        Toast.makeText(MainActivity.this, "Button被点击了", Toast.LENGTH_LONG).show();
    }
}

3.5 通过 xml 标签指定

以上四种本质上其实都是通过实现OnClickListener接口去监听点击事件的,除此之外还可以在通过布局文件中添加onClick标签的方式静态绑定点击事件。这种写法非常少见,在某些场景下可以帮助简化很多代码,但是它不太灵活,大家了解一下即可:
首先在 xml 中找到对应的 <Button/> 标签,然后添加onClick属性:

<?xml version="1.0" encoding="utf-8"?>

<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:onClick="eventHandle"
    android:text="点击事件处理">

</Button>

接着在 Activity 中声明eventHandle方法,这样就不需要手动获取 Button 实例,也不用绑定点击事件了。(注意eventHandle的方法签名必须是固定的)

package com.emercy.myapplication;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    // Java代码中无需绑定,直接实现处理函数即可
    public void eventHandle(View v) {
        Toast.makeText(getApplicationContext(), "Button被点击了", Toast.LENGTH_LONG).show();
    }
}

4. 总结

本节介绍了 Android 的事件处理机制以及主要常用的集中事件,并以最常用的OnClickListener为例详细讲解了集中实现方式,其他的集中事件几乎都是照壶画瓢,大家有兴趣的也可以模仿本节示例自行实现一下。在本节之前的内容大多是为 UI 布局为主,本节之后大家将会见到很多事件相应及逻辑控制,只有将 UI 和事件处理和在一起,才能写出各式各样的App,你打算写一个什么样的呢?

环境搭建,开发相关
Android 系统背景及结构概述 Android 开发环境搭建 Genymotion 的安装与使用 Android 工程解析及使用 Android 程序签名打包
常用 UI 布局
Android 的 UI 根基 View与View Android 线性布局 LinearLayout Android相对布局RelativeLayout Android 表格布局 TableLayout Android 网格布局 GridLayout Android 帧布局 FrameLayout Android绝对布局AbsoluteLayout
基础控件
Android 文本框 TextView Android 文本输入框 EditText 按钮 Button/ImageButton 选择框 RadioButton/Check 开关控件ToggleButton/Switch Android 图片控件 ImageView Android 进度条 ProgressBar Android 拖动条 SeekBar Android 评分条 RatingBar Android 滚动条 ScrollView 轮播滚动视图 ViewFlipper
Adapter 相关控件
Android 适配器 Adapter Android 列表控件 ListView Android 网格视图 GridView Android 下拉选择框 Spinner 自动补全文本框 AutoCompleteText 折叠列表 ExpandableListView
提示类控件
吐司提示:Toast 的使用方法 状态栏通知:Notification 对话框:AlertDialog 悬浮窗:PopupWindow
菜单类控件
菜单:Menu
其他控件
视频页面:ViewPager 侧滑菜单:DrawerLayout
事件处理机制
基于监听的事件处理机制 Handler 消息传递机制 触摸事件分发处理 AsyncTask:异步任务 Android 手势处理
Android 四大组件
活动:Activity 服务:Service 广播接收器:Broadcast Receiver 内容提供者 - Content Provider
数据存储
文件存储 SharedPreferences 存储 数据库:SQLite 的使用
网络编程
HTTP 使用详解 xml 数据解析 JSON 数据解析 网页视图:WebView Socket 网络接口
绘图与动画
图片资源:Drawable 位图:Bitmap
多媒体开发
媒体播放器:MediaPlayer 相机:Camera 音频录制:MediaRecorder
并发编程
多线程