Intereting Posts
Изменение системной яркости программно Android Studio Keyboard Shortcut для полноэкранного режима Java.lang.SecurityException пытается прочитать из URI Android Contacts Установка предварительного просмотра камеры на SurfaceView больше, чем на дисплее Создайте проект Eclipse с Android Git Звук не отображается при потоковой передаче между классом java и андроидной активностью Библиотека Json Parsing Gson от Google: В чем разница между JsonElement и JsonObject? Как играть в mp3-файл в необработанной папке в качестве уведомления об уведомлении в андроиде Приложение загружается с белым экраном в течение 3 секунд, прежде чем показывать правильный пользовательский интерфейс – используя фрагменты (без веб-просмотра) Получить значение даты и времени из одного фрагмента диалога и установить его в EditText Как установить Tint в NavigationView на некоторые значки Использует ли Flexbox в React Native использовать dp или pixel? Не удается установить пример проекта ApiDemos на Android-эмуляторе Невозможно связать собственную библиотеку в образце OpenCV Android Использование IntentService для локального прослушивания, но onDestroy вызывается сразу после onHandleIntent

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)); } } 

Однако всякий раз, когда я поворачиваю, угол поворота намного больше и иногда он вращается с неправильной стороны. Есть какие нибудь идеи как это починить?

Кстати, я тестирую его на Motorola Atrix, поэтому у него нет ошибки сенсорного экрана.

благодаря

Solutions Collecting From Web of "Android Два пальца вращения"

Усовершенствования класса:

  • Возврат угла полностью, так как началась ротация
  • Удаление ненужных функций
  • упрощение
  • Получить позицию первого указателя только после того, как второй указатель опущен
 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 метод onCreate ) и 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 в представлении вместо Activity .

Вот мое улучшение в ответе Лешека. Я обнаружил, что он не работал для небольших просмотров, так как когда прикосновение выходило за рамки угла, расчет угла был неправильным. Решение состоит в том, чтобы получить исходное местоположение вместо просто 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); } } 

Я попробовал комбинацию ответов, которые здесь, но он все еще не работает отлично, поэтому мне пришлось немного изменить его.

Этот код дает вам угол дельта на каждом повороте, он отлично работает для меня, я использую его для поворота объекта в 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); } } 

Есть еще некоторые ошибки, вот решение, которое отлично сработало для меня …

вместо

 float angle = angleBtwLines(fX, fY, nfX, nfY, sX, sY, nsX, nsY); 

Вам нужно написать

 float angle = angleBtwLines(fX, fY, sX, sY, nfX, nfY, nsX, nsY); 

И 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)); } 

Тогда угол, который вы получаете, – это угол, по которому вы должны вращать изображение …

 ImageAngle += angle... 

У вас проблема:

 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)); } 

Вы должны закрепить углы в диапазоне [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; } 

В C ++ я бы закодировал ClipAngleTo0_360 как

 float ClipAngleTo0_360( float Angle ) { return std::fmod( Angle, 360.0f ); } 

Где 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; } 

Да, осторожная арифметика с плавающей точкой намного лучше, чем очевидная while () петля.

Как упоминалось MeTTeO (ссылка на java, 15.17.3), вы можете использовать оператор «%» вместо std :: fmod на C ++:

 float ClipAngleTo0_360( float Angle ) { return Angle % 360.0; }