Android手势基本知识

当用户用一根或多根手指触碰屏幕时,接收触摸事件的View的onTouchEvent())函数就会被回调。对于一系列连续的触摸事件(位置、压力、大小、额外的一根手指等等),onTouchEvent())会被调用若干次,并且最终识别为一种手势。手势开始于用户刚触摸屏幕时,其后系统会持续地追踪用户手指的位置,用户手指都离开屏幕时手势结束。在整个交互期间,MotionEvent被分发给onTouchEvent())函数,来提供每次交互的详细信息。你的app可以使用MotionEvent提供的数据,来判断是否发生了某种特定的手势。

1.Android为手势检测提供一个GestureDetector类,GestureDetector实例代表了一个手势检测器,创建GestureDetector时需要传入一个GestureDetector.OnGestureListener实例,GestureDetector.OnGestureListener就是一个监听器,负责对用户的手势行为提供相应。


package com.gesture.gesturedetector;
import android.app.Activity;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.widget.Toast;


public class MainActivity extends Activity implements OnGestureListener {
//定义手势检测器实例
private GestureDetector mDetector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建手势检测器
mDetector = new GestureDetector(this,
(android.view.GestureDetector.OnGestureListener) this);
}
//将该Activity上的触碰事件交给GestureDetector处理
@Override
public boolean onTouchEvent(MotionEvent me){
return mDetector.onTouchEvent(me);
}
public boolean onDown(MotionEvent arg0){
Toast.makeText(this, "onDown!", 50).show();
return true;
}

public boolean onFling(MotionEvent arg0,MotionEvent arg1,
float velocityX,float velocityY){
Toast.makeText(this, "onFling!", 50).show();
return true;
}
public void onLongPress(MotionEvent arg0){
Toast.makeText(this, "onLongPress!", 50).show();
}
public boolean onScroll(MotionEvent arg0,MotionEvent arg1,float distanceX,float distanceY){
Toast.makeText(this, "onScroll!", 50).show();
return true;
}
public void onShowPress(MotionEvent arg0){
Toast.makeText(this, "onShowPress!", 50).show();
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
Toast.makeText(this, "onSingleTapup!", 50).show();
return true;
}
}   
运行上面的程序,当用户随意的在屏幕上触碰时,程序将会检测到用户到底执行了哪些手势。
2.通过手势缩放图片
用户只要在图片上随意地挥动手指,根据手指横向移动的速度,图片就可以被缩放。
package org.crazyit.io;


import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.widget.ImageView;


public class GestureZoom extends Activity 
implements OnGestureListener
{
// 定义手势检测器实例
GestureDetector detector;
ImageView imageView;
// 初始的图片资源
Bitmap bitmap;
// 定义图片的宽、高
int width, height;
// 记录当前的缩放比
float currentScale = 1;
// 控制图片缩放的Matrix对象
Matrix matrix;


@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 创建手势检测器
detector = new GestureDetector(this, this);
imageView = (ImageView) findViewById(R.id.show);
matrix = new Matrix();
// 获取被缩放的源图片
bitmap = BitmapFactory.decodeResource(
this.getResources(), R.drawable.flower);
// 获得位图宽
width = bitmap.getWidth();
// 获得位图高
height = bitmap.getHeight();
// 设置ImageView初始化时显示的图片。
imageView.setImageBitmap(BitmapFactory.decodeResource(
this.getResources(), R.drawable.flower));
}


@Override
public boolean onTouchEvent(MotionEvent me)
{
// 将该Activity上的触碰事件交给GestureDetector处理
return detector.onTouchEvent(me);
}


@Override
public boolean onFling(MotionEvent event1, MotionEvent event2,
float velocityX, float velocityY) //②
{
velocityX = velocityX > 4000 ? 4000 : velocityX;
velocityX = velocityX < -4000 ? -4000 : velocityX;
// 根据手势的速度来计算缩放比,如果velocityX>0,放大图像,否则缩小图像。
currentScale += currentScale * velocityX / 4000.0f;
// 保证currentScale不会等于0
currentScale = currentScale > 0.01 ? currentScale: 0.01f;
// 重置Matrix
matrix.reset();
// 缩放Matrix
matrix.setScale(currentScale, currentScale, 160, 200);
BitmapDrawable tmp = (BitmapDrawable)
imageView.getDrawable();
// 如果图片还未回收,先强制回收该图片
if (!tmp.getBitmap().isRecycled()) // ①
{
tmp.getBitmap().recycle();
}
// 根据原始位图和Matrix创建新图片
Bitmap bitmap2 = Bitmap.createBitmap(bitmap, 0, 0
, width, height, matrix, true);
// 显示新的位图
imageView.setImageBitmap(bitmap2);
return true;
}


@Override
public boolean onDown(MotionEvent arg0)
{
return false;
}


@Override
public void onLongPress(MotionEvent event)
{
}


@Override
public boolean onScroll(MotionEvent event1
, MotionEvent event2, float distanceX, float distanceY)
{
return false;
}


@Override
public void onShowPress(MotionEvent event)
{
}


@Override
public boolean onSingleTapUp(MotionEvent event)
{
return false;
}
}


main.xml文件如下:在界面中定义一个ImageView来显示图片即可。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView  
android:id="@+id/show"
android:layout_width="fill_parent" 
android:layout_height="fill_parent" 
android:scaleType="center"
/>
</LinearLayout>


3.通过手势实现翻页效果
把Activity的TouchEvent交给GestureDetector处理,使用了一个ViewFlipper组件,ViewFlipper可以使用动画控制多个组件之间的切换效果。
package org.crazyit.io;
import org.crazyit.io.R;
import android.app.Activity;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.ViewFlipper;


public class GestureFlip extends Activity 
implements OnGestureListener
{
// ViewFlipper实例
ViewFlipper flipper;
// 定义手势检测器实例
GestureDetector detector;
// 定义一个动画数组,用于为ViewFlipper指定切换动画效果
Animation[] animations = new Animation[4];
// 定义手势动作两点之间的最小距离
final int FLIP_DISTANCE = 50;


@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 创建手势检测器
detector = new GestureDetector(this, this);
// 获得ViewFlipper实例
flipper = (ViewFlipper) this.findViewById(R.id.flipper);
// 为ViewFlipper添加5个ImageView组件
flipper.addView(addImageView(R.drawable.java));
flipper.addView(addImageView(R.drawable.ee));
flipper.addView(addImageView(R.drawable.ajax));
flipper.addView(addImageView(R.drawable.xml));
flipper.addView(addImageView(R.drawable.classic));
// 初始化Animation数组
animations[0] = AnimationUtils.loadAnimation(
this, R.anim.left_in);
animations[1] = AnimationUtils.loadAnimation(
this, R.anim.left_out);
animations[2] = AnimationUtils.loadAnimation(
this, R.anim.right_in);
animations[3] = AnimationUtils.loadAnimation(
this, R.anim.right_out);
}


// 定义添加ImageView的工具方法
private View addImageView(int resId)
{
ImageView imageView = new ImageView(this);
imageView.setImageResource(resId);
imageView.setScaleType(ImageView.ScaleType.CENTER);
return imageView;
}


@Override
public boolean onFling(MotionEvent event1, MotionEvent event2,
float velocityX, float velocityY)
{
// 如果第一个触点事件的X座标大于第二个触点事件的X座标超过FLIP_DISTANCE
// 也就是手势从右向左滑。
if (event1.getX() - event2.getX() > FLIP_DISTANCE)
{
// 为flipper设置切换的的动画效果
flipper.setInAnimation(animations[0]);
flipper.setOutAnimation(animations[1]);
flipper.showPrevious();
return true;
}
// 如果第二个触点事件的X座标大于第一个触点事件的X座标超过FLIP_DISTANCE
// 也就是手势从右向左滑。
else if (event2.getX() - event1.getX() > FLIP_DISTANCE)
{
// 为flipper设置切换的的动画效果
flipper.setInAnimation(animations[2]);
flipper.setOutAnimation(animations[3]);
flipper.showNext();
return true;
}
return false;
}


@Override
public boolean onTouchEvent(MotionEvent event)
{
// 将该Activity上的触碰事件交给GestureDetector处理
return detector.onTouchEvent(event);
}


@Override
public boolean onDown(MotionEvent arg0)
{
return false;
}


@Override
public void onLongPress(MotionEvent event)
{
}


@Override
public boolean onScroll(MotionEvent event1
, MotionEvent event2, float arg2, float arg3)
{
return false;
}


@Override
public void onShowPress(MotionEvent event)
{
}


