当前位置: 移动技术网 > 移动技术>移动开发>Android > Android手势ImageView三部曲 第一部

Android手势ImageView三部曲 第一部

2019年07月24日  | 移动技术网移动技术  | 我要评论

这几天一直在研究github上的photoview跟gestureimageview,发现写的都很牛,看了很久的代码,于是打算把自己所看的一些东西总结一下,内容还是很多的,但是很有含金量哈~~

先附上两个开源项目的链接:

gestureimageview:

photoview:https://github.com/chrisbanes/photoview

这样说有点乏味哈,先看看我们今天要实现的效果:

当一个手指按住图片的时候,此时的效果为拖拽的效果。
当两个手指按住图片的时候,手指旋转则图片跟着旋转,手指缩放则图片缩放。
效果图大致为(我模拟器不太好模拟旋转):

这里写图片描述

好了下面我们来实现一下这个手势缩放imageview:

首先我们创建一个叫matriximageview的类去继承imageview,然后重写其构造方法(我就不考虑直接new的情况了哈):

public class matriximageview2 extends imageview {
 public matriximageview2(context context, attributeset attrs) {
  super(context, attrs);
  initview();
 }
}

然后我们需要定义几种当前view的状态:
mode_none(初始状态);
mode_drag(拖拽状态);
mode_zoom(两个手指缩放状态)

public class matriximageview2 extends imageview {
 private static final int mode_none = 190;
 private static final int mode_drag = 468;
 private static final int mode_zoom = 685;
 .....
}

我们对imageview做旋转、缩放、位移等操作主要是用到imageview的这个方法:

 /**
  * adds a transformation {@link matrix} that is applied
  * to the view's drawable when it is drawn. allows custom scaling,
  * translation, and perspective distortion.
  *
  * @param matrix the transformation parameters in matrix form
  */
 public void setimagematrix(matrix matrix) {
  // collapse null and identity to just null
  if (matrix != null && matrix.isidentity()) {
   matrix = null;
  }

  // don't invalidate unless we're actually changing our matrix
  if (matrix == null && !mmatrix.isidentity() ||
    matrix != null && !mmatrix.equals(matrix)) {
   mmatrix.set(matrix);
   configurebounds();
   invalidate();
  }
 }

利用的是matrix这个类(对这个类不懂的童鞋自己去查资料哈~),然后通过监听我们的ontouchevent方法获取当前手势操作,然后对matrix进行相应操作,改变图片的状态。

代码比较短,而且我每行都注释了,我就直接给代码了:

matriximageview.java:

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.view.motionevent;
import android.widget.imageview;

public class matriximageview2 extends imageview {
 private static final int mode_none = 190;
 private static final int mode_drag = 468;
 private static final int mode_zoom = 685;

 //当前mode
 private int mode;
 //手指按下时候的坐标
 private float startx, starty;
 //两个手指中间点的位置
 private float midx, midy;
 //当前imageview的matirx对象,以前imageview的matrix对象
 private matrix currmatrix, savedmatrix;
 //之前图片的旋转角度
 private float prerotate;
 //之间两个手指之间的距离
 private float prespacing;

 public matriximageview2(context context, attributeset attrs) {
  super(context, attrs);
  initview();
 }

