Intereting Posts
Обе версии и бета-версии в Google Play Соглашение об именах с сериализацией / десериализацией Firebase? Как мы можем выполнить функцию javascript и получить возвращаемое значение в нашем приложении для Android? Андроид – тег манифеста <activity-alias> Android-приложение не запускается на эмуляторе Как автоматически настроить размер шрифта в панели инструментов панели действий на основе ориентации? LibGDX + gdx-freetype + BlueStacks Как я могу поймать SIGSEGV (ошибка сегментации) и получить трассировку стека под JNI на Android? Эффективность getDrawable (): является ли Drawable кэшированной каркасом? Crash in Marshmallow Fragment для карт Получите время выполнения видео, воспроизводимого при видеообъявлении? Можно ли смешивать «предпочтение» и заголовок предпочтения? Можно ли использовать SQLite Cursor после закрытия базы данных? AutoCompleteTextView с настраиваемым списком: как настроить OnItemClickListener Robolectric tests throw RuntimeException: java.lang.ClassNotFoundException

Соответствующий повернутый объект к числовым значениям

У меня есть кодовый замок, вращающийся в круге на 360 градусов.

Комбинированный замок имеет на нем числовые значения, они чисто графические.

Мне нужен способ перевести поворот изображения на значения 0-99 на графике.

В этом первом графике значение должно быть в состоянии сказать мне «0»,

http://i48.tinypic.com/27y67b7.png

На этом графике после того, как пользователь повернул изображение, значение должно быть в состоянии сказать мне «72»,

http://i46.tinypic.com/2ueiogh.png

Вот код:

