学习Android的第二十六天

目录

[Android Gesture 手势](#Android Gesture 手势)

[Android 中手势交互的执行顺序](#Android 中手势交互的执行顺序)

GestureListener

SimpleOnGestureListener

范例

参考文档

[Android Gesture 手势添加与识别](#Android Gesture 手势添加与识别)

使用GestureLibraries

范例

参考文档


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中处理手势操作的重要接口,它提供了一系列回调方法来响应用户的不同手势动作。这些手势包括但不限于按下、抛掷、长按、滚动、按住和抬起等。下面将详细解释每个回调方法及其用途:

  1. onDown(MotionEvent e)
  • 说明:当用户的手指刚刚接触到触摸屏时触发。这是最基本的手势识别方法,几乎所有的手势识别都是从onDown()开始的。
  • 用途:可以用来初始化某些操作,比如高亮显示被触摸的元素。
  1. onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
  • 说明:当用户在触摸屏上迅速移动手指并松开时触发。e1和e2分别是手指按下和抬起时的MotionEvent对象,velocityX和velocityY表示手指离开屏幕时的速度。
  • 用途:常用于实现翻页效果或者删除条目时的快速滑动。
  1. onLongPress(MotionEvent e)
  • 说明:当用户的手指按在屏幕上持续一段时间,并且没有松开时触发。
  • 用途:用于显示上下文菜单、进入拖拽模式等操作。
  1. onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
  • 说明:当用户的手指在屏幕上滑动时触发。e1是手指按下时的MotionEvent对象,e2是当前移动事件的MotionEvent对象,distanceX和distanceY表示从上一个事件到这个事件的滑动距离。
  • 用途:用于实现滚动列表、图片查看器中的图片滑动等。
  1. onShowPress(MotionEvent e)
  • 说明:当用户的手指按在触摸屏上,且还未移动或松开时触发。它的时间范围在按下起效,在长按之前。
  • 用途:可以用来给用户一个视觉反馈,表明他们的按压已经被识别,但还未触发长按。
  1. 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();
    }
}

参考文档

  1. Android GestureDetector

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"/>

参考文档

  1. 官方 API 文档: GestureDetector
相关推荐
hopetomorrow1 分钟前
学习路之压力测试--jmeter安装教程
学习·jmeter·压力测试
hopetomorrow2 分钟前
学习路之PHP--使用GROUP BY 发生错误 SELECT list is not in GROUP BY clause .......... 解决
开发语言·学习·php
闲暇部落23 分钟前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
/**书香门第*/30 分钟前
Cocos creator 3.8 支持的动画 7
学习·游戏·游戏引擎·游戏程序·cocos2d
美式小田1 小时前
单片机学习笔记 9. 8×8LED点阵屏
笔记·单片机·嵌入式硬件·学习
猫爪笔记1 小时前
前端:HTML (学习笔记)【2】
前端·笔记·学习·html
-一杯为品-2 小时前
【51单片机】程序实验5&6.独立按键-矩阵按键
c语言·笔记·学习·51单片机·硬件工程
诸神黄昏EX2 小时前
Android 分区相关介绍
android
风尚云网3 小时前
风尚云网前端学习:一个简易前端新手友好的HTML5页面布局与样式设计
前端·css·学习·html·html5·风尚云网
大白要努力!3 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle