图片操作系列 —（2）手势旋转图片

前言

1. 根据二个手指头的旋转来使图片跟着旋转
2. 当二个手指头放开后，图片会自动回归到合适的位置。

ps:我这边可以再贴出相关基础的链接： android matrix 最全方法详解与进阶（完整篇） Android Matrix

根据二个手指头的旋转来使图片跟着旋转：

1.获取二个手指头的手势监听

```public interface IRotateDetector {

/**
* handle rotation in onTouchEvent
*
* @param event The motion event.
* @return True if the event was handled, false otherwise.
*/
boolean onTouchEvent(MotionEvent event);

/**
* is the Gesture Rotate
*
* @return true:rotating;false,otherwise
*/
boolean isRotating();
}复制代码```
```public class RotateGestureDetector implements IRotateDetector{

private int mLastAngle = 0;//最后一次的角度值
private IRotateListener mListener;//用来旋转的回调Listener
private boolean mIsRotate;//是否处于旋转

//用来设置回调Listener的方法
public void setRotateListener(IRotateListener listener) {
this.mListener = listener;
}

//用来接收触摸事件
@Override
public boolean onTouchEvent(MotionEvent event) {
return doRotate(event);
}

//真正的计算手势操作所得到的角度值的方法，及回调调用。
private boolean doRotate(MotionEvent ev) {
if (ev.getPointerCount() != 2) {
return false;
}
//Calculate the angle between the two fingers
int pivotX = (int) (ev.getX(0) + ev.getX(1)) / 2;
int pivotY = (int) (ev.getY(0) + ev.getY(1)) / 2;
float deltaX = ev.getX(0) - ev.getX(1);
float deltaY = ev.getY(0) - ev.getY(1);

double radians = Math.atan(deltaY / deltaX);

int degrees = (int) Math.round(Math.toDegrees(Math.atan2(deltaY,deltaX)));

case MotionEvent.ACTION_DOWN:
mLastAngle = degrees;
mIsRotate = false;
break;
case MotionEvent.ACTION_UP:
mIsRotate = false;
break;
case MotionEvent.ACTION_POINTER_DOWN:
mLastAngle = degrees;
mIsRotate = false;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_POINTER_UP:
mIsRotate = false;
upRotate(pivotX, pivotY);
mLastAngle = degrees;
break;
case MotionEvent.ACTION_MOVE:
mIsRotate = true;
int degreesValue = degrees  - mLastAngle;
if (degreesValue > 45) {
//Going CCW across the boundary
rotate(-5, pivotX, pivotY);
} else if (degreesValue < -45) {
//Going CW across the boundary
rotate(5, pivotX, pivotY);
} else {
//Normal rotation, rotate the difference
rotate(degreesValue, pivotX, pivotY);
}
//Save the current angle
mLastAngle = degrees;
break;
}
return true;
}

//回调的方法之一：控制图片根据手势的变化实时进行旋转
private void rotate(int degree, int pivotX, int pivotY) {
if (mListener != null) {
mListener.rotate(degree, pivotX, pivotY);
}
}

//回调的方法之一：最后某个手指放开后，控制图片自动回归到合适的位置。
private void upRotate(int pivotX, int pivotY) {
if (mListener != null) {
mListener.upRotate(pivotX, pivotY);
}
}

}复制代码```

2.获取二个手指头的角度变化

