近日在github上面看到一篇讲Android检测旋转手势的,以为挺有意思的,如今分享给你们。java
主要涉及到一些初中的几何知识和反正切函数的使用,分析以下。android
旋转是一种两个手指的多点触屏动做,屏幕上的旋转手势通常能够近似看作以两个手指连线上的某一点为中心画圆。如上图,为了简单,假设为连线的中心点。A0B0是一开始两个手指之间的连线,通过一段时间后,旋转到了A1B1,而后到了A2B2。git
能够看到,第一次旋转的角度为a,第二次为b,如今的问题是如何求b,明显b=e,e=f-d=c-d,如今的问题的关键变成了如何求c,d,而c,d偏偏是旋转开始先后,两个手指x,y坐标差的反正切值arctan(△y/△x),又因为这是逆时针旋转,最后累计旋转的角度是-a-b。github
最后附上实现的代码和效果图:canvas
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; /** * Created by cquxcm on 2016/6/20. */ public class RotateView extends View { private static final double MAX_ANGLE = 1e-1; private Paint mPaint; private float mRotation; private Float mPreviousAngle; public RotateView(Context context) { super(context); Log.d("xcm", "constructor 1"); } public RotateView(Context context, AttributeSet attrs) { super(context, attrs); Log.d("xcm", "constructor 2"); } public RotateView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); Log.d("xcm", "constructor 3"); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); Log.d("xcm", "onAttachedToWindow"); mPaint = new Paint(); mPaint.setColor(Color.BLACK); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(10); mPaint.setAntiAlias(true); mPreviousAngle = null; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int width = getWidth(); int height = getHeight(); int radius = (int) (width > height ? height * 0.4444f : width * 0.444f); canvas.drawCircle(width / 2, height / 2, radius, mPaint); canvas.save(); canvas.rotate(mRotation, width / 2, height / 2); canvas.drawLine(width / 2, height * 0.1f, width / 2, height * 0.9f, mPaint); canvas.restore(); } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getPointerCount() == 2) { float currentAngle = (float) angle(event); if (mPreviousAngle != null) { mRotation -= Math.toDegrees(clamp(mPreviousAngle - currentAngle, -MAX_ANGLE, MAX_ANGLE)); invalidate(); } mPreviousAngle = currentAngle; } else { mPreviousAngle = null; } return true; } private static double angle(MotionEvent event) { // Log.d("xcm", "x0 = " + event.getX(0) + "," + "x1 = " + event.getX(1)); // Log.d("xcm", "y0 = " + event.getY(0) + "," + "y1 = " + event.getY(1)); double deltaX = (event.getX(0) - event.getX(1));//获取两个手指触摸点的X坐标值的差值 double deltaY = (event.getY(0) - event.getY(1));//获取两个手指触摸点的Y坐标值的差值 return Math.atan2(deltaY, deltaX); } private static double clamp(double value, double min, double max) { if (value < min) { return min; } if (value > max) { return max; } return value; } }