我是靠谱客的博主 忧心睫毛,最近开发中收集的这篇文章主要介绍android+图片手势,Android手势ImageView三部曲 第一部,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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

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

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

当一个手指按住图片的时候,此时的效果为拖拽的效果。

当两个手指按住图片的时候,手指旋转则图片跟着旋转,手指缩放则图片缩放。

效果图大致为(我模拟器不太好模拟旋转):

1d68528478d2324170ca97e6a46de051.gif

好了下面我们来实现一下这个手势缩放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));

}

}

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

android:layout_width="match_parent"

android:layout_height="match_parent"

android:scaleType="matrix"

android:src="@mipmap/test"

/>

最后运行代码:

1d68528478d2324170ca97e6a46de051.gif

好了,虽说我们是实现了我们的手势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;

}

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

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

最后

以上就是忧心睫毛为你收集整理的android+图片手势,Android手势ImageView三部曲 第一部的全部内容,希望文章能够帮你解决android+图片手势,Android手势ImageView三部曲 第一部所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(81)

评论列表共有 0 条评论

立即
投稿
返回
顶部