Android - Pinch zoom ontouch координаты события - PullRequest
4 голосов
/ 20 марта 2012

Я пытаюсь получить координаты холста для создаваемого приложения для Android.Это прекрасно работает, пока я не добавлю код для использования точки фокусировки шкалы (следующие две строки):

scalePoint.setX((int) detector.getFocusX());
scalePoint.setY((int) detector.getFocusY());

Вот мой исходный код для моего класса представления:

    package us.kniffin.Jigsaw;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;

public class TestView extends View {
    private float mLastTouchX;
    private float mLastTouchY;
    private float mPosX;
    private float mPosY;

    private Rect rect;

    private float cX, cY; // circle coords


    // Scaling objects
    private ScaleGestureDetector mScaleDetector;
    private float mScaleFactor = 1.f;
    // The focus point for the scaling
    private float scalePointX; 
    private float scalePointY;


    public TestView(Context context) {
        super(context);

        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
    }

    @Override
    protected void onDraw(Canvas canvas) {      
        super.onDraw(canvas);
        Paint p = new Paint();
        p.setColor(Color.RED);

         rect = canvas.getClipBounds();

        canvas.save();
        canvas.scale(mScaleFactor, mScaleFactor, scalePointX, scalePointY);
        canvas.translate(mPosX, mPosY);
        canvas.drawCircle(cX, cY, 10, p);

        canvas.restore();

    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
         // Let the ScaleGestureDetector inspect all events.
        mScaleDetector.onTouchEvent(ev);

        final int action = ev.getAction();


        switch(action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN: {

            final float x = ev.getX()/mScaleFactor;// screen X position
            final float y = ev.getY()/mScaleFactor;// screen Y position

            cX = x - (rect.left * mScaleFactor) - mPosX; // canvas X
            cY = y - (rect.top * mScaleFactor) - mPosY; // canvas Y

            // Remember where we started
            mLastTouchX = x;
            mLastTouchY = y;
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            final float x = ev.getX()/mScaleFactor;
            final float y = ev.getY()/mScaleFactor;
            cX = x - (rect.left * mScaleFactor) - mPosX; // canvas X
            cY = y - (rect.top * mScaleFactor) - mPosY; // canvas Y


            // Only move if the ScaleGestureDetector isn't processing a gesture.
            if (!mScaleDetector.isInProgress()) {
                final float dx = x - mLastTouchX; // change in X
                final float dy = y - mLastTouchY; // change in Y

                mPosX += dx;
                mPosY += dy;

                invalidate();
            }

            mLastTouchX = x;
            mLastTouchY = y;

            break;

        }
        case MotionEvent.ACTION_UP: {
            mLastTouchX = 0;
            mLastTouchY = 0;
            invalidate();
        }
        }
        return true;
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            mScaleFactor *= detector.getScaleFactor();
            scalePointX =  detector.getFocusX();
            scalePointY = detector.getFocusY();

            // Don't let the object get too small or too large.
            mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));

            invalidate();
            return true;
        }
    }


}

Любые идеи, чтоМне нужно сделать, чтобы это работало?

Обновление: Я заменил пример кода другим, который имеет ту же проблему, но упрощен до основных

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

Ответы [ 3 ]

8 голосов
/ 22 марта 2012

Ха-ха!Успех!Это заняло у меня почти весь день, но я разобрался с кучей догадок и проверок.

Вот фрагмент кода, который мне был нужен:

case MotionEvent.ACTION_DOWN: {
            final float x = (ev.getX() - scalePointX)/mScaleFactor;
            final float y = (ev.getY() - scalePointY)/mScaleFactor;
            cX = x - mPosX + scalePointX; // canvas X
            cY = y - mPosY + scalePointY; // canvas Y
[snip]
}

И подобный фрагмент кодадля ACTION_MOVE

1 голос
/ 21 марта 2012

Всякий раз, когда вы используете перевод, масштабирование, вращение (и т. Д.), Вы меняете матрицу холста.Как правило, эта матрица используется для определения каждого (x, y) места на холсте.Когда вы создаете холст, матрица является единичной матрицей: (1) (1) (1) Но всякий раз, когда вы делаете одно из этих изменений, эта матрица также изменяется.Если вы хотите знать правильные координаты, то перед масштабированием / вращением следует использовать canvas.save (), а после масштабирования - canvas.restore ()

0 голосов
/ 25 мая 2013

Я недавно столкнулся с этой проблемой, когда делал что-то очень похожее, и после некоторых проб и ошибок и большого количества поисков в Google, я в итоге адаптировал этот ответ (https://stackoverflow.com/a/9945896/1131180):

(например, MotionEvent, поэтому лучше всего использовать этот код в onTouch или onLongPress)

mClickCoords = new float[2];

//e is the motionevent that contains the screen touch we
//want to translate into a canvas coordinate
mClickCoords[0] = e.getX();
mClickCoords[1] = e.getY();

Matrix matrix = new Matrix();
matrix.set(getMatrix());

//this is where you apply any translations/scaling/rotation etc.
//Typically you want to apply the same adjustments that you apply
//in your onDraw().

matrix.preTranslate(mVirtualX, mVirtualY);
matrix.preScale(mScaleFactor, mScaleFactor, mPivotX, mPivotY);

// invert the matrix, creating the mapping from screen 
//coordinates to canvas coordinates
matrix.invert(matrix); 

//apply the mapping
matrix.mapPoints(mClickCoords);

//mClickCoords[0] is the canvas x coordinate and
//mClickCoords[1] is the y coordinate.

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

...