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

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

时间: 2024-09-08 01:03:24

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

Android手势ImageView三部曲 第三部

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

Android手势ImageView三部曲 第二部

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

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

Android手势密码的实现_Android

一.大致界面介绍: 图1 图2 图3 图4 图1:手势密码绘制界面 [主要是绘制上方的9个提示图标和9个宫格密码图标] 图2:设置手势密码 [监听手势的输入,TouchEvent的事件处理,获取输入的手势密码,同时显示在上方的提示区域] 图3:再绘制一次,两次密码不一致提示界面 [这里在实现的时候,错误提示文字加了"左右晃动的动画",错误路径颜色标记为红色] 图4:校验手势密码,输入的密码错误,给予红色路径+错误文字提示 二.实现思路: 1. 正上方的提示区域,用一个类(LockInd

Android手势滑动GestureDetector和OnGestureListener(二)

main.xml如下: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" >