Android移动开发-添加手势以及识别用户手势

所谓手势,其实是指用户手指或触摸笔在触摸屏上的连续触碰的行为,比如在屏幕上从左向右划出的一个动作,就是手势;再比如在屏幕上画出一个圆圈也是手势。手势这种连续的触碰会形成某个方向上的移动趋势,也会形成一个不规则的几何图形。Android对两种手势行为都是提供了支持。
- 对于第一种手势行为,Android提供了手势检测,并为手势检测提供了相应的监听器;
- 对于第二种手势行为,Android允许开发者添加手势,并提供了相应的API识别用户手势。

添加手势

Android除了提供手势检测之外,还允许应用程序把用户手势(多个持续的触摸事件在屏幕上形成特定的形状)添加到指定文件中,已备以后使用——如果程序需要,当用户下次再次画出该手势时,系统将可识别该手势。
下面的应用程序在界面布局中使用了GestureOverlayView。

  • 布局文件layout/activity_main.xml代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="请在下面屏幕上绘制手势" />
    <!-- 使用手势绘制组件 -->
    <android.gesture.GestureOverlayView
        android:id="@+id/gesture"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gestureStrokeType="multiple" />
</LinearLayout>

由于GestureOverlayView并不是标准的视图组件,因此在界面布局中使用该组件时需要使用全限类名。
上面的程序使用GestureOverlayView组件时指定了一个android:gestureStrokeType参数,该参数控制手势是否需要多一笔完成。大部分时候,一个手势只要一笔就可以完成,此时可将该参数设为single。如果该手势需要多笔来完成,则将该参数设为multiple。
接下来程序将会为GestureOverlayView添加一个OnGesturePerformedListener监听器,当手势事件完成时,该监听器会打开一个对话框,让用户选择保存该手势。

  • MainActivity.java逻辑代码:
package com.fukaimei.addgesture;

import android.content.DialogInterface;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

    EditText editText;
    GestureOverlayView gestureView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 获取文本编辑框
        editText = (EditText) findViewById(R.id.gesture_name);
        // 获取手势编辑视图
        gestureView = (GestureOverlayView) findViewById(R.id.gesture);
        // 设置手势的绘制颜色
        gestureView.setGestureColor(Color.RED);
        // 设置手势的绘制宽度
        gestureView.setGestureStrokeWidth(4);
        // 为gesture的手势完成事件监听器
        gestureView.addOnGesturePerformedListener(new GestureOverlayView.OnGesturePerformedListener() {  //①
            @Override
            public void onGesturePerformed(GestureOverlayView overlay, final Gesture gesture) {
                // 加载sava.xml界面布局代表的视图
                View saveDialog = getLayoutInflater().inflate(R.layout.save, null);
                // 获取saveDialog里的show组件
                ImageView imageView = (ImageView) saveDialog.findViewById(R.id.show);
                // 获取saveDialog里的gesture_name组件
                final EditText gestureName = (EditText) saveDialog.findViewById(R.id.gesture_name);
                // 根据Gesture包含的手势创建一个位图
                Bitmap bitmap = gesture.toBitmap(128, 128, 10, 0xffff0000);
                imageView.setImageBitmap(bitmap);
                // 使用对话框显示saveDialog组件
                new AlertDialog.Builder(MainActivity.this).setView(saveDialog).setPositiveButton("保存",
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                // 获取指定文件对应的手势库
                                GestureLibrary gestureLib = GestureLibraries.fromFile("/mnt/sdcard/mygestures");  //②
                                // 添加手势
                                gestureLib.addGesture(gestureName.getText().toString(), gesture);  //②
                                // 保存手势库
                                gestureLib.save();  //②
                            }
                        }).setNegativeButton("取消", null).show();
            }
        });

    }
}

上面程序中标为序号①的代码为GestureOverlayView绑定OnGesturePerformedListener监听器,该监听器用于在手势完成时提供响应——它的响应就是打开一个对话框。该对话框的界面布局代码如下。

  • layout/save.xml
<?xml version="1.0" encoding="utf-8"?>
<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="8dip"
            android:text="请填手势名" />
        <!-- 定义一个文本框来让用户输入手势名 -->
        <EditText
            android:id="@+id/gesture_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>
    <!-- 定义一个图片框来显示手势 -->
    <ImageView
        android:id="@+id/show"
        android:layout_width="128dp"
        android:layout_height="128dp"
        android:layout_marginTop="10dp" />
</LinearLayout>

AddGesture程序中的标为②序号的代码是在对话框中完成的,这段代码用于从SD卡的指定文件中加载手势库,并添加用户刚刚输入的手势。

注意:上面的程序需要将手势库保存在SD卡上,因此还需要在清单文件AndroidManifest.xml里面添加程序的读写SD卡的权限。

  • 在清单文件AndroidManifest.xml里添加的权限代码如下:
    <!-- 在SD卡中创建与删除文件权限 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <!-- 向SD卡写入数据权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

识别用户手势

前面已经提到,GestureLibrary提供了recognize(Gesture ges)方法来识别手势,该方法将会返回该手势库中所有与ges匹配的手势——两个手势的图形越相似,相似度越高。
recognize(Gesture ges)方法的返回值为ArrayList< Predicttion>,其中Prediction封装了手势的匹配信息,Prediction对象的name属性代表了匹配的手势名,score属性代表了手势的相似度。
下面的程序将会利用前一个程序所创建的手势库来识别手势。该程序的界面很简单,只是在界面中定义了一个GestureOverlayView组件,允许用户在该组件上输入手势。程序为该组件绑定了OnGesturePerformedListener监听器,该监听器检测到用户手势完成时,就会调用手势库来识别用户输入的手势。


  • layout/activity_main.xml界面代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <!-- 使用手势编辑组件 -->
    <android.gesture.GestureOverlayView
        android:id="@+id/gesture"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gestureStrokeType="multiple" />
</LinearLayout>
  • layout/result.xml界面代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/show"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
  • MainActivity.java逻辑代码:
package com.fukaimei.recognisegesture;

import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.Prediction;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    // 定义手势编辑组件
    GestureOverlayView gestureView;
    // 记录手机上已有的手势库
    GestureLibrary gestureLibrary;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 读取上一个程序所创建的手势库
        gestureLibrary = GestureLibraries.fromFile("/mnt/sdcard/mygestures");
        if (gestureLibrary.load()) {
            Toast.makeText(this, "手势文件装载成功", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "手势文件装载失败", Toast.LENGTH_SHORT).show();
        }
        // 获取手势编辑组件
        gestureView = (GestureOverlayView) findViewById(R.id.gesture);
        // 为手势编辑组价绑定事件监听器
        gestureView.addOnGesturePerformedListener(new GestureOverlayView.OnGesturePerformedListener() {
            @Override
            public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
                // 识别用户刚刚所绘制的手势
                ArrayList<Prediction> predictions = gestureLibrary.recognize(gesture);  //①
                ArrayList<String> result = new ArrayList<String>();
                // 遍历所有找到的Prediction对象
                for (Prediction pred : predictions) {
                    // 只有相似度大于2.0的手势才会被输出
                    if (pred.score > 2.0) {
                        result.add("与手势【" + pred.name + "】相似度为" + pred.score);
                    }
                }
                if (result.size() > 0) {
                    ArrayAdapter<Object> adapter = new ArrayAdapter<Object>(MainActivity.this,
                            android.R.layout.simple_dropdown_item_1line, result.toArray());
                    // 使用一个带List的对话框来显示所有匹配的手势
                    new AlertDialog.Builder(MainActivity.this).setAdapter(adapter, null)
                            .setPositiveButton("确定", null).show();
                } else {
                    Toast.makeText(MainActivity.this, "无法找到能匹配的手势", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
}

注意:上面的程序同样需要将手势库保存在SD卡上,因此还需要在清单文件AndroidManifest.xml里面添加程序的读写SD卡的权限。

  • 在清单文件AndroidManifest.xml里添加的权限代码如下:
    <!-- 在SD卡中创建与删除文件权限 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <!-- 向SD卡写入数据权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

上面的MainActivity.java程序中的标序号为①的代码就负责调用前一个程序的手势库来识别用户刚输入的手势,用户只要在屏幕上绘制一个大致与之前相似的手势,即可提示的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值