我正在尝试创建一个用户可以在其中拖动和缩放ImageView的应用程序。但是我在以下代码中遇到问题。

当scaleFactor不为1并且第二根手指放下时,它会稍微平移到其他位置。我不知道翻译的来源...

这是完整的课程:

package me.miutaltbati.ramaview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;

import static android.view.MotionEvent.INVALID_POINTER_ID;

public class RamaView extends ImageView {
    private Context context;
    private Matrix matrix = new Matrix();
    private Matrix translateMatrix = new Matrix();
    private Matrix scaleMatrix = new Matrix();

    // Properties coming from outside:
    private int drawableLayoutId;
    private int width;
    private int height;

    private static float MIN_ZOOM = 0.33333F;
    private static float MAX_ZOOM = 5F;

    private PointF mLastTouch = new PointF(0, 0);
    private PointF mLastFocus = new PointF(0, 0);
    private PointF mLastPivot = new PointF(0, 0);
    private float mPosX = 0F;
    private float mPosY = 0F;

    public float scaleFactor = 1F;
    private int mActivePointerId = INVALID_POINTER_ID;

    private Paint paint;
    private Bitmap bitmapLayout;

    private OnFactorChangedListener mListener;
    private ScaleGestureDetector mScaleDetector;

    public RamaView(Context context) {
        super(context);
        initializeInConstructor(context);
    }

    public RamaView(Context context, @android.support.annotation.Nullable AttributeSet attrs) {
        super(context, attrs);
        initializeInConstructor(context);
    }

    public RamaView(Context context, @android.support.annotation.Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initializeInConstructor(context);
    }

    public void initializeInConstructor(Context context) {
        this.context = context;
        paint = new Paint();
        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
        mScaleDetector.setQuickScaleEnabled(false);

        setScaleType(ScaleType.MATRIX);
    }

    public Bitmap decodeSampledBitmap() {
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inMutable = true;
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), drawableLayoutId, options);

        // Calculate inSampleSize
        options.inSampleSize = Util.calculateInSampleSize(options, width, height); // e.g.: 4, 8

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(getResources(), drawableLayoutId, options);
    }

    public void setDrawable(int drawableId) {
        drawableLayoutId = drawableId;
    }

    public void setSize(int width, int height) {
        this.width = width;
        this.height = height;

        bitmapLayout = decodeSampledBitmap();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mScaleDetector.onTouchEvent(event);

        int action = event.getActionMasked();

        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                int pointerIndex = event.getActionIndex();
                float x = event.getX(pointerIndex);
                float y = event.getY(pointerIndex);

                // Remember where we started (for dragging)
                mLastTouch = new PointF(x, y);

                // Save the ID of this pointer (for dragging)
                mActivePointerId = event.getPointerId(0);
            }

            case MotionEvent.ACTION_POINTER_DOWN: {
                if (event.getPointerCount() == 2) {
                    mLastFocus = new PointF(mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
                }
            }

            case MotionEvent.ACTION_MOVE: {
                // Find the index of the active pointer and fetch its position
                int pointerIndex = event.findPointerIndex(mActivePointerId);

                float x = event.getX(pointerIndex);
                float y = event.getY(pointerIndex);

                // Calculate the distance moved
                float dx = 0;
                float dy = 0;

                if (event.getPointerCount() == 1) {
                    // Calculate the distance moved
                    dx = x - mLastTouch.x;
                    dy = y - mLastTouch.y;

                    matrix.setScale(scaleFactor, scaleFactor, mLastPivot.x, mLastPivot.y);

                    // Remember this touch position for the next move event
                    mLastTouch = new PointF(x, y);
                } else if (event.getPointerCount() == 2) {
                    // Calculate the distance moved
                    dx = mScaleDetector.getFocusX() - mLastFocus.x;
                    dy = mScaleDetector.getFocusY() - mLastFocus.y;

                    matrix.setScale(scaleFactor, scaleFactor, -mPosX + mScaleDetector.getFocusX(), -mPosY + mScaleDetector.getFocusY());

                    mLastPivot = new PointF(-mPosX + mScaleDetector.getFocusX(), -mPosY + mScaleDetector.getFocusY());
                    mLastFocus = new PointF(mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
                }

                mPosX += dx;
                mPosY += dy;

                matrix.postTranslate(mPosX, mPosY);

                break;
            }

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

            case MotionEvent.ACTION_POINTER_UP: {
                final int pointerIndex = event.getActionIndex();
                final int pointerId = event.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;
                    mLastTouch = new PointF(event.getX(newPointerIndex), event.getY(newPointerIndex));
                    mActivePointerId = event.getPointerId(newPointerIndex);
                } else {
                    final int tempPointerIndex = event.findPointerIndex(mActivePointerId);
                    mLastTouch = new PointF(event.getX(tempPointerIndex), event.getY(tempPointerIndex));
                }

                break;
            }
        }

        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();

        canvas.setMatrix(matrix);
        canvas.drawColor(Color.BLACK);
        canvas.drawBitmap(bitmapLayout, 0, 0, paint);

        canvas.restore();
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            scaleFactor *= detector.getScaleFactor();
            scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));

            return true;
        }
    }
}


我认为问题出在这条线上:

matrix.setScale(scaleFactor, scaleFactor, -mPosX + mScaleDetector.getFocusX(), -mPosY + mScaleDetector.getFocusY());


我尝试了很多事情,但无法正常工作。

更新:

以下是初始化RamaView实例的方法:

主要活动的onCreate:

rvRamaView = findViewById(R.id.rvRamaView);

final int[] rvSize = new int[2];
ViewTreeObserver vto = rvRamaView.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    @Override
    public boolean onPreDraw() {
        rvRamaView.getViewTreeObserver().removeOnPreDrawListener(this);
        rvSize[0] = rvRamaView.getMeasuredWidth();
        rvSize[1] = rvRamaView.getMeasuredHeight();

        rvRamaView.setSize(rvSize[0], rvSize[1]);
        return true;
    }
});

rvRamaView.setDrawable(R.drawable.original_jpg);

最佳答案

最好使用矩阵来累积更改,而不是尝试自己重新计算转换。您可以使用矩阵post ...和pre ...方法来执行此操作,并远离重置矩阵的set ...方法。

这是RamaView类的重做,除了上面提到的矩阵的特定处理外,RamaView类主要针对目标。该模块是onTouchEvent()方法的。视频是在示例应用程序中工作的代码的输出。

android - 如何放大ImageView,而不会在其他位置略微跳动? (Android)-LMLPHP

RamaView.java

public class RamaView extends ImageView {
    private final Matrix matrix = new Matrix();

    // Properties coming from outside:
    private int drawableLayoutId;
    private int width;
    private int height;

    private static final float MIN_ZOOM = 0.33333F;
    private static final float MAX_ZOOM = 5F;

    private PointF mLastTouch = new PointF(0, 0);
    private PointF mLastFocus = new PointF(0, 0);

    public float scaleFactor = 1F;
    private int mActivePointerId = INVALID_POINTER_ID;

    private Paint paint;
    private Bitmap bitmapLayout;

    //    private OnFactorChangedListener mListener;
    private ScaleGestureDetector mScaleDetector;

    public RamaView(Context context) {
        super(context);
        initializeInConstructor(context);
    }

    public RamaView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initializeInConstructor(context);
    }

    public RamaView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initializeInConstructor(context);
    }

    public void initializeInConstructor(Context context) {
        paint = new Paint();
        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
        mScaleDetector.setQuickScaleEnabled(false);

        setScaleType(ScaleType.MATRIX);
    }

    public Bitmap decodeSampledBitmap() {
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inMutable = true;
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), drawableLayoutId, options);

        options.inSampleSize = Util.calculateInSampleSize(options, width, height); // e.g.: 4, 8

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(getResources(), drawableLayoutId, options);
    }

    public void setDrawable(int drawableId) {
        drawableLayoutId = drawableId;
    }

    public void setSize(int width, int height) {
        this.width = width;
        this.height = height;

        bitmapLayout = decodeSampledBitmap();
    }

    private float mLastScaleFactor = 1.0f;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mScaleDetector.onTouchEvent(event);

        int action = event.getActionMasked();

        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                int pointerIndex = event.getActionIndex();
                float x = event.getX(pointerIndex);
                float y = event.getY(pointerIndex);

                // Remember where we started (for dragging)
                mLastTouch = new PointF(x, y);

                // Save the ID of this pointer (for dragging)
                mActivePointerId = event.getPointerId(0);
            }

            case MotionEvent.ACTION_POINTER_DOWN: {
                if (event.getPointerCount() == 2) {
                    mLastFocus = new PointF(mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
                }
            }

            case MotionEvent.ACTION_MOVE: {
                // Find the index of the active pointer and fetch its position
                int pointerIndex = event.findPointerIndex(mActivePointerId);

                float x = event.getX(pointerIndex);
                float y = event.getY(pointerIndex);

                // Calculate the distance moved
                float dx = 0;
                float dy = 0;

                if (event.getPointerCount() == 1) {
                    // Calculate the distance moved
                    dx = x - mLastTouch.x;
                    dy = y - mLastTouch.y;

                    // Remember this touch position for the next move event
                    mLastTouch = new PointF(x, y);
                } else if (event.getPointerCount() == 2) {
                    // Calculate the distance moved
                    float focusX = mScaleDetector.getFocusX();
                    float focusY = mScaleDetector.getFocusY();
                    dx = focusX - mLastFocus.x;
                    dy = focusY - mLastFocus.y;

                    // Since we are accumating translation/scaling, we are just adding to
                    // the previous scale.
                    matrix.postScale(scaleFactor/mLastScaleFactor, scaleFactor/mLastScaleFactor, focusX, focusY);
                    mLastScaleFactor = scaleFactor;

                    mLastFocus = new PointF(focusX, focusY);
                }

                // Translation is cumulative.
                matrix.postTranslate(dx, dy);

                break;
            }

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

            case MotionEvent.ACTION_POINTER_UP: {
                final int pointerIndex = event.getActionIndex();
                final int pointerId = event.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;
                    mLastTouch = new PointF(event.getX(newPointerIndex), event.getY(newPointerIndex));
                    mActivePointerId = event.getPointerId(newPointerIndex);
                } else {
                    final int tempPointerIndex = event.findPointerIndex(mActivePointerId);
                    mLastTouch = new PointF(event.getX(tempPointerIndex), event.getY(tempPointerIndex));
                }

                break;
            }
        }

        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();

        canvas.setMatrix(matrix);
        canvas.drawColor(Color.BLACK);
        canvas.drawBitmap(bitmapLayout, 0, 0, paint);

        canvas.restore();
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            scaleFactor *= detector.getScaleFactor();
            scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));

            return true;
        }
    }
}

关于android - 如何放大ImageView,而不会在其他位置略微跳动? (Android),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58667202/

10-11 03:28