pre*_*mba 28 android rotation gesture-recognition
我试图在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));
}
}
Run Code Online (Sandbox Code Playgroud)
然而,每当我旋转时,旋转角度要大得多,有时它会旋转到错误的一侧.有想法该怎么解决这个吗?
顺便说一句,我在摩托罗拉Atrix上测试它,所以它没有触摸屏错误.
谢谢
les*_*usz 60
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);
}
}
Run Code Online (Sandbox Code Playgroud)
RotationGestureDetector.javamRotationDetector类型的私有字段,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));
}
}
Run Code Online (Sandbox Code Playgroud)
您也可以使用RotationGestureDetector该类View而不是Activity.
aar*_*ino 14
这是我对Leszek答案的改进.我发现他不适用于小视图,因为当触摸超出视角时,角度计算是错误的.解决方案是获取原始位置而不仅仅是getX/Y.
相信这个线程可以在可旋转视图上获取原始点.
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);
}
}
Run Code Online (Sandbox Code Playgroud)
我尝试了这里的答案组合,但它仍然没有完美的工作,所以我不得不稍微修改它.
这段代码为你提供了每次旋转的增量角度,它对我很有效,我用它来旋转OpenGL中的一个对象.
public class RotationGestureDetector {
private static final int INVALID_POINTER_ID = -1;
private float fX, fY, sX, sY, focalX, focalY;
private int ptrID1, ptrID2;
private float mAngle;
private boolean firstTouch;
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:
sX = event.getX();
sY = event.getY();
ptrID1 = event.getPointerId(0);
mAngle = 0;
firstTouch = true;
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());
mAngle = 0;
firstTouch = true;
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));
if (firstTouch) {
mAngle = 0;
firstTouch = false;
} else {
mAngle = angleBetweenLines(fX, fY, sX, sY, nfX, nfY, nsX, nsY);
}
if (mListener != null) {
mListener.OnRotation(this);
}
fX = nfX;
fY = nfY;
sX = nsX;
sY = nsY;
}
break;
case MotionEvent.ACTION_UP:
ptrID1 = INVALID_POINTER_ID;
break;
case MotionEvent.ACTION_POINTER_UP:
ptrID2 = INVALID_POINTER_ID;
break;
}
return true;
}
private float getMidpoint(float a, float b){
return (a + b) / 2;
}
float findAngleDelta( float angle1, float angle2 )
{
float From = ClipAngleTo0_360( angle2 );
float To = ClipAngleTo0_360( angle1 );
float Dist = To - From;
if ( Dist < -180.0f )
{
Dist += 360.0f;
}
else if ( Dist > 180.0f )
{
Dist -= 360.0f;
}
return Dist;
}
float ClipAngleTo0_360( float Angle ) {
return Angle % 360.0f;
}
private float angleBetweenLines (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 findAngleDelta((float)Math.toDegrees(angle1),(float)Math.toDegrees(angle2));
}
public static interface OnRotationGestureListener {
public boolean OnRotation(RotationGestureDetector rotationDetector);
}
}
Run Code Online (Sandbox Code Playgroud)
你在这里有问题:
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));
}
Run Code Online (Sandbox Code Playgroud)
您必须将角度剪裁到 [0..2*Pi] 范围内,然后仔细计算 (-Pi..+Pi) 范围内的角度差。
这是 0..360 角度范围的代码
float FindAngleDelta( float angle1, float angle2 )
{
float From = ClipAngleTo0_360( angle2 );
float To = ClipAngleTo0_360( angle1 );
float Dist = To - From;
if ( Dist < -180.0f )
{
Dist += 360.0f;
}
else if ( Dist > 180.0f )
{
Dist -= 360.0f;
}
return Dist;
}
Run Code Online (Sandbox Code Playgroud)
在 C++ 中,我将 ClipAngleTo0_360 编码为
float ClipAngleTo0_360( float Angle ) { return std::fmod( Angle, 360.0f ); }
Run Code Online (Sandbox Code Playgroud)
其中 std::fmod 返回浮点余数。
在java中你可以使用类似的东西
float ClipAngleTo0_360( float Angle )
{
float Res = Angle;
while(Angle < 0) { Angle += 360.0; }
while(Angle >= 360.0) { Angle -= 360.0; }
return Res;
}
Run Code Online (Sandbox Code Playgroud)
是的,仔细的浮点运算比明显的 while() 循环要好得多。
正如 MeTTeO 提到的(java 参考,15.17.3),您可以使用 '%' 运算符代替 C++ 的 std::fmod:
float ClipAngleTo0_360( float Angle ) { return Angle % 360.0; }
Run Code Online (Sandbox Code Playgroud)
还有一些错误,这里的解决方案对我来说非常合适......
代替
float angle = angleBtwLines(fX, fY, nfX, nfY, sX, sY, nsX, nsY);
Run Code Online (Sandbox Code Playgroud)
你需要写
float angle = angleBtwLines(fX, fY, sX, sY, nfX, nfY, nsX, nsY);
Run Code Online (Sandbox Code Playgroud)
和angleBetweenLines应该是
private float angleBetweenLines (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 findAngleDelta((float)Math.toDegrees(angle1),(float)Math.toDegrees(angle2));
}
Run Code Online (Sandbox Code Playgroud)
然后你得到的角度是你应该旋转图像的角度......
ImageAngle += angle...
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
28412 次 |
| 最近记录: |