目录
[Android Gesture 手势](#Android Gesture 手势)
[Android 中手势交互的执行顺序](#Android 中手势交互的执行顺序)
[Android Gesture 手势添加与识别](#Android Gesture 手势添加与识别)
Android Gesture 手势
手势操作在现代移动应用中扮演了非常重要的角色,它不仅提高了用户体验,还增加了应用的互动性和直观性。在Android开发中,实现手势识别是一项基本而重要的技能。Android系统为开发者提供了强大的手势识别功能,让开发者可以轻松地在自己的应用中实现各种手势操作。
Android 中手势交互的执行顺序
1、触摸屏幕事件发生:当用户用手指触摸屏幕时,系统生成一个MotionEvent事件。
2、MotionEvent传递给OnTouchListener:如果你为某个View设置了OnTouchListener,那么这个View上的触摸事件首先会被OnTouchListener的onTouch()方法捕获。
java
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// 处理触摸事件
return false;
}
});
3、MotionEvent转发给GestureDetector:在OnTouchListener的onTouch()方法内,可以将MotionEvent对象传递给GestureDetector处理。
java
@Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
4、GestureDetector处理并回调OnGestureListener:GestureDetector内部根据MotionEvent事件的序列来识别具体的手势动作,并根据识别结果调用OnGestureListener接口中相应的方法。
java
GestureDetector gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
// 当用户按下时触发
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// 用户执行fling操作时触发
return true;
}
// 其他手势回调如onScroll, onLongPress等
});
关键组件说明
- MotionEvent:这个类封装了触摸屏幕时的动作事件(如触摸、移动、松开等),以及触摸点的坐标和其他信息。
- GestureDetector:这个类用于识别一系列的手势动作。它可以处理由MotionEvent输入的手势,并根据这些手势调用OnGestureListener接口中的方法。
- OnGestureListener:这是一个监听手势交互的接口,提供了多个抽象方法以响应不同的手势事件,如单击、滑动、长按等。开发者通过实现这些方法来定义特定手势的响应行为。
通过这种机制,Android应用可以灵活地响应和处理用户的手势操作,从而提供丰富的交互体验。需要注意的是,在OnTouchListener的onTouch()方法中返回true表示该触摸事件已被消费,不会再向后传递;返回false则表示事件未被消费,还可以继续传递给其他监听器或处理方法。
GestureListener
GestureListener是Android中处理手势操作的重要接口,它提供了一系列回调方法来响应用户的不同手势动作。这些手势包括但不限于按下、抛掷、长按、滚动、按住和抬起等。下面将详细解释每个回调方法及其用途:
- onDown(MotionEvent e)
- 说明:当用户的手指刚刚接触到触摸屏时触发。这是最基本的手势识别方法,几乎所有的手势识别都是从onDown()开始的。
- 用途:可以用来初始化某些操作,比如高亮显示被触摸的元素。
- onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
- 说明:当用户在触摸屏上迅速移动手指并松开时触发。e1和e2分别是手指按下和抬起时的MotionEvent对象,velocityX和velocityY表示手指离开屏幕时的速度。
- 用途:常用于实现翻页效果或者删除条目时的快速滑动。
- onLongPress(MotionEvent e)
- 说明:当用户的手指按在屏幕上持续一段时间,并且没有松开时触发。
- 用途:用于显示上下文菜单、进入拖拽模式等操作。
- onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
- 说明:当用户的手指在屏幕上滑动时触发。e1是手指按下时的MotionEvent对象,e2是当前移动事件的MotionEvent对象,distanceX和distanceY表示从上一个事件到这个事件的滑动距离。
- 用途:用于实现滚动列表、图片查看器中的图片滑动等。
- onShowPress(MotionEvent e)
- 说明:当用户的手指按在触摸屏上,且还未移动或松开时触发。它的时间范围在按下起效,在长按之前。
- 用途:可以用来给用户一个视觉反馈,表明他们的按压已经被识别,但还未触发长按。
- onSingleTapUp(MotionEvent e)
- 说明:当用户的手指离开触摸屏的那一刹那触发。
- 用途:用于处理轻触屏幕的点击操作,比如打开一个新的界面或触发按钮等。
SimpleOnGestureListener
如果我们只想要在应用中处理一种或少数几种手势,使用GestureDetector.SimpleOnGestureListener会是一个更加便捷和高效的选择。SimpleOnGestureListener是一个实现了GestureDetector.OnGestureListener和GestureDetector.OnDoubleTapListener接口的类,为所有的手势提供了空实现。这意味着我们可以只覆盖(重写)你感兴趣的那些手势方法,而不是所有的方法。
比如,如果你只对滑动手势(onScroll)感兴趣,你可以创建一个GestureDetector对象,并传入一个匿名内部类,该类继承自SimpleOnGestureListener,然后只重写onScroll方法。
范例
XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_gesture"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="请在此区域内尝试不同的手势"
android:textSize="24sp"/>
</RelativeLayout>
java
package com.example.myapplication3;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private GestureDetector gestureDetector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
showToast("按下");
return true;
}
@Override
public void onLongPress(MotionEvent e) {
showToast("长按");
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
showToast("抛掷");
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
showToast("滚动");
return true;
}
@Override
public void onShowPress(MotionEvent e) {
showToast("按住");
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
showToast("单击抬起");
return true;
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
gestureDetector.onTouchEvent(event);
return super.onTouchEvent(event);
}
private void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
}
参考文档
Android Gesture 手势添加与识别
在Android应用开发中,GestureLibrary和GestureOverlayView是实现高级手势识别和处理的重要工具。通过使用这些工具,开发者可以创建、存储、识别自定义手势,并在应用中实现丰富的交互体验。
使用GestureLibraries
加载手势库:
首先,需要从某个位置加载一个手势库。Android提供了几种加载手势库的方法:
- fromFile(String path) 或 fromFile(File path):从文件系统中的指定路径加载手势库。
- fromPrivateFile(Context context, String name):从应用的私有文件目录加载手势库。
- fromRawResource(Context context, int resourceId):从应用的资源文件中加载手势库。
示例
java
GestureLibrary gestureLib = GestureLibraries.fromRawResource(context, R.raw.gestures);
if (!gestureLib.load()) {
// 手势库加载失败处理
}
使用手势库:
加载手势库后,可以使用GestureLibrary对象提供的方法来管理和识别手势。
- 添加手势:addGesture(String entryName, Gesture gesture)
- 获取所有手势名称:getGestureEntries()
- 根据名称获取手势:getGestures(String entryName)
- 识别手势:recognize(Gesture gesture)
- 删除手势或手势集:removeEntry(String entryName) 和 removeGesture(String entryName, Gesture gesture)
- 保存手势库:save()
使用GestureOverlayView
GestureOverlayView是一个透明的覆盖层,允许用户在其上绘制手势。它提供了三种监听器接口来响应手势事件:
- OnGestureListener:在手势绘制过程中提供回调。
- OnGesturePerformedListener:当用户完成手势绘制时触发。
- OnGesturingListener:提供关于手势开始和结束的回调。
最常用的是OnGesturePerformedListener,它用于在用户完成手势绘制时进行响应。
范例
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="请在下方屏幕中绘制手势~"
android:textSize="20sp"/>
<!-- gestureStrokeType 控制手势是否需要一笔完成,multiple 表示允许多笔 -->
<android.gesture.GestureOverlayView
android:id="@+id/gesture"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gestureStrokeType="multiple" />
</LinearLayout>
XML
<?xml version="1.0" encoding="utf-8"?>
<!-- dialog_save.xml-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:text="输入手势名称"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/gesture_name"/>
</LinearLayout>
<ImageView
android:id="@+id/gesture_show"
android:layout_width="128dp"
android:layout_height="128dp"
android:layout_marginTop="10dp"/>
</LinearLayout>
java
package com.example.myapplication3;
import android.gesture.Gesture;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.GestureLibraries;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
public class MainActivity extends AppCompatActivity {
private GestureOverlayView gestureOverlayView;
private GestureLibrary gestureLibrary;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gestureOverlayView = findViewById(R.id.gesture);
gestureOverlayView.setGestureColor(Color.GREEN);
gestureOverlayView.setGestureStrokeWidth(5);
gestureOverlayView.addOnGesturePerformedListener(this::onGesturePerformed);
// 初始化手势库
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/myGestures";
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
gestureLibrary = GestureLibraries.fromFile(file);
}
private void onGesturePerformed(GestureOverlayView overlay, final Gesture gesture) {
View saveDialog = getLayoutInflater().inflate(R.layout.dialog_save, null, false);
ImageView imgShow = saveDialog.findViewById(R.id.gesture_show);
final EditText editName = saveDialog.findViewById(R.id.gesture_name);
Bitmap bitmap = gesture.toBitmap(128, 128, 10, 0xffff0000);
imgShow.setImageBitmap(bitmap);
new AlertDialog.Builder(this)
.setView(saveDialog)
.setPositiveButton("保存", (dialogInterface, i) -> saveGesture(gesture, editName.getText().toString()))
.setNegativeButton("取消", null)
.show();
}
private void saveGesture(Gesture gesture, String name) {
if (!gestureLibrary.load()) {
Toast.makeText(this, "手势库加载失败!", Toast.LENGTH_SHORT).show();
return;
}
gestureLibrary.addGesture(name, gesture);
boolean isSaved = gestureLibrary.save();
if (isSaved) {
Toast.makeText(this, "手势保存成功!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "手势保存失败!", Toast.LENGTH_SHORT).show();
}
}
}
然后在 AndroidManifest.xml 文件中加入读 SD 卡的权限
XML
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
参考文档
- 官方 API 文档: GestureDetector