Android手势ImageView三部曲 第一部

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

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

GestureImageView: https://github.com/jasonpolites/gesture-imageview

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,感兴趣的小伙伴请继续关注哦。

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

(0)

相关推荐

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

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

  • Android通过手势实现的缩放处理实例代码

    网络上传言HTC的HERO-ROM支持多点触摸的论证大多源于浏览网页和图片时,能像IPhone一样通过手势来控制页面的大小.下面的例子是利用现有的API实现HERO浏览图片和网页的缩放功能. 主要原理是onTouchEvent事件中的参数MotionEvent,它有一个getSize()方法.在一个点的时候,该方法永远返回0,而在两个触电的时候,该方法则根据两点相对位置变化而返回不同的值.我们只需计算出两点之间的距离变化,距离的大小表明我们希望目标变化的趋势.而getX()和getY()方法则永

  • Android ImageView随手势变化动态缩放图片

    在Android实际项目开发中,经常要用到ImageView这个组件,如果纯粹只是用来显示某一张图片,这个原生的组件是很方便的.但有时候为了效果更炫一点,比如很多软件都有的对图片的处理,随着手势的变化来拉大或缩小图片,这时就需要对原生的ImageView做一些处理来达到我们想要的效果. 原理就是Matrix 和PointF的运用,其实明白后一点都不难.我们需要写一个类继承ImageView来实现我们想要的功能,我这里演示的是类名是:ScaleImageView ,主要是重写onTouchEven

  • Android实现手势控制ImageView图片大小

    本文实例实现的主要功能是在ImageView中识别手势用以控制图片放大或缩小,具有一定的参考价值,分享给大家. public class MatrixImageView extends ImageView { private GestureDetector mGestureDetector; private Matrix mMatrix = new Matrix(); private float mImageWidth; private float mImageHeight; private f

  • Android手势ImageView三部曲 第二部

    废话不多说了,还记得上一节Android手势ImageView三部曲(一)最后我们提及的那个框架么?这一节我们重点了掌握一下GestureDetector这个类相关的属性方法. 一.那么GestureDetector是干嘛的呢? 顾名思义,字面意思就是"手势检测器"的意思,还记得我们上一节中实现的GestureImageView么?我们在onTouchEvent中检测到了各种个样的手势(手指按下.抬起.什么时候属于拖拽.什么时候属于缩放)都是通过我们的计算得到的,但是有了Gesture

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

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

  • android开发之为activity增加左右手势识别示例

    android开发中为activity增加左右手势识别,如右滑关闭当前页面. 复制代码 代码如下: /* *  for左右手势 *  1.复制下面的内容到目标Activity *  2.目标Activity的onCreate()调用initGesture() *  3.目标Activity需implements OnTouchListener, OnGestureListener */   private GestureDetector mGestureDetector;   private i

  • Android自定义GestureDetector实现手势ImageView

    不说废话了,进入我们今天的主题吧. 先贴上前面内容的地址: Android手势ImageView三部曲(一) Android手势ImageView三部曲(二) Android手势ImageView三部曲(三) 前面我们讲到了ScaleGestureDetector这个工具类,我在疑惑,为什么搞出一个ScaleGestureDetector,不顺带把什么旋转.移动.做了呢? 好吧-! 谷歌肯定还是想给开发者留一点自己的空间哈. 仿照ScaleGestureDetector,我们来定义一个叫Move

  • android使用gesturedetector手势识别示例分享

    复制代码 代码如下: public class MyGestureLintener extends SimpleOnGestureListener {private Context context;public MyGestureLintener(Context context) {    super();    this.context = context;} // 单击,触摸屏按下时立刻触发/*@Overridepublic boolean onDown(MotionEvent e) {  

  • Android手势ImageView三部曲 第三部

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

随机推荐