@Override
public boolean onSingleTapUp(MotionEvent event)
{
return false;
}
}
main.xml文件如下:
<?xml version="1.0" encoding="utf-8"?>   
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<!-- 定义ViewFlipper组件 -->
<ViewFlipper android:id="@+id/flipper"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    />
</LinearLayout>  


4.增减手势
Android使用GestureLibrary来代表手势库,并提供了GestureLibraries工具类来创建手势库,GestureLibraries提供了如下四个静态方法从不同位置加载手势库。
static GestureLibrary fromFile(String path):从path代表的文件中加载手势库
static GestureLibrary fromFile(File path):从path代表的文件中加载手势库
static GestureLibrary fromPrivateFile(Context context,String name):从指定的应用程序的数据文件中name文件中加载手势库
static GestureLibrary fromRawResource(Context context,int resourceld):从resourceld所代表的资源中加载手势库
Android还提供了专门的手势编辑组件:OnGestureOverlayView,它就像一个绘图组件,只是用户在组件上绘制的不是图形,而是手势。
为了监听OnGestureOverlayView组件上的手势事件,OnGestureOverlayView还提供了OnGestureListener,OnGesturePerformedListener,OnGesturingLietener三个监听器接口,这些监听器接口分别响应手势事件开始,结束,完成,取消事件,一般来说OnGesturePerformedListener是最常用的监听器,可用于在手势事件完成时提供响应。
package org.crazyit.io;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.GestureOverlayView.OnGesturePerformedListener;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;


public class AddGesture extends Activity
{
EditText editText;
GestureOverlayView gestureView;


@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.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 OnGesturePerformedListener()
{
@Override
public void onGesturePerformed(GestureOverlayView overlay,
final Gesture gesture)
{
// 加载save.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(AddGesture.this)
.setView(saveDialog)
.setPositiveButton("保存", new 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();
}
});
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="请在下面屏幕上绘制手势"/>
<!-- 使用手势绘制组件 -->
<android.gesture.GestureOverlayView
android:id="@+id/gesture"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gestureStrokeType="multiple" />
</LinearLayout>
save.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dip"
android:text="@string/gesture_name"
/>
<!-- 定义一个文本框来让用户输入手势名 -->  
<EditText
android:id="@+id/gesture_name"
android:layout_width="fill_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>
Ps:由于该程序需要将手势库保存在SD卡上,因此需要在AndroidManifest.xml中添加读写SD卡的权限。


5.识别用户手势
GestureLibrary提供了recognize(Gesture gas)方法来识别手势,该方法会返回该手势库中所有与gas相匹配的手势:两个手势的图形越相似,相似度越高。
package org.crazyit.io;
import java.util.ArrayList;
import android.app.Activity;
import android.app.AlertDialog;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.Prediction;
import android.gesture.GestureOverlayView.OnGesturePerformedListener;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.Toast;


public class RecogniseGesture extends Activity
{
// 定义手势编辑组件
GestureOverlayView gestureView;
// 记录手机上已有的手势库
GestureLibrary gestureLibrary;


@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 读取上一个程序所创建的手势库
gestureLibrary = GestureLibraries
.fromFile("/mnt/sdcard/mygestures");
if (gestureLibrary.load())
{
Toast.makeText(RecogniseGesture.this, "手势文件装载成功!",
Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(RecogniseGesture.this, "手势文件装载失败!",
Toast.LENGTH_LONG).show();
}
// 获取手势编辑组件
gestureView = (GestureOverlayView) findViewById(R.id.gesture);
// 为手势编辑组件绑定事件监听器
gestureView.addOnGesturePerformedListener(
new 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>(RecogniseGesture.this,
android.R.layout.simple_dropdown_item_1line
, result.toArray());
// 使用一个带List的对话框来显示所有匹配的手势
new AlertDialog.Builder(RecogniseGesture.this)
.setAdapter(adapter, null)
.setPositiveButton("确定", null).show();
}
else
{
Toast.makeText(RecogniseGesture.this
, "无法找到能匹配的手势!",
Toast.LENGTH_LONG).show();
}
}
});
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<!-- 使用手势编辑组件 -->
<android.gesture.GestureOverlayView
android:id="@+id/gesture"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gestureStrokeType="multiple" />
</LinearLayout>
result.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView 
android:id="@+id/show"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>













  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值