一、Gesture基础:
(一)、概念:
所谓手势,其实是指用户手指或者触摸笔在触摸屏上的连续触碰行为。比如在屏幕上从左至右划出一个动作,就是手势。再比如在屏幕上画一个圆圈也是手势。手势这种连续的触碰会形成某个方向上的移动趋势,也会形成一个不规则的几何图形。
应用程序中的手势就是:多个持续的触摸事件在屏幕上形成特定的形状。
Android对两种手势行为都提供了支持:
- 对于第一种手势行为而言,Android提供了手势检测,并为手势检测提供了相应的监听器;
- 对于第二种手势行为,Android允许开发者添加手势,并提供了相应的API识别用户手势。
(二)、原理:
对于触摸屏,其原生的消息无非按下、抬起、移动这几种,我们只需要简单重载onTouch或者设置触摸侦听器setOnTouchListener即可进行处理。
为了提高我们的APP的用户体验,有时候我们需要识别用户的手势,Android给我们提供了手势识别工具
GestureDetector。 GestureDetector的工作原理是,当我们接收到用户触摸消息时,将这个消息交给GestureDetector去加工,我们通过设置侦听器获得GestureDetector处理后的手势。
二、手势检测:
(一)、
操作步骤:
1、在Activity中实例化
GestureDetector,一个GestureDetector实例代表一个
手势检测器;
2、构建GestureDetector时需要一个参数GestureDetector.
OnGestureListener。OnGestureListener是一个监听器,负责对用户的手势行为提供相应。
3、重写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
;
// 初始的图片资源
privateint
width
,
height
;
// 定义图片的宽、高
privatefloat
currentScale
= 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) {
return
detector
.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
;
privatefinalint
FLIP_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) {
return
detector
.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)
- Set getGestureEntries()
- ArrayList getGestures (String entryName)
- ArrayList recognize (Gesture gesture)
-
- 从当前手势库中识别与gesture匹配的全部手势。
- void removeEntry (String 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
。
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 predictions =
gestureLibrary
.recognize(gesture);
List result =
new
ArrayList();
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();
}
}
});
}
}