Android SDK - Not Scaling at Centre of Touch

左心房为你撑大大i 提交于 2020-01-01 08:58:46

问题


I have a little problem with my app. I have just implemented pinch zoom, but when I pinch to zoom, the image does not scale at the centre of the 2 points as displayed in the images below. Image 1 is where my fingers are before pinching and image 2 is to display that it has not scaled out at the centre of my fingers like it should.

Image 1 vs Image 2:

Here is my code:

        package com.jpiionefourone.guitarimage;

    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.ScaleGestureDetector;
    import android.view.View;

    public class MultiTouchView extends View {
        private Drawable mIcon;
        private float mPosX;
        private float mPosY;

        private float mLastTouchX;
        private float mLastTouchY;

        private static final int INVALID_POINTER_ID = -1;

        // The ‘active pointer’ is the one currently moving our object.
        private int mActivePointerId = INVALID_POINTER_ID;

        private ScaleGestureDetector mScaleDetector;
        private float mScaleFactor = 1.f;


        public MultiTouchView(Context context) {
            this(context, null, 0);
        }

        public MultiTouchView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }

        public MultiTouchView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            mIcon = context.getResources().getDrawable(R.drawable.guitar);
            mIcon.setBounds(0, 0, mIcon.getIntrinsicWidth(), mIcon.getIntrinsicHeight());

            // Create our ScaleGestureDetector
            mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());

        }

        @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();
                final float y = ev.getY();

                mLastTouchX = x;
                mLastTouchY = y;
                mActivePointerId = ev.getPointerId(0);
                break;
            }

            case MotionEvent.ACTION_MOVE: {
                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
                final float x = ev.getX(pointerIndex);
                final float y = ev.getY(pointerIndex);

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

                    mPosX += dx;
                    mPosY += dy;

                    invalidate();
                }

                mLastTouchX = x;
                mLastTouchY = y;

                break;
            }

            case MotionEvent.ACTION_UP: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }

            case MotionEvent.ACTION_CANCEL: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }

            case MotionEvent.ACTION_POINTER_UP: {
                final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) 
                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                final int pointerId = ev.getPointerId(pointerIndex);
                if (pointerId == mActivePointerId) {
                    // This was our active pointer going up. Choose a new
                    // active pointer and adjust accordingly.
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                    mLastTouchX = ev.getX(newPointerIndex);
                    mLastTouchY = ev.getY(newPointerIndex);
                    mActivePointerId = ev.getPointerId(newPointerIndex);
                }
                break;
            }
            }

            return true;
        }

        @Override
        public void onDraw(Canvas canvas) {
            super.onDraw(canvas);

            canvas.save();
            canvas.translate(mPosX, mPosY);
            canvas.scale(mScaleFactor, mScaleFactor);
            mIcon.draw(canvas);
            canvas.restore();
        }

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

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

                invalidate();
                return true;
            }
        }
    }

Thanks,

Jared


回答1:


In your ScaleListener class you only adjust mScaleFactor so, indeed, you image will indeed not zoom around the center of you multi-touch gesture!

Additionally, you need to update mPosX and mPosY to use a different center of scaling.

For one of my own applications, I used something like this:

final float scale = detector.getScaleFactor();
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor * scale, 5.0f));

if (mScaleFactor < 5f) {
   // 1 Grabbing
   final float centerX = detector.getFocusX();
   final float centerY = detector.getFocusY();
   // 2 Calculating difference
   float diffX = centerX - mPosX;
   float diffY = centerY - mPosY;
   // 3 Scaling difference
   diffX = diffX * scale - diffX;
   diffY = diffY * scale - diffY;
   // 4 Updating image origin
   mPosX -= diffX;
   mPosY -= diffY;
}

Basically, it calculates the relative change of the origin of your image by

  1. grabbing the origin of your multi-touch gesture (getFocus())
  2. calculating the difference between your image origin (diffX and diffY)
  3. applying the scale factor
  4. and updating the origin of the image by using the scaled difference

This (still) works for my situation. I do not know whether we both use(d) the same coordinate systems, but something like this should help you in properly scaling the image at the center of the multi-touch gesture.




回答2:


It took me really long to get this working.. however thanks for almost working code! I'm not sure was I just messing around or what but with this changes in code I got my zooming work like I wanted! Hopefully this will safe someones time.

mScaleFactor *= detector.getScaleFactor();
mScaleFactor = Math.max(0.5f, Math.min(mScaleFactor, 3.0f));
scale = mScaleFactor / scaleAtm


mTranslateMatrix.preTranslate(diffX, diffY);
mTranslateMatrix.invert(mTranslateMatrixInverse);
mPosX += diffX;
mPosY += diffY;
scaleAtm = mScaleFactor;

At the beginning scaleAtm = 1.



来源:https://stackoverflow.com/questions/13961817/android-sdk-not-scaling-at-centre-of-touch

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!