 private void initview() {
  //初始化模式为初始状态
  mode = mode_none;
  currmatrix = new matrix();
  savedmatrix = new matrix();
  displaymetrics dm = getresources().getdisplaymetrics();
  //给imageview设置一张图片(此处为了测试直接在imageview里面设置了一张测试图片)
  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) {
  //多点触碰如果需要监听action_pointer_down等操作的时候,必须用event.getaction() & motionevent.action_mask
  //而不是直接的event.getaction();
  switch (event.getaction() & motionevent.action_mask) {
   //当一个手指按下的时候
   case motionevent.action_down:
    //保存当前imageview的matrix对象
    savedmatrix.set(currmatrix);
    //记录手指开始的坐标
    startx = event.getx();
    starty = event.gety();
    //此时的状态为拖拽状态
    mode = mode_drag;
    break;
   //当两个手指按下的时候(我们先不考虑很多个的情况哈,能力有限~!)
   case motionevent.action_pointer_down:
    //计算两个手指之间的距离并保存起来
    prespacing = calspacing(event);
    //如果两个手指之间的距离大于我们指定的一个值后(改变状态为缩放)
    if (prespacing > 10f) {
     savedmatrix.set(currmatrix);
     mode = mode_zoom;
     //记录下缩放的中间坐标值
     midx = (event.getx(0) + event.getx(1)) / 2;
     midy = (event.gety(0) + event.gety(1)) / 2;
    }
    //根据两个手指的位置计算出当前角度并保存
    prerotate = calrotate(event);
    break;
   //当手指移动的时候
   case motionevent.action_move:
    //如果之前给的状态为拖拽状态的时候
    if (mode == mode_drag) {
     //首先把之前的matrix的状态赋给当前的matrix对象
     currmatrix.set(savedmatrix);
     //算出手指移动的距离
     float dx = event.getx() - startx;
     float dy = event.gety() - starty;
     //把手指移动的距离设置给matrix对象
     currmatrix.posttranslate(dx, dy);
     //当状态为放大状态的时候,并且有两个手指按下的时候
    } else if (mode == mode_zoom && event.getpointercount() == 2) {
     //首先把之前的matrix的状态赋给当前的matrix对象
     currmatrix.set(savedmatrix);
     //计算出此时两个手指之间的距离
     float spacing = calspacing(event);
     //如果此时两手指之间的距离大于我们给定的值
     if (spacing > 10f) {
      //此时两手指距离/第二个手指刚按下时两手指的距离
      float scale = spacing / prespacing;
      //把算出的缩放值给当前matrix对象,(缩放中心点为之前算出的mid)
      currmatrix.postscale(scale, scale, midx, midy);
     }
     //根据两手指位置算出此时的旋转角度
     float rotate = calrotate(event);
     if (rotate != prerotate) {
      //算出此时需要旋转的角度
      rotate = rotate - prerotate;
      //开始旋转图片
      currmatrix.postrotate(rotate, getmeasuredwidth() / 2, getmeasuredheight() / 2);
     }
    }
    break;
  }
  //最后记得把当前的matrix对象给imageview
  setimagematrix(currmatrix);
  return true;
 }

 /**
  * 根据两手指的位置算出角度
  * 勾股定理 tan0=x(两手指横坐标距离)/y(两手指纵坐标距离);
  * @param event
  * @return
  */
 private float calrotate(motionevent event) {
  double x = event.getx(0) - event.getx(1);
  double y = event.gety(0) - event.gety(1);
  double radius = math.atan2(y, x);
  return (float) math.todegrees(radius);
 }

 /**
  * 两个点距离公式为d*d=(x1-x0)的平方+(y1-y0)的平方
  * @param event
  * @return
  */
 private float calspacing(motionevent event) {
  float x = event.getx(0) - event.getx(1);
  float y = event.gety(0) - event.gety(1);
  return (float) math.sqrt(math.pow(x, 2) + math.pow(y, 2));
 }
}

然后添加我们的布局文件:

 <com.leo.gestureimageview.matriximageview
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:scaletype="matrix"
  android:src="@mipmap/test"
  />

最后运行代码:

这里写图片描述

好了,虽说我们是实现了我们的手势imageview的基本功能,但是如果要处理那种多点(>两个手指)触碰,还有一些复杂的操作的时候,我们的ontouchevent里面写的代码可能就不止这么一点了(还是有点复杂的,考虑的因素太多),但如果可以把某个事件的处理单独拿出去分成很多个分支的话,还会这么复杂么?? 如果说我们的代码可以像下面这样的话,你是不是觉得很爽呢?

package com.leo.gestureimageview;

import android.content.context;
import android.graphics.bitmap;
import android.graphics.bitmapfactory;
import android.graphics.matrix;
import android.graphics.pointf;
import android.util.attributeset;
import android.util.displaymetrics;
import android.view.motionevent;
import android.view.scalegesturedetector;
import android.widget.imageview;

