Android 提供了基本的手势检测工具类,我们拿来就可以用的,主要有两个GestureDetector和ScaleGestureDetector类,如何使用,具体见下面的介绍。
先看看这个类的结构,有三个接口组成,包含一个SimpleOnGestureListener类,而这个类是对三个接口的实现。
通过自定义view并完成双击图片可以放大,再次双击还原,并实现随意拖动图片功能,介绍这两个手势工具类的使用情况。先看看GestureDetector.OnGestureListener的函数:
public class GestureDetector {
/**
* The listener that is used to notify when gestures occur.
* If you want to listen for all the different gestures then implement
* this interface. If you only want to listen for a subset it might
* be easier to extend {@link SimpleOnGestureListener}.
*/
public interface OnGestureListener {
/**
* Notified when a tap occurs with the down {@link MotionEvent}
* that triggered it. This will be triggered immediately for
* every down event. All other events should be preceded by this.
*
* @param e The down motion event.
*/
boolean onDown(MotionEvent e);//手指触摸到屏幕就会触发。
/**
* The user has performed a down {@link MotionEvent} and not performed
* a move or up yet. This event is commonly used to provide visual
* feedback to the user to let them know that their action has been
* recognized i.e. highlight an element.
*
* @param e The down motion event
*/
void onShowPress(MotionEvent e);//按着不放的过程,知道触发onLongPress,介于onDown与onLongPress之间。
/**
* Notified when a tap occurs with the up {@link MotionEvent}
* that triggered it.
*
* @param e The up motion event that completed the first tap
* @return true if the event is consumed, else false
*/
boolean onSingleTapUp(MotionEvent e);//单击屏幕拿起时触发。
/**
* Notified when a scroll occurs with the initial on down {@link MotionEvent} and the
* current move {@link MotionEvent}. The distance in x and y is also supplied for
* convenience.
*
* @param e1 The first down motion event that started the scrolling.
* @param e2 The move motion event that triggered the current onScroll.
* @param distanceX The distance along the X axis that has been scrolled since the last
* call to onScroll. This is NOT the distance between {@code e1}
* and {@code e2}.
* @param distanceY The distance along the Y axis that has been scrolled since the last
* call to onScroll. This is NOT the distance between {@code e1}
* and {@code e2}.
* @return true if the event is consumed, else false
*/
boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);//在屏幕中滑动时触发,随意移动图片就在这里触发的。
/**
* Notified when a long press occurs with the initial on down {@link MotionEvent}
* that trigged it.
*
* @param e The initial on down motion event that started the longpress.
*/
void onLongPress(MotionEvent e);//长按时触发。
/**
* Notified of a fling event when it occurs with the initial on down {@link MotionEvent}
* and the matching up {@link MotionEvent}. The calculated velocity is supplied along
* the x and y axis in pixels per second.
*
* @param e1 The first down motion event that started the fling.
* @param e2 The move motion event that triggered the current onFling.
* @param velocityX The velocity of this fling measured in pixels per second
* along the x axis.
* @param velocityY The velocity of this fling measured in pixels per second
* along the y axis.
* @return true if the event is consumed, else false
*/
boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);//快速滑动屏幕时触发。
}
在上面的代码中我已经解释各个函数的功能,通过这个手势工具类实现随意的拖动图片。
第一步就是在onTouchEvent中调用这个手势mScaleGestureDetector.onTouchEvent(event),这样就开始使用手势了。如何实现随意的拖动能,涉及到matrix的使用,可以详见自行百度下使用方法。
在使用matrix时,在构造函数中调用 setScaleType(ScaleType.MATRIX);否则就没有效果,看源码中说,从xml配置文件加载都必须要要加这个配置。
matrix中有个postTranslate函数,对矩阵进行位移变换的,onscroll中提供了位移变化量,所以就特别简单了,通过这个位移函数就可以实现图片的随意的拖动了。而setImageMatrix(mMatrix) 的作用就是不断加载数据然后刷新,这样才能实现真正的移动。
public interface OnDoubleTapListener {
/**
* Notified when a single-tap occurs.
*
* Unlike {@link OnGestureListener#onSingleTapUp(MotionEvent)}, this
* will only be called after the detector is confident that the user's
* first tap is not followed by a second tap leading to a double-tap
* gesture.
*
* @param e The down motion event of the single-tap.
* @return true if the event is consumed, else false
*/
boolean onSingleTapConfirmed(MotionEvent e);//单击事件触发
/**
* Notified when a double-tap occurs.
*
* @param e The down motion event of the first tap of the double-tap.
* @return true if the event is consumed, else false
*/
boolean onDoubleTap(MotionEvent e);//双击事件触发。
/**
* Notified when an event within a double-tap gesture occurs, including
* the down, move, and up events.
*
* @param e The motion event that occurred during the double-tap gesture.
* @return true if the event is consumed, else false
*/
boolean onDoubleTapEvent(MotionEvent e);//一个完整的双击事件,包括down,move,up事件。根据日志感觉和上面哪个没有区别。
在onDoubleTap(MotionEvent e)中实现放大和还原功能即可,也是通过matrix实现的,详见代码。
还剩一个onContextLisenter类就不介绍了,基本上用不上。
ScaleGestureDetector 是缩放类工具栏,通过onTouchEvent(MotioinEvent event)或onTouch(MotionEvent event)中event事件来实现手势触发。主要有以下集中方法:
Demo:
public class GestureImageViewer extends ImageView implements GestureDetector.OnGestureListener,
ScaleGestureDetector.OnScaleGestureListener,
GestureDetector.OnDoubleTapListener {
private static final String TAG = GestureImageViewer.class.getSimpleName();
private GestureDetector mGestureDetector = null;
private ScaleGestureDetector mScaleGestureDetector = null;
private Matrix mMatrix = new Matrix();
private float startDis = 0f;
private float oldAngle = 0f;
public GestureImageViewer(Context context) {
super(context);
}
public GestureImageViewer(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setScaleType(ScaleType.MATRIX);//这个千万不要忘记了,否则没有效果。
mGestureDetector = new GestureDetector(context, this);
mScaleGestureDetector = new ScaleGestureDetector(context, this);
mMatrix.set(this.getImageMatrix());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mGestureDetector.onTouchEvent(event)) {
return true;
}
if (mScaleGestureDetector.onTouchEvent(event)) {//放缩的手势类需要在这调用下
return true;
}
return true;
}
@Override
public boolean onDown(MotionEvent e) {
Log.i(TAG, "onDown");
return false;
}
@Override
public void onShowPress(MotionEvent e) {
Log.i(TAG, "onShowPress");
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
Log.i(TAG, "onSingleTapUp");
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent event, float distanceX, float distanceY) {
Log.i(TAG, "onScroll");
mMatrix.postTranslate(-distanceX, -distanceY);
setImageMatrix(mMatrix);
return false;
}
private float calculateAngle(MotionEvent event) {
return (float) Math.toDegrees(Math.atan2(event.getY(0) - event.getY(1), event.getX(0) - event.getX(1)));
}
@Override
public void onLongPress(MotionEvent e) {
Log.i(TAG, "onLongPress");
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.i(TAG, "onFling" + "velocityX:" + velocityX + "velocityY:" + velocityY);
return false;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
mMatrix.set(getImageMatrix());
float scaleFactor = detector.getCurrentSpan() / startDis;
mMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
setImageMatrix(mMatrix);
startDis = detector.getCurrentSpan();
Log.i(TAG, "onScale" + scaleFactor);
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
startDis = detector.getCurrentSpan();
Log.i(TAG, "onScaleBegin");
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
Log.i(TAG, "onScaleEnd");
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Log.i(TAG, "onSingleTapConfirmed");
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
Log.i(TAG, "onDoubleTap");
mMatrix.set(getImageMatrix());
float[] values = new float[9];
mMatrix.getValues(values);
float scaleFactor = values[Matrix.MSCALE_X];
if (scaleFactor == 1) {
mMatrix.postScale(1.5f, 1.5f, e.getX(), e.getY());
} else {
mMatrix.reset();
}
setImageMatrix(mMatrix);
return true;
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
Log.i(TAG, "onDoubleTapEvent");
return true;
}
}