```//真正的计算手势操作所得到的角度值的方法，及回调调用。
private boolean doRotate(MotionEvent ev) {
//如果触摸的手指头不是2个，直接返回。
if (ev.getPointerCount() != 2) {
return false;
}

//获取二个手指头的中心点的X与Y值，等会选择二个手指头的中心点作为旋转的中心
int pivotX = (int) (ev.getX(0) + ev.getX(1)) / 2;
int pivotY = (int) (ev.getY(0) + ev.getY(1)) / 2;
//获取二个手指头之间的X和Y的差值
float deltaX = ev.getX(0) - ev.getX(1);
float deltaY = ev.getY(0) - ev.getY(1);
//获取角度
int degrees = (int) Math.round(Math.toDegrees(Math.atan2(deltaY,deltaX)));

case MotionEvent.ACTION_DOWN:
mLastAngle = degrees;
mIsRotate = false;
break;
case MotionEvent.ACTION_UP:
mIsRotate = false;
break;
case MotionEvent.ACTION_POINTER_DOWN:
mLastAngle = degrees;
mIsRotate = false;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_POINTER_UP:
mIsRotate = false;
upRotate(pivotX, pivotY);
mLastAngle = degrees;
break;
case MotionEvent.ACTION_MOVE:
mIsRotate = true;
/*
每次把上一次的角度赋值给mLastAngle，然后获取当前新获取的角度degrees，
二者相减获取到二个手指头在移动的时候相应的角度变化。
*/
int degreesValue = degrees  - mLastAngle;

/*
这里主要出现这么个情况，二个手指头如果相隔有一段距离，那么在移动的过程中，角度不会一下子变化很大.
但是比如我们这里故意二个手指头是碰在一起的,然后二个手指头稍微动一下，你就会发现角度变化会很大。
这样图片就会瞬间也旋转了很大的角度，让人体验感觉很怪，所以我们这里瞬间顺时针或者逆时针超过45度，都只移动5度值。
*/
if (degreesValue > 45) {
rotate(-5, pivotX, pivotY);
} else if (degreesValue < -45) {
rotate(5, pivotX, pivotY);
} else {
rotate(degreesValue, pivotX, pivotY);
}
//Save the current angle
mLastAngle = degrees;
break;
}
return true;
}复制代码```

`doRotate方法`中最主要的就是根据二个手指头触摸获取到的X,Y的差值，根据Math.atan2来获取到角度。我们具体来看下为什么这样可以来获取角度：

3.在Activity中设置Listener来进行图片的旋转

```rotateGestureDetector.setRotateListener(new IRotateListener() {
@Override
public void rotate(int degree, int pivotX, int pivotY) {
//图片跟着手势进行旋转
mSuppMatrix.postRotate(degree, pivotX, pivotY);
//Post the rotation to the image
checkAndDisplayMatrix();
}

@Override
public void upRotate(int pivotX, int pivotY) {

//当手指头松开的时候，让图片自动更新到合适的位置。
float[] v = new float[9];
mSuppMatrix.getValues(v);
// calculate the degree of rotation
int angle = (int) Math.round(Math.toDegrees(Math.atan2(v[Matrix.MSKEW_Y], v[Matrix.MSCALE_X])));

mRightAngleRunnable = new RightAngleRunnable(angle, pivotX, pivotY);
photoView.post(mRightAngleRunnable);
}
});复制代码```

手指头松开手图片自动旋转到合适位置：

```@Override
public void upRotate(int pivotX, int pivotY) {

//当手指头松开的时候，让图片自动更新到合适的位置。
float[] v = new float[9];
mSuppMatrix.getValues(v);
// calculate the degree of rotation
int angle = (int) Math.round(Math.toDegrees(Math.atan2(v[Matrix.MSKEW_Y], v[Matrix.MSCALE_X])));

mRightAngleRunnable = new RightAngleRunnable(angle, pivotX, pivotY);
photoView.post(mRightAngleRunnable);
}复制代码```

Matrix，中文里叫矩阵，高等数学里有介绍，在图像处理方面，主要是用于平面的缩放、平移、旋转等操作。在Android里面，Matrix由9个float值构成，是一个3*3的矩阵。最好记住。如下图：

```public class Matrix {

public static final int MSCALE_X = 0;   //!< use with getValues/setValues
public static final int MSKEW_X  = 1;   //!< use with getValues/setValues
public static final int MTRANS_X = 2;   //!< use with getValues/setValues
public static final int MSKEW_Y  = 3;   //!< use with getValues/setValues
public static final int MSCALE_Y = 4;   //!< use with getValues/setValues
public static final int MTRANS_Y = 5;   //!< use with getValues/setValues
public static final int MPERSP_0 = 6;   //!< use with getValues/setValues
public static final int MPERSP_1 = 7;   //!< use with getValues/setValues
public static final int MPERSP_2 = 8;   //!< use with getValues/setValues

......
......
......
}复制代码```