import com.leo.gestureimageview.gesturedetectors.movegesturedetector;
import com.leo.gestureimageview.gesturedetectors.rotategesturedetector;

public class matriximageview2 extends imageview {
 private matrix mmatrix = new matrix();
 private float mscalefactor =1f;
 private float mrotationdegrees = 0.f;
 private float mfocusx = 0.f;
 private float mfocusy = 0.f;


 private scalegesturedetector mscaledetector;
 private rotategesturedetector mrotatedetector;
 private movegesturedetector mmovedetector;
 public matriximageview2(context context, attributeset attrs) {
 super(context, attrs);
 initview();
 }

 private void initview() {
 //初始化模式为初始状态
 displaymetrics dm = getresources().getdisplaymetrics();
 //给imageview设置一张图片(此处为了测试直接在imageview里面设置了一张测试图片)
 bitmap bitmap = bitmapfactory.decoderesource(getresources(), r.mipmap.test);
 bitmap = bitmap.createscaledbitmap(bitmap, dm.widthpixels, dm.heightpixels, true);
 setimagebitmap(bitmap);
 mscaledetector = new scalegesturedetector(getcontext(), new scalelistener());
 mrotatedetector = new rotategesturedetector(getcontext(), new rotatelistener());
 mmovedetector = new movegesturedetector(getcontext(), new movelistener());
 mfocusx = dm.widthpixels/2f;
 mfocusy = dm.heightpixels/2f;

 }

 @override
 public boolean ontouchevent(motionevent event) {
 mscaledetector.ontouchevent(event);
 mrotatedetector.ontouchevent(event);
 mmovedetector.ontouchevent(event);
 return true;
 }
 private class scalelistener extends scalegesturedetector.simpleonscalegesturelistener {
 @override
 public boolean onscale(scalegesturedetector detector) {
  mscalefactor *= detector.getscalefactor(); // scale change since previous event
  // don't let the object get too small or too large.
  mscalefactor = math.max(0.1f, math.min(mscalefactor, 10.0f));
  changematrix();
  return true;
 }
 }
 private class rotatelistener extends rotategesturedetector.simpleonrotategesturelistener {
 @override
 public boolean onrotate(rotategesturedetector detector) {
  mrotationdegrees -= detector.getrotationdegreesdelta();
  changematrix();
  return true;
 }
 }
 private class movelistener extends movegesturedetector.simpleonmovegesturelistener {
 @override
 public boolean onmove(movegesturedetector detector) {
  pointf d = detector.getfocusdelta();
  mfocusx += d.x;
  mfocusy += d.y;
  changematrix();
  return true;
 }
 }
 private void changematrix(){
 float scaledimagecenterx = (getdrawable().getintrinsicwidth()*mscalefactor)/2;
 float scaledimagecentery = (getdrawable().getintrinsicheight()*mscalefactor)/2;
 mmatrix.reset();
 mmatrix.postscale(mscalefactor, mscalefactor);
 mmatrix.postrotate(mrotationdegrees, scaledimagecenterx, scaledimagecentery);
 mmatrix.posttranslate(mfocusx - scaledimagecenterx, mfocusy - scaledimagecentery);
 setimagematrix(mmatrix);
 }
}

我们的imageview的ontouchevent就只剩下短短的几行代码了,然后各个detector处理完事件后,我们只需要拿到处理好的值就可以了:

 @override
 public boolean ontouchevent(motionevent event) {
 //把缩放事件给mscaledetector
 mscaledetector.ontouchevent(event);
 //把旋转事件个mrotatedetector
 mrotatedetector.ontouchevent(event);
 //把移动事件给mmovedetector
 mmovedetector.ontouchevent(event);
 return true;
 }

是不是觉得很爽呢? 是的,我也是无意中看到了这个开源项目,先附上这个框架的github链接:
https://github.com/almeros/android-gesture-detectors

下一节我们将深入了解detector,以及系统自带的手势处理工具类gesturedetector,感兴趣的小伙伴请继续关注哦。

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网