您的位置:首页 > 移动开发 > Android开发

Android手势基本知识

2014-10-17 18:46 190 查看
当用户用一根或多根手指触碰屏幕时,接收触摸事件的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>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: