Правильно установить точку фокусировки для изображения - PullRequest
0 голосов
/ 15 февраля 2012

У меня есть SurfaceView, который можно использовать для рисования Bitmap в качестве фона, и еще один, который будет использоваться в качестве наложения.Поэтому я решил сделать все преобразования, используя Matrix, который можно использовать для обоих растровых изображений, поскольку это (я думаю) один из самых быстрых способов сделать это без использования OpenGL.

Я былЯ могу реализовать панорамирование и масштабирование, но у меня есть некоторые проблемы с тем, с чем я пришел:

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

Есть ли способ добиться этого, используяМатрица или есть другое решение?

Код ниже (я использую SurfaceHolder в отдельном потоке, блокирую холст SurfaceView и вызываю его метод doDraw):

public class MapSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

    public void doDraw(Canvas canvas) {
        canvas.drawColor(Color.BLACK);
        canvas.drawBitmap(mBitmap, mTransformationMatrix, mPaintAA);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_POINTER_DOWN: {
                if (event.getPointerCount() == 2) {
                    mOriginalDistance = MathUtils.distanceBetween(event.getX(0), event.getX(1), event.getY(0), event.getY(1));
                    mScreenMidpoint = MathUtils.midpoint(event.getX(0), event.getX(1), event.getY(0), event.getY(1));
                    mImageMidpoint = MathUtils.midpoint((mXPosition+event.getX(0))/mScale, (mXPosition+event.getX(1))/mScale, (mYPosition+event.getY(0))/mScale, (mYPosition+event.getY(1))/mScale);
                    mOriginalScale = mScale;
                }
            }
            case MotionEvent.ACTION_DOWN: {
                mOriginalTouchPoint = new Point((int)event.getX(), (int)event.getY());
                mOriginalPosition = new Point(mXPosition, mYPosition);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                if (event.getPointerCount() == 2) {
                    final double currentDistance = MathUtils.distanceBetween(event.getX(0), event.getX(1), event.getY(0), event.getY(1));
                    if (mIsZooming || currentDistance - mOriginalDistance > mPinchToZoomTolerance || mOriginalDistance - currentDistance > mPinchToZoomTolerance) {
                        final float distanceRatio = (float) (currentDistance / mOriginalDistance);
                        float tempZoom = mOriginalScale * distanceRatio;

                        mScale = Math.min(10, Math.max(Math.min((float)getHeight()/(float)mBitmap.getHeight(), (float)getWidth()/(float)mBitmap.getWidth()), tempZoom));
                        mScale = (float) MathUtils.roundToDecimals(mScale, 1);
                        mIsZooming = true;
                        mTransformationMatrix = new Matrix();
                        mTransformationMatrix.setScale(mScale, mScale);//, mImageMidpoint.x, mImageMidpoint.y);
                    } else {
                        System.out.println("Dragging");
                        mIsZooming = false;
                        final int deltaX = (int) ((int) (mOriginalTouchPoint.x - event.getX()));
                        final int deltaY = (int) ((int) (mOriginalTouchPoint.y - event.getY()));
                        mXPosition = mOriginalPosition.x + deltaX;  
                        mYPosition = mOriginalPosition.y + deltaY;
                        validatePositions();
                        mTransformationMatrix = new Matrix();
                        mTransformationMatrix.setScale(mScale, mScale);
                        mTransformationMatrix.postTranslate(-mXPosition, -mYPosition);
                    }
                }
                break;
            }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP: {
                mIsZooming = false;
                validatePositions();
                mTransformationMatrix = new Matrix();
                mTransformationMatrix.setScale(mScale, mScale);
                mTransformationMatrix.postTranslate(-mXPosition, -mYPosition);
            }
        }
        return true;
    }

    private void validatePositions() {
        // Lower right corner
        mXPosition = Math.min(mXPosition, (int)((mBitmap.getWidth() * mScale)-getWidth()));
        mYPosition = Math.min(mYPosition, (int)((mBitmap.getHeight() * mScale)-getHeight()));
        // Upper left corner
        mXPosition = Math.max(mXPosition, 0);
        mYPosition = Math.max(mYPosition, 0);
        // Image smaller than the container, should center it
        if (mBitmap.getWidth() * mScale <= getWidth()) {
            mXPosition = (int) -((getWidth() - (mBitmap.getWidth() * mScale))/2);
        }
        if (mBitmap.getHeight() * mScale <= getHeight()) { 
            mYPosition = (int) -((getHeight() - (mBitmap.getHeight() * mScale))/2);
        }
    }
}

1 Ответ

2 голосов
/ 15 февраля 2012

Вместо того, чтобы каждый раз переустанавливать матрицу преобразования, используя новую Matrix (), попробуйте обновить ее, используя post * ().Таким образом, вы делаете только операции, связанные с экраном.Проще думать в терминах: «увеличить эту точку на экране».

Теперь немного кода.Рассчитав mScale в части масштабирования:

...
mScale = (float) MathUtils.roundToDecimals(mScale, 1);
float ratio = mScale / mOriginalScale;
mTransformationMatrix.postScale(ratio, ratio, mScreenMidpoint.x, mScreenMidpoint.y);

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

Во время перетаскивания вы переводите, используя deltaX и deltaY вместо абсолютных точек:

mTransformationMatrix.postTranslate(-deltaX, -deltaY);

Конечнотеперь вам нужно изменить метод validatePositions () на:

  • , чтобы deltaX и deltaY не заставляли изображение перемещаться слишком сильно, или
  • использовали матрицу преобразования, чтобы проверить, находится ли изображение вне экранаи затем переместите его в счетчик, чтобы

я опишу второй метод, так как он более гибкий и позволяет также проверять масштабирование.

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

void validate() {

    mTransformationMatrix.mapRect(new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()));

    float height = rect.height();
    float width = rect.width();

    float deltaX = 0, deltaY = 0;

    // Vertical delta
    if (height < mScreenHeight) {
        deltaY = (mScreenHeight - height) / 2 - rect.top;
    } else if (rect.top > 0) {
        deltaY = -rect.top;
    } else if (rect.bottom < mScreenHeight) {
        deltaY = mScreenHeight - rect.bottom;
    }

    // Horziontal delta
    if (width < mScreenWidth) {
        deltaX = (mScreenWidth - width) / 2 - rect.left;
    } else if (rect.left > 0) {
        deltaX = -rect.left;
    } else if (rect.right < mScreenWidth) {
        deltaX = mScreenWidth - rect.right;
    }

    mTransformationMatrix.postTranslate(deltaX, deltaY)
}
...