章节索引 :

Android 手势处理

作为忠实的 Android 系统用户,你应该会经常用到各种手势:点击、长按、双击、缩放、滑动、拖拽、返回等等,可以说丰富的手势可以让用户更加简洁方便的使用 App,甚至直接影响到 App 的使用体验。这些手势都是系统为我们提供的操作方式,今天来一起看看如何捕捉用户的手势输入。

1 手势检测工具

在前面的章节我们讲到过触摸事件:onTouch(),它是一切手势的开始,所以根据onTouch的几种事件类型我们可以判断出用户的各种手势,但是对于一些相对复杂的手势(比如缩放、旋转、双击等)判断起来会比较麻烦。不过这些都不用担心Android 系统为我们提供了一个叫GestureDetector的工具类,通过它我们可以轻松的接收到用户的各种复杂手势。

GestureDetector的使用方法非常简单,首先创建一个类继承自GestureDetector.SimpleOnGestureListener,然后覆写其中需要监听的事件方法,如下:

GestureDetector mGesture;
mGesture = new GestureDetector(this, new Gesture());
   
class Gesture extends GestureDetector.SimpleOnGestureListener{
   public boolean onSingleTapUp(MotionEvent ev) {
          // 处理单击事件
   }
   
   public void onLongPress(MotionEvent ev) {
          // 处理长按
   }
   
   public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
   float distanceY) {
          // 处理滑动手势
   }
   
   public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
   float velocityY) {
          // 处理快速滚动
   }
}

2 GestureListener 相关事件

Gesture 支持很多复杂的手势处理,基本上处理手势用它就没错了。这里挑几个最常见的进行详细的讲解,其余的也大同小异。

  • onDown: 触摸事件,同onTouch事件当中的ACTION_DOWN,所有手势的起点
  • onSingleTapUp: 单击
  • onLongPress: 长按
  • onScroll: 滚动
  • onFling: 手指快速滚动,并离开屏幕,在屏幕继续滚动的时候触发

3 手势的处理方式

在写好了第 1 小节的代码之后,关键就是去实现事件处理代码了,点击、长按等事件都比较好理解,这里以缩放手势为例讲解一下具体的手势处理逻辑。

3.1 缩放处理工具类

缩放的手势处理是通过ScaleGestureDetector来实现的,首先创建一个ScaleGestureDetector

ScaleGestureDetector scaleGestureDetector;
scaleGestureDetector = new ScaleGestureDetector(this, new ScaleListener());

构造器需要传入两个参数,一个是上下文 context,一个是缩放时间监听器。所以,在此之前我们还需要创建一个ScaleListener,然后覆写OnTouchEvent(MotionEvent e),并且在OnTouchEvent中将触摸事件传递给 ScaleGestureDetector,代码如下:

public boolean onTouchEvent(MotionEvent ev) {
   SGD.onTouchEvent(ev);
   return true;
}

private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
   @Override
   public boolean onScale(ScaleGestureDetector detector) {
      float scale = detector.getScaleFactor();
      return true;
   }
}

3.2 手势处理中常用的系统方法

在处理手势的过程中,我们还会调用一些系统方法来辅助完成事件处理,主要有以下几个:

  • getEventTime():
    获取事件发生的时间戳
  • getFocusX():
    获取当前手势焦点的 X 轴坐标
  • getFocusY():
    获取当前手势焦点的 Y 轴坐标
  • getTimeDelta():
    获取两次缩放时间的时间差
  • isInProgress():
    判断当前是否正处理缩放过程中
  • onTouchEvent(MotionEvent event):
    接收系统触摸事件,并分发到相应的监听器中

4 手势处理示例

本节通过GestureDetector完成一个类似微信聊天中的大图缩放功能,即通过双指往外或者向内的手势来控制图片的放大、缩小。

4.1 编写布局文件

布局文件非常简单,核心就是一个 ImageView,用来承载我们缩放的目标图片。需要注意的是,这里要将图片的 scaleType设置成“matrix”,用于后续做缩放:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="20dp"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:text="手势处理示例"
        android:textSize="35sp" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textview"
        android:layout_centerHorizontal="true"
        android:text="慕课 Android 教程"
        android:textColor="#ff7aff24"
        android:textSize="35dp" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_below="@+id/textView"
        android:layout_alignParentStart="true"
        android:layout_alignParentEnd="true"
        android:layout_alignParentBottom="true"
        android:scaleType="matrix"
        android:src="@drawable/avatar" />

</RelativeLayout>

4.2 手势处理逻辑编写

手势处理基本遵循上述逻辑,分为 4 步:

  • 第一步: 首先创建一个ScaleListener,覆写onScale方法来接收缩放手势;
  • 第二步: 然后创建ScaleGestureDetector,构造器传入 context 对象及刚刚创建的ScaleListener对象;
  • 第三步: 接着覆写onTouchEvent(MotionEvent ev),调用 ScaleGestureDetector 的onTouchEvent()方法,传入 MotionEvent,这样就把触摸事件传递给了ScaleGestureDetector,后续的整个缩放手势就全权交给ScaleGestureDetector来处理。
  • 第四步: 最后我们只需要在onScale()中完成我们的图片缩放逻辑即可。

代码如下,整体思路还是比较清晰的:


package com.emercy.myapplication;

import android.app.Activity;
import android.graphics.Matrix;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;

public class MainActivity extends Activity {
    private ImageView iv;
    private Matrix matrix = new Matrix();
    private float scale = 1f;
    private ScaleGestureDetector mDetector;

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

        iv = findViewById(R.id.imageView);
        // 第2步:创建缩放手势检测器ScaleGestureDetector,用于检测缩放手势
        mDetector = new ScaleGestureDetector(this, new ScaleListener());
    }

    // 第3步:覆写onTouchEvent,将触摸事件传递给ScaleGestureDetector
    public boolean onTouchEvent(MotionEvent ev) {
        mDetector.onTouchEvent(ev);
        return true;
    }

    // 第1步:创建缩放监听器,用于接收缩放事件
    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            // 第4步:实现图片缩放逻辑
            scale *= detector.getScaleFactor();
            scale = Math.max(0.1f, Math.min(scale, 5.0f));
            matrix.setScale(scale, scale);
            iv.setImageMatrix(matrix);
            return true;
        }
    }
}

编译运行效果如下:

图片描述

我们用双指向外或者向内滑动,就可以看到“照骗”会跟随我们的手势而放大/缩小,整个流程非常顺滑无污染,欢迎自行编译体验。

温馨提示: 照骗高清,请谨慎放大

5 小结

本节介绍了一个非常强大的手势处理工具——GestureDetector,如果不使用它,我们需要自行监听onTouch事件,然后结合“DOWN”、“MOVE”、“UP”等等各种不同的 onTouch 事件组合起来才能检测出一些复杂的手势,这一切 GestureDetector 都帮助我们实现了。学完这一章,我们只需要按照几个简单的步骤就可以进行复杂手势的监听,从此可以释放双手,来创造更多复杂的事件让用户更加顺滑的使用了。

环境搭建,开发相关
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
并发编程
多线程