Gesture手势及手势库
一、Gesture基础:
(一)、概念:
所谓手势,其实是指用户手指或者触摸笔在触摸屏上的连续触碰行为。比如在屏幕上从左至右划出一个动作,就是手势。再比如在屏幕上画一个圆圈也是手势。手势这种连续的触碰会形成某个方向上的移动趋势,也会形成一个不规则的几何图形。
应用程序中的手势就是:多个持续的触摸事件在屏幕上形成特定的形状。
Android对两种手势行为都提供了支持:
- 对于第一种手势行为而言,Android提供了手势检测,并为手势检测提供了相应的监听器;
- 对于第二种手势行为,Android允许开发者添加手势,并提供了相应的API识别用户手势。
(二)、原理:
对于触摸屏,其原生的消息无非按下、抬起、移动这几种,我们只需要简单重载onTouch或者设置触摸侦听器setOnTouchListener即可进行处理。
为了提高我们的APP的用户体验,有时候我们需要识别用户的手势,Android给我们提供了手势识别工具GestureDetector。 GestureDetector的工作原理是,当我们接收到用户触摸消息时,将这个消息交给GestureDetector去加工,我们通过设置侦听器获得GestureDetector处理后的手势。
二、手势检测:
(一)、操作步骤:
1、在View中实例化GestureDetector,一个GestureDetector实例代表一个手势检测器;
2、构建GestureDetector时需要一个参数GestureDetector.OnGestureListener。OnGestureListener是一个监听器,负责对用户的手势行为提供相应。
3、重写该View的onToutchEvent()方法,返回detector.onToutchEvent(event).
(二)、OnGestureListener里包含的事件处理方法:
1、abstract boolean
onDown(MotionEvent e);
// 单击,触摸屏按下时立刻触发
2、abstract boolean
onSingleTapUp(MotionEvent e);
// 用户在触摸屏上轻击并抬起,手指离开触摸屏时触发(而长按、滚动、滑动时,不会触发这个手势)
3、abstract void
onShowPress(MotionEvent e);
// 重压,触摸屏按下后片刻后抬起,会触发这个手势,如果迅速抬起则不会
4、abstract void
onLongPress(MotionEvent e);
// 长按,触摸屏按下后既不抬起也不移动,过一段时间后触发
5、abstract boolean
onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
// 滚动,触摸屏按下后移动
6、abstract boolean
onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
// 滑动,触摸屏按下后快速移动并抬起,会先触发滚动手势,跟着触发一个滑动手势
(三)、实例代码:
1、手势检测核心代码:
public class
MainActivity extends Activity {
private final static
String TAG =
"MainActivity";
private
GestureDetector detector;
@Override
protected void
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
detector =
new GestureDetector(this,
new OnGestureListener() {
@Override
publicboolean
onSingleTapUp(MotionEvent e) {
Log.i(TAG,
"==onSingleTapUp" + e.getAction());
returnfalse;
}
@Override
public void
onShowPress(MotionEvent e) {
Log.i(TAG,
"==onShowPress" + e.getAction());
}
@Override
public boolean
onScroll(MotionEvent e1, MotionEvent e2,
float
distanceX, float distanceY) {
Log.i(TAG,
"==onScroll" + e1.getAction() +
":" + e2.getAction());
returnfalse;
}
@Override
public void
onLongPress(MotionEvent e) {
Log.i(TAG,
"==onLongPress" + e.getAction());
}
@Override
public boolean
onFling(MotionEvent e1, MotionEvent e2,
float
velocityX, float velocityY) {
Log.i(TAG,
"==onFling" + e1.getAction() +
":" + e2.getAction());
returnfalse;
}
@Override
publicboolean
onDown(MotionEvent e) {
Log.i(TAG,
"==onDown" + e.getAction());
returnfalse;
}
});
}
@Override
publicboolean
onTouchEvent(MotionEvent event) {
return
detector.onTouchEvent(event);
}
}
2、通过手势缩放图片核心代码:
publicclass
MainActivity extends Activity {
private
GestureDetector detector;
private
ImageView imageView_main_show;
private
Bitmap bitmap;//
初始的图片资源
privateintwidth,
height;//
定义图片的宽、高
privatefloatcurrentScale
= 1;// 记录当前的缩放比
private
Matrix matrix;//
控制图片缩放的Matrix对象
@Override
protectedvoid
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView_main_show
= (ImageView) findViewById(R.id.imageView_main_show);
// 获取被缩放的源图片
matrix =
new Matrix();
bitmap = BitmapFactory.decodeResource(this.getResources(),
R.drawable.lijiang);
width =
bitmap.getWidth();//
获得位图宽
height =
bitmap.getHeight();//
获得位图高
detector =
new GestureDetector(this,
new OnGestureListener() {
@Override
publicboolean
onSingleTapUp(MotionEvent e) {
//
TODO Auto-generated method stub
returnfalse;
}
@Override
publicvoid
onShowPress(MotionEvent e) {
//
TODO Auto-generated method stub
}
@Override
publicboolean
onScroll(MotionEvent e1, MotionEvent e2,
float
distanceX, float distanceY) {
//
TODO Auto-generated method stub
returnfalse;
}
@Override
publicvoid
onLongPress(MotionEvent e) {
//
TODO Auto-generated method stub
}
@Override
publicboolean
onFling(MotionEvent e1, MotionEvent e2,
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, 0, 0);
BitmapDrawable bmDrawable = (BitmapDrawable)
imageView_main_show
.getDrawable();
// 如果图片还未回收,先强制回收该图片
if
(!bmDrawable.getBitmap().isRecycled()) {
bmDrawable.getBitmap().recycle();
}
// 根据原始位图和Matrix创建新图片
Bitmap bitmap_new = Bitmap.createBitmap(bitmap,
0, 0, width,
height,
matrix,
true);
// 显示新的位图
imageView_main_show.setImageBitmap(bitmap_new);
returnfalse;
}
@Override
publicboolean
onDown(MotionEvent e) {
//
TODO Auto-generated method stub
returnfalse;
}
});
}
@Override
publicboolean
onTouchEvent(MotionEvent event) {
returndetector.onTouchEvent(event);
}
}
3、通过手势实现翻页核心代码:
A、分析:
说到android的左右滑动效果我们可以说是在每个应用上面都可以看到这样的效果,不管是微博,还是QQ等。实现左右滑动的方式很多,有ViewPaer(这个需要android-support-v4.jar的支持),自定义实现Viewgroup,gallery等都可以达到这种效果。这里做下ViewFliper实现左右滑动的效果。
以下会用到的技术有:
1、ViewFlipper
2、GestureDetector
3、Animation
主要是这三个类在起作用。
B、原理:
向左向右滑动主要是依赖手势来控制,手势向右滑动就调用 viewFlipper.showNext();方法,同理,向左滑动就会去调用viewFlipper.showPrevious();方法。
publicclass
MainActivity extends Activity {
privatestaticfinal
String TAG =
"MainActivity";
private
ViewFlipper viewFlipper_main;
private
GestureDetector detector;
private
Animation leftInAnimation;
private
Animation leftOutAnimation;
private
Animation rightInAnimation;
private
Animation rightOutAnimation;
privatefinalintFLIP_DISTANCE
= 50;
@Override
protectedvoid
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewFlipper_main
= (ViewFlipper) findViewById(R.id.viewFlipper_main);
viewFlipper_main.addView(getImageView(R.drawable.img001));
viewFlipper_main.addView(getImageView(R.drawable.img012));
viewFlipper_main.addView(getImageView(R.drawable.img017));
viewFlipper_main.addView(getImageView(R.drawable.img021));
viewFlipper_main.addView(getImageView(R.drawable.img030));
viewFlipper_main.addView(getImageView(R.drawable.img031));
leftInAnimation
= AnimationUtils.loadAnimation(this,
R.anim.left_in);
leftOutAnimation
= AnimationUtils.loadAnimation(this,
R.anim.left_out);
rightInAnimation
= AnimationUtils.loadAnimation(this,
R.anim.right_in);
rightOutAnimation
= AnimationUtils
.loadAnimation(this,
R.anim.right_out);
detector =
new GestureDetector(this,
new OnGestureListener() {
@Override
publicboolean
onSingleTapUp(MotionEvent e) {
//
TODO Auto-generated method stub
returnfalse;
}
@Override
publicvoid
onShowPress(MotionEvent e) {
//
TODO Auto-generated method stub
}
@Override
publicboolean
onScroll(MotionEvent e1, MotionEvent e2,
float
distanceX, float distanceY) {
//
TODO Auto-generated method stub
returnfalse;
}
@Override
publicvoid
onLongPress(MotionEvent e) {
//
TODO Auto-generated method stub
}
@Override
publicboolean
onFling(MotionEvent event1, MotionEvent event2,
float
velocityX, float velocityY) {
/*
* 如果第一个触点事件的X座标大于第二个触点事件的X座标超过FLIP_DISTANCE 也就是手势从右向左滑。
*/
if
(event1.getX() - event2.getX() > FLIP_DISTANCE) {
Log.i(TAG,
"==向左:e1-e2=" + (event1.getX() - event2.getX()));
// 为flipper设置切换的的动画效果
viewFlipper_main.setInAnimation(leftInAnimation);
viewFlipper_main.setOutAnimation(leftOutAnimation);
viewFlipper_main.showPrevious();
returntrue;
}
/*
* 如果第二个触点事件的X座标大于第一个触点事件的X座标超过FLIP_DISTANCE 也就是手势从左向右滑。
*/
elseif
(event2.getX() - event1.getX() > FLIP_DISTANCE) {
Log.i(TAG,
"==向右:e2-e1=" + (event2.getX() - event1.getX()));
// 为flipper设置切换的的动画效果
viewFlipper_main.setInAnimation(rightInAnimation);
viewFlipper_main.setOutAnimation(rightOutAnimation);
viewFlipper_main.showNext();
returntrue;
}
returnfalse;
}
@Override
publicboolean
onDown(MotionEvent e) {
//
TODO Auto-generated method stub
returnfalse;
}
});
}
private
ImageView getImageView(int id) {
ImageView imageView =
new ImageView(this);
imageView.setImageResource(id);
return
imageView;
}
@Override
publicboolean
onTouchEvent(MotionEvent event) {
returndetector.onTouchEvent(event);
}
}
三、手势库增加手势:
(一)、概念:
Android中除了提供手势检测外,还允许应用程序把用户手势添加到指定文件中,以备以后使用——如果程序需要,当用户下次再画出该手势时,系统将可识别该手势。
Android中使用GestureLibrary来代表手势库,并提供了GestureLibraries工具类来创建手势库。GestureLibraries提供了四个静态方法从不同位置加载手势库。
除了GestureLibrary来管理手势之外,还提供了一个专门的手势编辑组件:GestureOverlayView
, 该组件就像一个“绘图组件”,只是用户在组件上绘制的不是图形,而是手势。为了监听GestureOverlayView 组件上的手势事件,Android提供了三个监听器接口:OnGestureListener、OnGesturePerformedListener、OnGesturingListener。OnGesturePerformedListener是最常见的监听器,它用于在手势事件完成时提供响应。
由于GestureOverlayView 并不是标准的视图组件,因此在界面布局中使用该组件时需要全限定类名(完整包路径类名)。
(二)、加载手势库:(GestureLibraries的4个静态方法)
- static GestureLibrary fromFile(String path)
- static GestureLibrary fromFile(File path)
- static GestureLibrary fromPrivateFile(Context context , String name)
- static GestureLibrary fromRawResource(Context context , int resourceId)
(三)、添加手势和识别手势:(GestureLibrary对象的方法)
- void addGesture(String entryName , Gesture gesture)
- 添加一个名为entryName的手势。
- Set<String> getGestureEntries()
- 获取该手势库中的所有手势的名称。
- ArrayList<Gesture> getGestures (String entryName)
- 获取entryName所对应的所有手势。
- ArrayList<Prediction> recognize (Gesture gesture)
- 从当前手势库中识别与gesture匹配的全部手势。
- void removeEntry (String entryName)
- 删除手势库中entryName对应的手势。
- void removeGesture (String entryName , Gesture gesture)
- 删除手势库中entryName、gesture 对应的手势。
- boolean save()
- 当向手势库中添加手势或从中删除手势后调用该方法以此来保存手势库。
(四)、实例代码:
publicclass
MainActivity extends Activity {
private
GestureOverlayView gestureOverlayView_main;
@Override
protectedvoid
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gestureOverlayView_main
= (GestureOverlayView) findViewById(R.id.gestureOverlayView_main);
gestureOverlayView_main
.addOnGesturePerformedListener(new
OnGesturePerformedListener() {
@Override
publicvoid
onGesturePerformed(GestureOverlayView overlay,
final
Gesture gesture) {
View dialog_savegesture = getLayoutInflater().inflate(
R.layout.dialog_savegesture,
null);
final
EditText editText_dialog_gesturename = (EditText) dialog_savegesture
.findViewById(R.id.editText_dialog_gesturename);
ImageView imageView_dialog_showgesture = (ImageView) dialog_savegesture
.findViewById(R.id.imageView_dialog_showgesture);
Bitmap bm = gesture.toBitmap(120, 120, 0, Color.CYAN);
imageView_dialog_showgesture.setImageBitmap(bm);
AlertDialog.Builder builder =
new AlertDialog.Builder(
MainActivity.this);
builder.setView(dialog_savegesture);
builder.setPositiveButton("保存",
new OnClickListener() {
@Override
publicvoid
onClick(DialogInterface dialog,
int
which) {
GestureLibrary gestureLibrary = GestureLibraries
.fromFile("/mnt/sdcard/mygestures");
gestureLibrary.addGesture(
editText_dialog_gesturename.getText()
.toString(), gesture);
gestureLibrary.save();
}
});
builder.setNegativeButton("取消",
null);
builder.show();
}
});
}
}
四、识别用户手势:
(一)、使用步骤:
1、GestureLibrary提供了recognize(Gesture gesture) 方法来识别手势,该方法将返回该手势库中所有与gesture匹配的所有手势,两个手势图形越相似,相似度越高。返回值是:ArrayList<Prediction> 。
2、Prediction中封装了手势的匹配信息:
- Prediction对象的name属性:手势名;
- Prediction对象的score属性:手势相似度。
(二)、实例代码:
publicclass
MainActivity extends Activity {
private
GestureOverlayView gestureOverlayView_main;
private
GestureLibrary gestureLibrary;
@Override
protectedvoid
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gestureLibrary
= GestureLibraries.fromFile("/mnt/sdcard/mygestures");
if
(gestureLibrary.load()) {
Toast.makeText(this,
"手势加载ok!", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(this,
"手势加载失败!", Toast.LENGTH_SHORT).show();
}
gestureOverlayView_main
= (GestureOverlayView) findViewById(R.id.gestureOverlayView_main);
gestureOverlayView_main
.addOnGesturePerformedListener(new
OnGesturePerformedListener() {
@Override
publicvoid
onGesturePerformed(GestureOverlayView overlay,
Gesture gesture) {
ArrayList<Prediction> predictions =
gestureLibrary
.recognize(gesture);
List<String> result =
new ArrayList<String>();
for
(Prediction prediction : predictions) {
if
(prediction.score > 2) {
result.add("与手势"
+ prediction.name +
"匹配相似度为:"
+ prediction.score);
}
}
if
(result.size() > 0) {
Toast.makeText(MainActivity.this,
result.toString(), Toast.LENGTH_SHORT)
.show();
}
else {
Toast.makeText(MainActivity.this,
"无匹配手势!",
Toast.LENGTH_SHORT).show();
}
}
});
}
}