世事洞明皆学问,人情练达即文章。这篇文章主要讲述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);
}
}
如何使用它:
- 将上面的类放在一个单独的文件
RotationGestureDetector.java
中 - 在你的活动类中创建一个
mRotationDetector
类型的私有字段RotationGestureDetector
,并在初始化期间创建一个新的检测器实例(例如onCreate
方法),并给出一个实现onRotation
方法的类(这里是activity = this
)。 - 在方法
onTouchEvent
中,使用'mRotationDetector.onTouchEvent(event);
'将接收到的触摸事件发送到手势检测器 - 在您的活动中实施
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); } }
推荐阅读
- 试图在android中旋转布局,画布似乎不会旋转
- 如何在Android中旋转位图,使图像中心平滑,无振荡运动
- Android Camera2预览偶尔会旋转90度
- Android以XML格式访问SDK属性
- 如何解决Windows中的cmd或node.js命令提示符问题(git未安装或未在PATH中)
- 使用Javascript,HTML和CSS创建C#Windows .NET应用程序
- symfony项目中的供应商文件夹不会与(netbeans或svn)subversion一起上传
- Zoho邮件在Hotmail和Outlook用户的垃圾邮件文件夹中结束(解决方案)
- 使用Magento 2开发人员工具栏以正确的方式调试和配置Magento 2