全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

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这些大牛写的框架做铺垫。

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


# Android  # 手势  # ImageView  # Android中ImageView.src设置图片拉伸、填满控件的方法  # Android自定义圆角ImageView控件  # Android ImageView 不显示JPEG图片的问题解决  # Android 自定义imageview实现图片缩放实例详解  # Android中ImageView实现选择本地图片并显示功能  # Android自定义控件之圆形、圆角ImageView  # Android ImageView实现图片裁剪和显示功能  # Android实现ImageView阴影和图层效果  # Android ImageView的selector效果实例详解  # 回调  # 说了  # 的是  # 好啦  # 告诉你  # 双击  # 拖动  # 按下  # 看一下  # 什么时候  # 只需  # 小伙伴  # 要在  # 给了  # 可以看到  # 当我们  # 这三个  # 很多次  # 自己的  # 都是 


相关文章: 如何用PHP快速搭建高效网站?分步指南  制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?  如何通过云梦建站系统实现SEO快速优化?  如何注册花生壳免费域名并搭建个人网站?  代购小票制作网站有哪些,购物小票的简要说明?  如何快速搭建高效服务器建站系统?  简易网站制作视频教程,使用记事本编写一个简单的网页html文件?  如何通过西部建站助手安装IIS服务器?  厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?  建站之星如何防范黑客攻击与数据泄露?  金*站制作公司有哪些,金华教育集团官网?  移动端手机网站制作软件,掌上时代,移动端网站的谷歌SEO该如何做?  建站主机是否属于云主机类型?  儿童网站界面设计图片,中国少年儿童教育网站-怎么去注册?  制作公司内部网站有哪些,内网如何建网站?  免费制作小说封面的网站有哪些,怎么接网站批量的封面单?  建站之星2.7模板:企业网站建设与h5定制设计专题  专业的网站制作设计是什么,如何制作一个企业网站,建设网站的基本步骤有哪些?  大型企业网站制作流程,做网站需要注册公司吗?  如何获取上海专业网站定制建站电话?  如何用虚拟主机快速搭建网站?详细步骤解析  合肥制作网站的公司有哪些,合肥聚美网络科技有限公司介绍?  电商平台网站制作流程,电商网站如何制作?  C++中的Pimpl idiom是什么,有什么好处?(隐藏实现)  建站之星安装步骤有哪些常见问题?  如何解决VPS建站LNMP环境配置常见问题?  江苏网站制作公司有哪些,江苏书法考级官方网站?  ,在苏州找工作,上哪个网站比较好?  网站制作怎么样才能赚钱,用自己的电脑做服务器架设网站有什么利弊,能赚钱吗?  如何在腾讯云服务器上快速搭建个人网站?  建站ABC备案流程中有哪些关键注意事项?  如何零基础开发自助建站系统?完整教程解析  如何快速搭建高效WAP手机网站吸引移动用户?  nginx修改上传文件大小限制的方法  建站之星后台管理系统如何操作?  自助网站制作软件,个人如何自助建网站?  建站主机选购指南:核心配置优化与品牌推荐方案  北京的网站制作公司有哪些,哪个视频网站最好?  阿里云高弹*务器配置方案|支持分布式架构与多节点部署  C++中引用和指针有什么区别?(代码说明)  手机怎么制作网站教程步骤,手机怎么做自己的网页链接?  logo在线制作免费网站在线制作好吗,DW网页制作时,如何在网页标题前加上logo?  C++如何编写函数模板?(泛型编程入门)  php能控制zigbee模块吗_php通过串口与cc2530 zigbee通信【介绍】  建站之星收费标准详解:套餐费用及年费价格表一览  青岛网站建设如何选择本地服务器?  深圳企业网站制作设计,在深圳如何网上全流程注册公司?  导航网站建站方案与优化指南:一站式高效搭建技巧解析  教学论文网站制作软件有哪些,写论文用什么软件 ?  建站主机核心功能解析:服务器选择与网站搭建流程指南 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。