```mRightAngleRunnable = new RightAngleRunnable(angle, pivotX, pivotY);
photoView.post(mRightAngleRunnable);复制代码```

```class RightAngleRunnable implements Runnable {
private static final int RECOVER_SPEED = 4;
private int mOldDegree;
private int mNeedToRotate;
private int mRoPivotX;
private int mRoPivotY;

RightAngleRunnable(int degree, int pivotX, int pivotY) {
Log.v("dyp4", "oldDegree:" + degree + "," + "calDegree:" + calDegree(degree));
this.mOldDegree = degree;
this.mNeedToRotate = calDegree(degree);
this.mRoPivotX = pivotX;
this.mRoPivotY = pivotY;
}

//最终计算需要矫正的角度值
/*
例如：

比如最终是60度，这时候其实是超过了45度，应该矫正成90度，
所以最终要多给它30度。顺时针多选择30度。这里计算会得到30。

比如如果是-60度，这时候应该是变成-90读，所以我们逆时针多旋转30度。
这时候计算会得到-30。

如果是20度，这时候没有超过45度，所以应该矫正成0度，
所以最终要逆时针转回20度，所以这里计算会得到-20。

如果是-120度，这时候要变成-90度，所以要顺时针转回30度，
所以计算会得到30。
*/
private int calDegree(int oldDegree) {
int N = Math.abs(oldDegree) / 45;
if ((0 <= N && N < 1) || 2 <= N && N < 3) {
return -oldDegree % 45;
} else {
if (oldDegree < 0) {
return -(45 + oldDegree % 45);
} else {
return (45 - oldDegree % 45);
}
}
}

/*
我们上面的calDegree方法可以获得我们需要矫正的角度，但是我们不是一下子就让图片选择N度，而是慢慢的转过来。
比如我们用RECOVER_SPEED = 4，4度的慢慢来旋转过来，不会给用户很突兀的感觉。
*/
@Override
public void run() {
if (mNeedToRotate == 0) {
return;
}
if (photoView == null) {
return;
}
if (mNeedToRotate > 0) {
//Clockwise rotation
if (mNeedToRotate >= RECOVER_SPEED) {
mSuppMatrix.postRotate(RECOVER_SPEED, mRoPivotX, mRoPivotY);
mNeedToRotate -= RECOVER_SPEED;
} else {
mSuppMatrix.postRotate(mNeedToRotate, mRoPivotX, mRoPivotY);
mNeedToRotate = 0;
}
} else if (mNeedToRotate < 0) {
//Counterclockwise rotation
if (mNeedToRotate <= -RECOVER_SPEED) {
mSuppMatrix.postRotate(-RECOVER_SPEED, mRoPivotX, mRoPivotY);
mNeedToRotate += RECOVER_SPEED;
} else {
mSuppMatrix.postRotate(mNeedToRotate, mRoPivotX, mRoPivotY);
mNeedToRotate = 0;
}
}

checkAndDisplayMatrix();
Compat.postOnAnimation(photoView, this);
}
}复制代码```

0 条评论

相关文章

30040

15410

19790

18230

什么样的人生才是有意义的人生——没有标准的标准答案

【导读】其实我们可以跳出这个小圈圈去更加科客观地看一下这个世界。在夜晚的时候我们仰望天空，浩瀚的宇宙中整个地球只是一粒浮尘，何况地球上一个小小的人类？在漫长的历...

1.8K50

32440

13840

19240

22370

SQL中GROUP BY用法示例

GROUP BY我们可以先从字面上来理解，GROUP表示分组，BY后面写字段名，就表示根据哪个字段进行分组，如果有用Excel比较多的话，GROUP BY比较类...

5.2K20