Android两指旋转

世事洞明皆学问,人情练达即文章。这篇文章主要讲述Android两指旋转相关的知识,希望能为你提供帮助。
【Android两指旋转】我试图在android中实现两个手指旋转,但它并没有按预期工作。目标是实现像Google Earth一样的旋转(双指旋转焦点周围的图像)。目前我的旋转侦听器如下所示:

private class RotationGestureListener { private static final int INVALID_POINTER_ID = -1; private float fX, fY, sX, sY, focalX, focalY; private int ptrID1, ptrID2; public RotationGestureListener(){ ptrID1 = INVALID_POINTER_ID; ptrID2 = INVALID_POINTER_ID; }public boolean onTouchEvent(MotionEvent event){ switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: sX = event.getX(); sY = event.getY(); ptrID1 = event.getPointerId(0); break; case MotionEvent.ACTION_POINTER_DOWN: fX = event.getX(); fY = event.getY(); focalX = getMidpoint(fX, sX); focalY = getMidpoint(fY, sY); ptrID2 = event.getPointerId(event.getActionIndex()); break; case MotionEvent.ACTION_MOVE:if(ptrID1 != INVALID_POINTER_ID & & ptrID2 != INVALID_POINTER_ID){ float nfX, nfY, nsX, nsY; nfX = event.getX(event.findPointerIndex(ptrID1)); nfY = event.getY(event.findPointerIndex(ptrID1)); nsX = event.getX(event.findPointerIndex(ptrID2)); nsY = event.getY(event.findPointerIndex(ptrID2)); float angle = angleBtwLines(fX, fY, nfX, nfY, sX, sY, nsX, nsY); rotateImage(angle, focalX, focalY); fX = nfX; fY = nfY; sX = nfX; sY = nfY; } break; case MotionEvent.ACTION_UP: ptrID1 = INVALID_POINTER_ID; break; case MotionEvent.ACTION_POINTER_UP: ptrID2 = INVALID_POINTER_ID; break; } return false; }private float getMidpoint(float a, float b){ return (a + b) / 2; } private float angleBtwLines (float fx1, float fy1, float fx2, float fy2, float sx1, float sy1, float sx2, float sy2){ float angle1 = (float) Math.atan2(fy1 - fy2, fx1 - fx2); float angle2 = (float) Math.atan2(sy1 - sy2, sx1 - sx2); return (float) Math.toDegrees((angle1-angle2)); } }

然而,每当我旋转时,旋转角度要大得多,有时它会旋转到错误的一侧。有想法该怎么解决这个吗?
顺便说一句,我在摩托罗拉Atrix上测试它,所以它没有触摸屏错误。
谢谢
答案 课程改进:
  • 自旋转开始以来返回的角度是总计
  • 删除不必要的功能
  • 简单化
  • 仅在第二个指针停止后才获取第一个指针的位置
public class RotationGestureDetector { private static final int INVALID_POINTER_ID = -1; private float fX, fY, sX, sY; private int ptrID1, ptrID2; private float mAngle; private OnRotationGestureListener mListener; public float getAngle() { return mAngle; }public RotationGestureDetector(OnRotationGestureListener listener){ mListener = listener; ptrID1 = INVALID_POINTER_ID; ptrID2 = INVALID_POINTER_ID; }public boolean onTouchEvent(MotionEvent event){ switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: ptrID1 = event.getPointerId(event.getActionIndex()); break; case MotionEvent.ACTION_POINTER_DOWN: ptrID2 = event.getPointerId(event.getActionIndex()); sX = event.getX(event.findPointerIndex(ptrID1)); sY = event.getY(event.findPointerIndex(ptrID1)); fX = event.getX(event.findPointerIndex(ptrID2)); fY = event.getY(event.findPointerIndex(ptrID2)); break; case MotionEvent.ACTION_MOVE: if(ptrID1 != INVALID_POINTER_ID & & ptrID2 != INVALID_POINTER_ID){ float nfX, nfY, nsX, nsY; nsX = event.getX(event.findPointerIndex(ptrID1)); nsY = event.getY(event.findPointerIndex(ptrID1)); nfX = event.getX(event.findPointerIndex(ptrID2)); nfY = event.getY(event.findPointerIndex(ptrID2)); mAngle = angleBetweenLines(fX, fY, sX, sY, nfX, nfY, nsX, nsY); if (mListener != null) { mListener.OnRotation(this); } } break; case MotionEvent.ACTION_UP: ptrID1 = INVALID_POINTER_ID; break; case MotionEvent.ACTION_POINTER_UP: ptrID2 = INVALID_POINTER_ID; break; case MotionEvent.ACTION_CANCEL: ptrID1 = INVALID_POINTER_ID; ptrID2 = INVALID_POINTER_ID; break; } return true; }private float angleBetweenLines (float fX, float fY, float sX, float sY, float nfX, float nfY, float nsX, float nsY) { float angle1 = (float) Math.atan2( (fY - sY), (fX - sX) ); float angle2 = (float) Math.atan2( (nfY - nsY), (nfX - nsX) ); float angle = ((float)Math.toDegrees(angle1 - angle2)) % 360; if (angle < -180.f) angle += 360.0f; if (angle > 180.f) angle -= 360.0f; return angle; }public static interface OnRotationGestureListener { public void OnRotation(RotationGestureDetector rotationDetector); } }

