Android手势ImageView三部曲 第二部

废话不多说了,还记得上一节Android手势ImageView三部曲(一)最后我们提及的那个框架么?这一节我们重点了掌握一下GestureDetector这个类相关的属性方法。

一、那么GestureDetector是干嘛的呢?

顾名思义,字面意思就是“手势检测器“的意思,还记得我们上一节中实现的GestureImageView么?我们在onTouchEvent中检测到了各种个样的手势(手指按下、抬起、什么时候属于拖拽、什么时候属于缩放)都是通过我们的计算得到的,但是有了GestureDetector这个类后,我们不需要自己做判断现在是什么手势了,GestureDetector会帮我们做好判断,完了后通过回调函数告诉你,就像官网所说的(This class should only be used with MotionEvents reported via touch (don't use for trackball events).)这个类仅仅是通过触碰检查事件的,而不是用于跟踪事件的,我检测到了事件,然后告诉你,至于你需要怎么处理这个事件,那就是你自己的事了。

GestureDetector的一些具体的api大家可以去查看谷歌官方文档或启舰大神的博客:

https://developer.android.google.cn/reference/android/view/GestureDetector.html

Android手势识别器GestureDetector使用详解

说了这么多估计你都有点累了,下面让我们看看具体怎么使用:

偷一下懒,我就直接用 Android手势ImageView三部曲(一)
中的MatrixImageView类改改代码了:

public class MatrixImageView extends ImageView { private static final int MODE_NONE = 190; private static final int MODE_DRAG = 468; private static final int MODE_ZOOM = 685; private int mode; private float startX, startY; private float midX, midY; private Matrix currMatrix, savedMatrix; private float preRotate, rotate; private float preSpacing; private GestureDetector detector; public MatrixImageView(Context context, AttributeSet attrs) { super(context, attrs); initView(); detector=new GestureDetector(context,onGestureListener); } private void initView() { mode = MODE_NONE; currMatrix = new Matrix(); savedMatrix = new Matrix(); DisplayMetrics dm = getResources().getDisplayMetrics(); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test); bitmap = Bitmap.createScaledBitmap(bitmap, dm.widthPixels, dm.heightPixels, true); setImageBitmap(bitmap); } @Override public boolean onTouchEvent(MotionEvent event) { return detector.onTouchEvent(event); } private GestureDetector.SimpleOnGestureListener onGestureListener=new GestureDetector.SimpleOnGestureListener(){ @Override public boolean onSingleTapUp(MotionEvent e) { Log.e("TAG", "====onSingleTapUp====="); return super.onSingleTapUp(e); } @Override public void onLongPress(MotionEvent e) { Log.e("TAG", "====onLongPress====="); super.onLongPress(e); } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { Log.e("TAG", "====onScroll====="); Log.e("TAG", "distanceX===>"+distanceX); Log.e("TAG", "distanceY===>"+distanceY); return super.onScroll(e1, e2, distanceX, distanceY); } @Override public void onShowPress(MotionEvent e) { Log.e("TAG", "====onShowPress====="); super.onShowPress(e); } @Override public boolean onDown(MotionEvent e) { Log.e("TAG", "====onDown====="); return true; } @Override public boolean onDoubleTap(MotionEvent e) { Log.e("TAG", "====onDoubleTap====="); return super.onDoubleTap(e); } @Override public boolean onDoubleTapEvent(MotionEvent e) { Log.e("TAG", "====onDoubleTapEvent====="); return super.onDoubleTapEvent(e); } @Override public boolean onSingleTapConfirmed(MotionEvent e) { Log.e("TAG", "====onSingleTapConfirmed====="); return super.onSingleTapConfirmed(e); } @Override public boolean onContextClick(MotionEvent e) { Log.e("TAG", "====onContextClick====="); return super.onContextClick(e); } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { Log.e("TAG", "====onFling====="); Log.e("TAG", "velocityX===>"+velocityX); Log.e("TAG", "velocityY===>"+velocityY); return super.onFling(e1, e2, velocityX, velocityY); } }; }

首先我们在构造方法中创建一个手势监测器的对象GestureDetector:

public MatrixImageView(Context context, AttributeSet attrs) { super(context, attrs); initView(); detector=new GestureDetector(context,onGestureListener); }

GestureDetector既然是监听我们的手势的工具类,那我们是不是得把我们得手势交给它呢? 是的!! 于是我们在onTouchEvent中把事件交给GestureDetector:

@Override public boolean onTouchEvent(MotionEvent event) { return detector.onTouchEvent(event); }

那我们把事件交给了GestureDetector,GestureDetector处理完毕后我们怎么知道呢? 还记得我们创建GestureDetector对象的时候传递的参数吗?

detector=new GestureDetector(context,onGestureListener);

我们传递给了GestureDetector一个onGestureListener对象,GestureDetector检查完毕手势后,会调用onGestureListener中的方法进行回调,我们只需要在onGestureListener对象的相应方法中作出处理就可以了:

private GestureDetector.SimpleOnGestureListener onGestureListener=new GestureDetector.SimpleOnGestureListener(){ @Override public boolean onSingleTapUp(MotionEvent e) { Log.e(“TAG”, “====onSingleTapUp=====”); return super.onSingleTapUp(e); } @Override public void onLongPress(MotionEvent e) { Log.e("TAG", "====onLongPress====="); super.onLongPress(e); } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { Log.e("TAG", "====onScroll====="); Log.e("TAG", "distanceX===>"+distanceX); Log.e("TAG", "distanceY===>"+distanceY); return super.onScroll(e1, e2, distanceX, distanceY); } @Override public void onShowPress(MotionEvent e) { Log.e("TAG", "====onShowPress====="); super.onShowPress(e); } @Override public boolean onDown(MotionEvent e) { Log.e("TAG", "====onDown====="); return super.onDown(e); } @Override public boolean onDoubleTap(MotionEvent e) { Log.e("TAG", "====onDoubleTap====="); return super.onDoubleTap(e); } @Override public boolean onDoubleTapEvent(MotionEvent e) { Log.e("TAG", "====onDoubleTapEvent====="); return super.onDoubleTapEvent(e); } @Override public boolean onSingleTapConfirmed(MotionEvent e) { Log.e("TAG", "====onSingleTapConfirmed====="); return super.onSingleTapConfirmed(e); } @Override public boolean onContextClick(MotionEvent e) { Log.e("TAG", "====onContextClick====="); return super.onContextClick(e); } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { Log.e("TAG", "====onFling====="); Log.e("TAG", "velocityX===>"+velocityX); Log.e("TAG", "velocityY===>"+velocityY); return super.onFling(e1, e2, velocityX, velocityY); } };

我们先不管其中方法啥时候调用,我们先重写它的所有方法,然后打上log,看看我们手指操作后相应的回调,于是我们运行代码:

是的,没错!就只是一张图片,因为我们也只是显示了一张图片:

我们轻轻的点击一下屏幕:

03-02 20:47:41.367 1798-1798/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 20:47:41.466 1798-1798/com.leo.gestureimageview E/TAG: ====onShowPress=====
03-02 20:47:41.967 1798-1798/com.leo.gestureimageview E/TAG: ====onLongPress=====

轻轻的点击一下屏幕:
我们可以看到log执行顺序:onDown->onShowPress->onLongPress

我们点击屏幕按下,然后过一会再放开:

03-02 21:51:27.121 17138-17138/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 21:51:27.222 17138-17138/com.leo.gestureimageview E/TAG: ====onShowPress=====
03-02 21:51:27.722 17138-17138/com.leo.gestureimageview E/TAG: ====onLongPress=====

我们滑动一下手指:

03-02 21:51:27.121 17138-17138/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 21:51:27.222 17138-17138/com.leo.gestureimageview E/TAG: ====onShowPress=====
03-02 21:51:27.722 17138-17138/com.leo.gestureimageview E/TAG: ====onLongPress=====

不管我们怎么样操作,打印的log总是这三个方法? 这是咋回事呢? 如果看到这里你有疑问的话,那我告诉你,你Android事件传递机制掌握的还不是很好,为什么这么说呢?? 下面我们带着疑问看看源码:

猜都可以猜到GestureDetector处理手势的代码肯定在onTouchEvent方法中,那么我们看一下onTouchEvent方法:

public boolean onTouchEvent(MotionEvent ev) { if (mDoubleTapListener != null) { boolean hadTapMessage = mHandler.hasMessages(TAP); if (hadTapMessage) mHandler.removeMessages(TAP); if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage && isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) { // This is a second tap mIsDoubleTapping = true; // Give a callback with the first tap of the double-tap handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent); // Give a callback with down event of the double-tap handled |= mDoubleTapListener.onDoubleTapEvent(ev); } else { // This is a first tap mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT); } } mDownFocusX = mLastFocusX = focusX; mDownFocusY = mLastFocusY = focusY; if (mCurrentDownEvent != null) { mCurrentDownEvent.recycle(); } mCurrentDownEvent = MotionEvent.obtain(ev); mAlwaysInTapRegion = true; mAlwaysInBiggerTapRegion = true; mStillDown = true; mInLongPress = false; mDeferConfirmSingleTap = false; if (mIsLongpressEnabled) { mHandler.removeMessages(LONG_PRESS); mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT + LONGPRESS_TIMEOUT); } mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT); handled |= mListener.onDown(ev); break; }

代码太多了,那为什么我们只收到了onDown、onShowPress、onLongPress这三个方法的回调呢?
我们知道,当我们手指刚按下屏幕的时候,ACTION_DOWN会执行,然后我们看到这么一行代码:

handled |= mListener.onDown(ev);

mListener是我们传递的SimpleOnGestureListener,于是就看到了控制台的第一个log:

03-02 21:51:29.706 17138-17138/com.leo.gestureimageview E/TAG: ====onDown=====

我们的onDown是打印了,然后handled |= mListener.onDown(ev);看一下我们返回的是什么值:

@Override public boolean onDown(MotionEvent e) { Log.e("TAG", "====onDown====="); return super.onDown(e); }

我们直接返回了super.onDown(e),接着我们看一下父类返回的是什么:

public boolean onDown(MotionEvent e) { return false; }

可以看到,父类直接返回了false,所以handled此时为false,然后当ACTION_DOWN执行完毕后,就回到了我们的自定义view中的onTouchEvent方法中了:

@Override public boolean onTouchEvent(MotionEvent event) { return detector.onTouchEvent(event); }

此时我们的view中的onTouchEvent 方法返回的是false,到了这里懂事件传递机制的小伙伴都懂,当我们的onTouchEvent返回了false的话,后面的事件都将接收不到了,也就是说只能执行ACTION_DOWN,那么有些小伙伴可能又要说了,那我把view的clickable或者longclickable设置成true,事件不就可以传递了么?
好的~! 我们试一试:

<com.leo.gestureimageview.MatrixImageView android:clickable="true" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="matrix" />

运行代码,还是只打印了那三个方法,那这又是怎么回事呢? 还记得我们view的onTouchEvent方法么?我们是这么写的:

@Override public boolean onTouchEvent(MotionEvent event) { return detector.onTouchEvent(event); }

如果改成这样再试试:

@Override public boolean onTouchEvent(MotionEvent event) { detector.onTouchEvent(event); return super.onTouchEvent(event); }

拖动手指返回结果:

好啦~!! 终于看到我们久违的结果了,如果我们还是想用以前的写法,把onTouchEvent的返回结果交给GestureDetector处理该怎么做呢?

@Override public boolean onTouchEvent(MotionEvent event) { return detector.onTouchEvent(event); }

我们只需要在回调方法的onDown中返回true即可:

@Override public boolean onDown(MotionEvent e) { Log.e("TAG", "====onDown====="); return true; }

我们再次运行代码并拖动手指:

好啦~! 说了那么多不知道小伙伴们理解了没?还是不理解的小伙伴可以去看看我前几篇事件传递的博客,嘻嘻~我们还是快点往下走吧….

我们长按一下屏幕然后提起手指:

03-02 22:29:37.361 22104-22104/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 22:29:37.367 22104-22104/com.leo.gestureimageview E/TAG: ====onSingleTapUp=====
03-02 22:29:37.663 22104-22104/com.leo.gestureimageview E/TAG: ====onSingleTapConfirmed=====

执行了onDown=>onSingleTapUp=>onSingleTapConfirmed.

我们快速点击一下屏幕:

03-02 22:31:48.603 22104-22104/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 22:31:48.610 22104-22104/com.leo.gestureimageview E/TAG: ====onSingleTapUp=====
03-02 22:31:48.903 22104-22104/com.leo.gestureimageview E/TAG: ====onSingleTapConfirmed=====

执行了onDown=>onSingleTapUp=>onSingleTapConfirmed.

然后我们再次滑动手指:

03-02 22:34:41.820 22104-22104/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 22:34:41.920 22104-22104/com.leo.gestureimageview E/TAG: ====onShowPress=====
03-02 22:34:42.018 22104-22104/com.leo.gestureimageview E/TAG: ====onScroll=====
03-02 22:34:42.018 22104-22104/com.leo.gestureimageview E/TAG: distanceX===>-117.13138
03-02 22:34:42.018 22104-22104/com.leo.gestureimageview E/TAG: distanceY===>75.100464
03-02 22:34:42.036 22104-22104/com.leo.gestureimageview E/TAG: ====onScroll=====
03-02 22:34:42.036 22104-22104/com.leo.gestureimageview E/TAG: distanceX===>-75.859314

执行顺序:onDown=》onShowPress=》onScroll(很多次)

最后我们手指拖动距离长一点再快一点:

03-02 22:47:42.453 5103-5103/com.leo.gestureimageview E/TAG: distanceX===>-274.69336
03-02 22:47:42.453 5103-5103/com.leo.gestureimageview E/TAG: distanceY===>-0.34838867
03-02 22:47:42.460 5103-5103/com.leo.gestureimageview E/TAG: ====onFling=====
03-02 22:47:42.460 5103-5103/com.leo.gestureimageview E/TAG: velocityX===>27284.943
03-02 22:47:42.460 5103-5103/com.leo.gestureimageview E/TAG: velocityY===>-95.6131

前 面还有一段log没给出了,调用方法顺序为:onDown=》onShowPress=》onScroll(很多次)=》最后松开手指的时候onFling();

好了~! 到这里,我们的回调方法中还有几个没有被调用,就是监听双击事件的时候,于是我们双击屏幕:

03-02 22:50:34.786 5103-5103/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 22:50:34.793 5103-5103/com.leo.gestureimageview E/TAG: ====onSingleTapUp=====
03-02 22:50:34.924 5103-5103/com.leo.gestureimageview E/TAG: ====onDoubleTap=====
03-02 22:50:34.924 5103-5103/com.leo.gestureimageview E/TAG: ====onDoubleTapEvent=====
03-02 22:50:34.924 5103-5103/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 22:50:34.932 5103-5103/com.leo.gestureimageview E/TAG: ====onDoubleTapEvent=====

我们双击屏幕执行的方法为:
onDown=>onSingleTapUp=>onDoubleTap=>onDoubleTapEvent=>onDoubleTapEvent
可见,执行了一次onDoubleTap,两次onDoubleTapEvent

好啦~! 看完了SimpleOnGestureListener中所有方法的回调,我们反过来再看一遍这些回调方法:

好啦~!说了那么api内容,下面写个小例子用一下GestureDetector:

package com.leo.gestureimageview; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.widget.ImageView; public class MatrixImageView extends ImageView { private Matrix currMatrix; private GestureDetector detector; public MatrixImageView(Context context, AttributeSet attrs) { super(context, attrs); initView(); detector=new GestureDetector(context,onGestureListener); } private void initView() { currMatrix = new Matrix(); DisplayMetrics dm = getResources().getDisplayMetrics(); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test); bitmap = Bitmap.createScaledBitmap(bitmap, dm.widthPixels, dm.heightPixels, true); setImageBitmap(bitmap); } @Override public boolean onTouchEvent(MotionEvent event) { return detector.onTouchEvent(event); } private float currX; private float currY; private GestureDetector.SimpleOnGestureListener onGestureListener=new GestureDetector.SimpleOnGestureListener(){ @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { Log.e("TAG", "====onScroll====="); Log.e("TAG", "distanceX===>"+distanceX); Log.e("TAG", "distanceY===>"+distanceY); currX-=distanceX; currY-=distanceY; currMatrix.reset(); currMatrix.postTranslate(currX,currY); setImageMatrix(currMatrix); return super.onScroll(e1, e2, distanceX, distanceY); } }; }

代码很短,想必大家都看得懂,就是一个随着手指移动而移动的图片:

好啦~~ 这篇有点长,为什么花这么久去研究GestureDetector,这也是为了给下一节的MoveGestureDetector、RotateGestureDetector、ShoveGestureDetector以及PhotoView这些大牛写的框架做铺垫。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

时间: 2024-09-20 11:47:08

Android手势ImageView三部曲 第二部的相关文章

Android手势ImageView三部曲 第三部

接着上一节 Android手势ImageView三部曲(二)的往下走,我们讲到了github上的GestureDetector框架, 先附上github链接: https://github.com/Almeros/android-gesture-detectors 其实把这个框架的主体思想也是参考的Android自带的ScaleGestureDetector工具类,ScaleGestureDetector估计是参考的GestureDetector工具类,不管谁参考谁的,既然被我们遇到了,我们就要

Android手势ImageView三部曲 第一部

这几天一直在研究github上的PhotoView跟GestureImageView,发现写的都很牛,看了很久的代码,于是打算把自己所看的一些东西总结一下,内容还是很多的,但是很有含金量哈-- 先附上两个开源项目的链接: GestureImageView: https://github.com/jasonpolites/gesture-imageview PhotoView:https://github.com/chrisbanes/PhotoView 这样说有点乏味哈,先看看我们今天要实现的效

今何在时隔12年推出“西游三部曲”第二部

遭遇李宗伟 从2004年第一次在团体赛中出任中国队第一单打以来,我在团体赛中保持了长达三年的全胜.这一纪录在2007年的苏迪曼杯混合团体赛上被终结. 小组赛最后一轮,中国队迎战马来西亚.当时,我们已经锁定了小组第一出线.而对手只有打败我们,才有可能晋级苏杯四强.论整体实力,谁都知道马来西亚的团体还不足以与中国队抗衡,大家关注的无非就是李宗伟和我的对决. 开赛以来,中国队总是以5比0横扫对手,媒体早就觉得无趣了.于是,在中马对抗前,有记者问李永波教练,面对背水一战的马来西亚队,是否会很难对付.李导

传《激战2》小说系列三部曲第二部12月发售

之前报道了< 激战2>小说的消息,这次的小说将以3部曲的形式加以呈现,第一部<阿斯卡隆的幽灵(Ghosts of Ascalon)>将于本月底发售,而目前该系列的第二部也有了动静. 外国的知名网上书店Simon和Schuster透露了新的,目前还没确定标题的<激战>小说新作将会在今年的12月28日上市发售.该网站的消息来源相当可靠,之前<阿斯卡隆的幽灵(Ghosts of Ascalon)>发售的消息就是来自于以上的网站. 相信<激战>小说的第二

Android手势滑动实现ImageView缩放图片大小_Android

本文推出了两种Android手势实现ImageView缩放图片大小的方法,分享给大家供大家参考,具体内容如下 方法一:将以下代码写到MulitPointTouchListener.java中,然后对你相应的图片进行OnTouchListener. 例如:imageView.setOnTouchListener(new MulitPointTouchListener ()); 在xml中要将ImageView的缩放格式改成Matrix 例如:android:scaleType="matrix&qu

Android使用ImageView实现支持手势缩放效果_Android

TouchImageView继承自ImageView具有ImageView的所有功能:除此之外,还有缩放.拖拽.双击放大等功能,支持viewpager和scaletype,并伴有动画效果. sharedConstructing private void sharedConstructing(Context context) { super.setClickable(true); this.context = context; mScaleDetector = new ScaleGestureDe

Android手势滑动实现ImageView缩放图片大小

本文推出了两种Android手势实现ImageView缩放图片大小的方法,分享给大家供大家参考,具体内容如下 方法一: 将以下代码写到MulitPointTouchListener.java中,然后对你相应的图片进行OnTouchListener. 例如:imageView.setOnTouchListener(new MulitPointTouchListener ()); 在xml中要将ImageView的缩放格式改成Matrix 例如:android:scaleType="matrix&q

Android手势控制实现缩放、移动图片_Android

本文实例为大家分享了Android手势控制实现缩放.移动图片的方法,供大家参考,具体内容如下 新建一个触摸监听器类 package com.liyong.btprinter; import android.graphics.Matrix; import android.graphics.PointF; import android.util.FloatMath; import android.view.MotionEvent; import android.view.View; import a

Android 手势操作编程详解_Android

      手势操作在我们使用智能设备的过程中奉献了不一样的体验.Android开发中必然会进行手势操作方面的编程.那么它的原理是怎样的呢?我们如何进行手势操作编程呢?        手势操作原理        首先,在Android系统中,每一次手势交互都会依照以下顺序执行.        1. 接触接触屏一刹那,触发一个MotionEvent事件.        2. 该事件被OnTouchListener监听,在其onTouch()方法里获得该MotionEvent对象.        3