近日在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; } }