如何使用它:
  1. 将上面的类放在一个单独的文件RotationGestureDetector.java
  2. 在你的活动类中创建一个mRotationDetector类型的私有字段RotationGestureDetector,并在初始化期间创建一个新的检测器实例(例如onCreate方法),并给出一个实现onRotation方法的类(这里是activity = this)。
  3. 在方法onTouchEvent中,使用'mRotationDetector.onTouchEvent(event); '将接收到的触摸事件发送到手势检测器
  4. 在您的活动中实施RotationGestureDetector.OnRotationGestureListener并在活动中添加方法'public void OnRotation(RotationGestureDetector rotationDetector)'。在这个方法中,获得与rotationDetector.getAngle()的角度
例:
public class MyActivity extends Activity implements RotationGestureDetector.OnRotationGestureListener { private RotationGestureDetector mRotationDetector; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mRotationDetector = new RotationGestureDetector(this); }@Override public boolean onTouchEvent(MotionEvent event){ mRotationDetector.onTouchEvent(event); return super.onTouchEvent(event); }@Override public void OnRotation(RotationGestureDetector rotationDetector) { float angle = rotationDetector.getAngle(); Log.d("RotationGestureDetector", "Rotation: " + Float.toString(angle)); }}

注意:你也可以在RotationGestureDetector而不是View中使用Activity类。
另一答案这是我对Leszek答案的改进。我发现他不适用于小视图,因为当触摸超出视角时,角度计算是错误的。解决方案是获取原始位置而不仅仅是getX / Y.
感谢this thread获得可旋转视图的原始点。
public class RotationGestureDetector {private static final int INVALID_POINTER_ID = -1; private PointF mFPoint = new PointF(); private PointF mSPoint = new PointF(); private int mPtrID1, mPtrID2; private float mAngle; private View mView; private OnRotationGestureListener mListener; public float getAngle() { return mAngle; }public RotationGestureDetector(OnRotationGestureListener listener, View v) { mListener = listener; mView = v; mPtrID1 = INVALID_POINTER_ID; mPtrID2 = INVALID_POINTER_ID; }public boolean onTouchEvent(MotionEvent event){switch (event.getActionMasked()) { case MotionEvent.ACTION_OUTSIDE: Log.d(this, "ACTION_OUTSIDE"); break; case MotionEvent.ACTION_DOWN: Log.v(this, "ACTION_DOWN"); mPtrID1 = event.getPointerId(event.getActionIndex()); break; case MotionEvent.ACTION_POINTER_DOWN: Log.v(this, "ACTION_POINTER_DOWN"); mPtrID2 = event.getPointerId(event.getActionIndex()); getRawPoint(event, mPtrID1, mSPoint); getRawPoint(event, mPtrID2, mFPoint); break; case MotionEvent.ACTION_MOVE: if (mPtrID1 != INVALID_POINTER_ID & & mPtrID2 != INVALID_POINTER_ID){ PointF nfPoint = new PointF(); PointF nsPoint = new PointF(); getRawPoint(event, mPtrID1, nsPoint); getRawPoint(event, mPtrID2, nfPoint); mAngle = angleBetweenLines(mFPoint, mSPoint, nfPoint, nsPoint); if (mListener != null) { mListener.onRotation(this); } } break; case MotionEvent.ACTION_UP: mPtrID1 = INVALID_POINTER_ID; break; case MotionEvent.ACTION_POINTER_UP: mPtrID2 = INVALID_POINTER_ID; break; case MotionEvent.ACTION_CANCEL: mPtrID1 = INVALID_POINTER_ID; mPtrID2 = INVALID_POINTER_ID; break; default: break; } return true; }void getRawPoint(MotionEvent ev, int index, PointF point){ final int[] location = { 0, 0 }; mView.getLocationOnScreen(location); float x = ev.getX(index); float y = ev.getY(index); double angle = Math.toDegrees(Math.atan2(y, x)); angle += mView.getRotation(); final float length = PointF.length(x, y); x = (float) (length * Math.cos(Math.toRadians(angle))) + location[0]; y = (float) (length * Math.sin(Math.toRadians(angle))) + location[1]; point.set(x, y); }private float angleBetweenLines(PointF fPoint, PointF sPoint, PointF nFpoint, PointF nSpoint) { float angle1 = (float) Math.atan2((fPoint.y - sPoint.y), (fPoint.x - sPoint.x)); float angle2 = (float) Math.atan2((nFpoint.y - nSpoint.y), (nFpoint.x - nSpoint.x)); float angle = ((float) Math.toDegrees(angle1 - angle2)) % 360; if (angle < -180.f) angle += 360.0f; if (angle > 180.f) angle -= 360.0f; return -angle; }public interface OnRotationGestureListener { void onRotation(RotationGestureDetector rotationDetector); } }

    推荐阅读