欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android手势ImageView三部曲 第二部

程序员文章站 2023-12-03 08:23:34
废话不多说了,还记得上一节android手势imageview三部曲(一)最后我们提及的那个框架么?这一节我们重点了掌握一下gesturedetector这个类相关的属性方...

废话不多说了,还记得上一节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,看看我们手指操作后相应的回调,于是我们运行代码:

Android手势ImageView三部曲 第二部

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

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

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);
 }

拖动手指返回结果:

Android手势ImageView三部曲 第二部

好啦~!! 终于看到我们久违的结果了,如果我们还是想用以前的写法,把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;
  }

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

Android手势ImageView三部曲 第二部

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

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

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中所有方法的回调,我们反过来再看一遍这些回调方法:

Android手势ImageView三部曲 第二部

好啦~!说了那么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);
 }

 };
}

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

Android手势ImageView三部曲 第二部

好啦~~ 这篇有点长,为什么花这么久去研究gesturedetector,这也是为了给下一节的movegesturedetector、rotategesturedetector、shovegesturedetector以及photoview这些大牛写的框架做铺垫。

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