package co.sts.combinationlock; import android.os.Bundle; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.util.Log; import android.view.GestureDetector; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.View.OnTouchListener; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.ImageView; import android.support.v4.app.NavUtils; public class ComboLock extends Activity{ private static Bitmap imageOriginal, imageScaled; private static Matrix matrix; private ImageView dialer; private int dialerHeight, dialerWidth; private GestureDetector detector; // needed for detecting the inversed rotations private boolean[] quadrantTouched; private boolean allowRotating; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_combo_lock); // load the image only once if (imageOriginal == null) { imageOriginal = BitmapFactory.decodeResource(getResources(), R.drawable.numbers); } // initialize the matrix only once if (matrix == null) { matrix = new Matrix(); } else { // not needed, you can also post the matrix immediately to restore the old state matrix.reset(); } detector = new GestureDetector(this, new MyGestureDetector()); // there is no 0th quadrant, to keep it simple the first value gets ignored quadrantTouched = new boolean[] { false, false, false, false, false }; allowRotating = true; dialer = (ImageView) findViewById(R.id.locknumbers); dialer.setOnTouchListener(new MyOnTouchListener()); dialer.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // method called more than once, but the values only need to be initialized one time if (dialerHeight == 0 || dialerWidth == 0) { dialerHeight = dialer.getHeight(); dialerWidth = dialer.getWidth(); // resize Matrix resize = new Matrix(); //resize.postScale((float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getWidth(), (float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getHeight()); imageScaled = Bitmap.createBitmap(imageOriginal, 0, 0, imageOriginal.getWidth(), imageOriginal.getHeight(), resize, false); // translate to the image view's center float translateX = dialerWidth / 2 - imageScaled.getWidth() / 2; float translateY = dialerHeight / 2 - imageScaled.getHeight() / 2; matrix.postTranslate(translateX, translateY); dialer.setImageBitmap(imageScaled); dialer.setImageMatrix(matrix); } } }); } /** * Rotate the dialer. * * @param degrees The degrees, the dialer should get rotated. */ private void rotateDialer(float degrees) { matrix.postRotate(degrees, dialerWidth / 2, dialerHeight / 2); //need to print degrees dialer.setImageMatrix(matrix); } /** * @return The angle of the unit circle with the image view's center */ private double getAngle(double xTouch, double yTouch) { double x = xTouch - (dialerWidth / 2d); double y = dialerHeight - yTouch - (dialerHeight / 2d); switch (getQuadrant(x, y)) { case 1: return Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI; case 2: case 3: return 180 - (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI); case 4: return 360 + Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI; default: // ignore, does not happen return 0; } } /** * @return The selected quadrant. */ private static int getQuadrant(double x, double y) { if (x >= 0) { return y >= 0 ? 1 : 4; } else { return y >= 0 ? 2 : 3; } } /** * Simple implementation of an {@link OnTouchListener} for registering the dialer's touch events. */ private class MyOnTouchListener implements OnTouchListener { private double startAngle; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // reset the touched quadrants for (int i = 0; i < quadrantTouched.length; i++) { quadrantTouched[i] = false; } allowRotating = false; startAngle = getAngle(event.getX(), event.getY()); break; case MotionEvent.ACTION_MOVE: double currentAngle = getAngle(event.getX(), event.getY()); rotateDialer((float) (startAngle - currentAngle)); startAngle = currentAngle; break; case MotionEvent.ACTION_UP: allowRotating = true; break; } // set the touched quadrant to true quadrantTouched[getQuadrant(event.getX() - (dialerWidth / 2), dialerHeight - event.getY() - (dialerHeight / 2))] = true; detector.onTouchEvent(event); return true; } } /** * Simple implementation of a {@link SimpleOnGestureListener} for detecting a fling event. */ private class MyGestureDetector extends SimpleOnGestureListener { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { // get the quadrant of the start and the end of the fling int q1 = getQuadrant(e1.getX() - (dialerWidth / 2), dialerHeight - e1.getY() - (dialerHeight / 2)); int q2 = getQuadrant(e2.getX() - (dialerWidth / 2), dialerHeight - e2.getY() - (dialerHeight / 2)); // the inversed rotations if ((q1 == 2 && q2 == 2 && Math.abs(velocityX) < Math.abs(velocityY)) || (q1 == 3 && q2 == 3) || (q1 == 1 && q2 == 3) || (q1 == 4 && q2 == 4 && Math.abs(velocityX) > Math.abs(velocityY)) || ((q1 == 2 && q2 == 3) || (q1 == 3 && q2 == 2)) || ((q1 == 3 && q2 == 4) || (q1 == 4 && q2 == 3)) || (q1 == 2 && q2 == 4 && quadrantTouched[3]) || (q1 == 4 && q2 == 2 && quadrantTouched[3])) { dialer.post(new FlingRunnable(-1 * (velocityX + velocityY))); } else { // the normal rotation dialer.post(new FlingRunnable(velocityX + velocityY)); } return true; } } /** * A {@link Runnable} for animating the the dialer's fling. */ private class FlingRunnable implements Runnable { private float velocity; public FlingRunnable(float velocity) { this.velocity = velocity; } @Override public void run() { if (Math.abs(velocity) > 5 && allowRotating) { //rotateDialer(velocity / 75); //velocity /= 1.0666F; // post this instance again dialer.post(this); } } } } 

Я думаю, мне нужно перевести некоторую информацию из матрицы в значение 0-99.

    Вы должны полностью реорганизовать свой код. Последующее умножение новых вращений в матрицу снова и снова представляет собой численно неустойчивое вычисление. В конце концов растровое изображение будет искажено. Попытка извлечь угол поворота из матрицы слишком сложна и не нужна.

    Прежде всего обратите внимание, что это полезная предыдущая статья о рисовании растровых изображений с вращением вокруг выбранной точки.

    Просто поддерживайте один double dialAngle = 0 который является текущим углом поворота циферблата.

    Вы делаете слишком много работы, чтобы получить угол от места касания. Пусть (x0,y0) – это место, где начинается сенсор. В то время,

     // Record the angle at initial touch for use in dragging. dialAngleAtTouch = dialAngle; // Find angle from x-axis made by initial touch coordinate. // y-coordinate might need to be negated due to y=0 -> screen top. // This will be obvious during testing. a0 = Math.atan2(y0 - yDialCenter, x0 - xDialCenter); 

    Это начальный угол. Когда касание тянется к (x,y) , используйте эту координату, чтобы отрегулировать циферблат относительно первоначального касания. Затем обновите матрицу и перерисуйте:

     // Find new angle to x-axis. Same comment as above on y coord. a = Math.atan2(y - yDialCenter, x - xDialCenter); // New dial angle is offset from the one at initial touch. dialAngle = dialAngleAtTouch + (a - a0); // normalize angles to the interval [0..2pi) while (dialAngle < 0) dialAngle += 2 * Math.PI; while (dialAngle >= 2 * Math.PI) dialAngle -= 2 * Math.PI; // Set the matrix for every frame drawn. Matrix API has a call // for rotation about a point. Use it! matrix.setRotate((float)dialAngle * (180 / 3.1415926f), xDialCenter, yDialCenter); // Invalidate the view now so it's redrawn in with the new matrix value. 

    Примечание. Math.atan2(y, x) делает все то, что вы делаете с квадрантами и арксинами.

    Чтобы получить «галочку» текущего угла, вам нужно 2 pi radians, чтобы соответствовать 100, так что это очень просто:

     double fractionalTick = dialAngle / (2 * Math.Pi) * 100; 

    Чтобы найти фактический ближайший тик как целое число, округлите долю и mod на 100. Обратите внимание, что вы можете игнорировать матрицу!

      int tick = (int)(fractionalTick + 0.5) % 100; 

    Это всегда будет работать, потому что dialAngle находится в [0..2pi). Мод необходим для сопоставления округленного значения 100 с точностью до 0.

    Чтобы лучше понять, что делает матрица, полезно понять матрицы преобразования 2d: http://en.wikipedia.org/wiki/Transformation_matrix#Examples_in_2D_graphics . Если единственное, что вы делаете, это вращение (не, скажем, преобразование или масштабирование), относительно легко извлечь вращение. Но, более практично, вы можете изменить код вращения и сохранить переменную состояния

      private float rotationDegrees = 0; /** * Rotate the dialer. * * @param degrees The degrees, the dialer should get rotated. */ private void rotateDialer(float degrees) matrix.postRotate(degrees, dialerWidth / 2, dialerHeight / 2); this.rotationDegrees += degrees; // Make sure we don't go over 360 this.rotationDegrees = this.rotationDegrees % 360 dialer.setImageMatrix(matrix); } 

    Храните переменную, чтобы сохранить полное вращение в градусах, которое вы увеличиваете в своей функции вращения. Теперь мы знаем, что 3,6 градуса – это тик. Простые математические выходы

     tickNumber = (int)rotation*100/360 // It could be negative if (tickNumber < 0) tickNumber = 100 - tickNumber 

    Последнее, что вам нужно проверить: если у вас есть вращение ровно на 360 градусов или количество тиков 100, вы должны рассматривать его как 0 (так как нет отметки 100)

    Это должно быть простое умножение с «масштабным» коэффициентом, который уменьшает ваше значение градуса (0-359) до вашей шкалы 0-99:

     float factor = 99f / 359f; float scaled = rotationDegree * factor; 

    EDIT: исправление функции getAngle

    Для getAngle вместо этого вы можете использовать функцию atan2 , которая преобразует декартовы координаты в угол.

    Просто сохраните первую касательную координату при касании и при движении, вы можете применить следующий расчет:

      // PointF a = touch start point // PointF b = current touch move point // Translate to origin: float x = bx - ax; float y = by - ay; float radians = (float) ((Math.atan2(-y, x) + Math.PI + HALF_PI) % TWO_PI); 

    Радианы имеют диапазон двух пи. По модулю вычисления вращают его так, что значение 0 точек вверх. Направление вращения против часовой стрелки.

    Поэтому вам нужно будет преобразовать это значение в градусы и изменить направление вращения для получения правильного угла.

    Циферблат должен поворачиваться ровно на 3,6 градуса, чтобы перейти от одной метки к следующей или предыдущей. Каждый раз, когда сенсор пользователя вращается (вокруг центра) на 3,6 градуса, циферблат должен поворачиваться на 1 метр (3,6 градуса).

    Фрагмент кода:

     float touchAngle = getTouchAngle(); float mark = touchAngle / 3.6f; if (mCurrentMark != mark) { setDialMark(mark); } 
    • getTouchAngle() вычисляет угол точки касания пользователя для набора номера с помощью atan2 .
    • setDialMark вращает циферблат по количеству измененных меток.

    ,

     void setDialMark(int mark) { rotateDialBy(mCurrentMark - mark); mCurrentMark = mark; } void rotateDialBy(int rotateMarks) { rotationAngle = rotateMarks * 3.6; ... /* Rotate the dial by rotationAngle. */ }