使用 Android 内置的手势侦听器和缩放侦听器实现双指缩放和拖动 [英] Implementing pinch zoom and drag using Android's build in gesture listener and scale listener

查看:35
本文介绍了使用 Android 内置的手势侦听器和缩放侦听器实现双指缩放和拖动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 Android 的手势侦听器和缩放侦听器实现双指缩放和拖动.问题是,当我执行双指缩放时,图像(我正在尝试缩放)会反弹到特定位置.变焦位置也不居中.以下代码演示了我想要实现的目标.知道图像跳动的原因吗(以及如何纠正它)?

I am trying to implement pinch zoom and drag using Android's gesture listener and scale listener. The problem is that when I perform pinch zoom, the image (which I am trying to zoom) bounces to a particular location. Also the zoom position is not centered. The following code demonstrates what I am trying to achieve. Any idea why the image is jumping (and how to correct it) ?

public class CustomView extends View {
    Bitmap image;
    int screenHeight;
    int screenWidth;
    Paint paint;
    GestureDetector gestures;
    ScaleGestureDetector scaleGesture;
    float scale = 1.0f;
    float horizontalOffset, verticalOffset;

    int NORMAL = 0;
    int ZOOM = 1;
    int DRAG = 2;
    boolean isScaling = false;
    float touchX, touchY;
        int mode = NORMAL;

    public CustomView(Context context) {
    super(context);
            //initializing variables
    image = BitmapFactory.decodeResource(getResources(),
            R.drawable.image_name);
            //This is a full screen view
    screenWidth = getResources().getDisplayMetrics().widthPixels;
    screenHeight = getResources().getDisplayMetrics().heightPixels;
    paint = new Paint();
    paint.setAntiAlias(true);
    paint.setFilterBitmap(true);
    paint.setDither(true);
    paint.setColor(Color.WHITE);

    scaleGesture = new ScaleGestureDetector(getContext(),
            new ScaleListener());
    gestures = new GestureDetector(getContext(), new GestureListener());
    mode = NORMAL;
    initialize();
}

//Best fit image display on canvas 
    private void initialize() {
    float imgPartRatio = image.getWidth() / (float) image.getHeight();
    float screenRatio = (float) screenWidth / (float) screenHeight;

    if (screenRatio > imgPartRatio) {
        scale = ((float) screenHeight) / (float) (image.getHeight()); // fit height
        horizontalOffset = ((float) screenWidth - scale
                * (float) (image.getWidth())) / 2.0f;
        verticalOffset = 0;
    } else {
        scale = ((float) screenWidth) / (float) (image.getWidth()); // fit width
        horizontalOffset = 0;
        verticalOffset = ((float) screenHeight - scale
                * (float) (image.getHeight())) / 2.0f;
    }
        invalidate();
}

@Override
protected void onDraw(Canvas canvas) {
    canvas.save();
    canvas.drawColor(0, Mode.CLEAR);
    canvas.drawColor(Color.WHITE);
    if(mode == DRAG || mode == NORMAL) {
        //This works perfectly as expected
        canvas.translate(horizontalOffset, verticalOffset);
        canvas.scale(scale, scale);
        canvas.drawBitmap(image, getMatrix(), paint);
    }
    else if (mode == ZOOM) {
        //PROBLEM AREA - when applying pinch zoom,
        //the image jumps to a position abruptly
        canvas.scale(scale, scale, touchX, touchY);
        canvas.drawBitmap(image, getMatrix(), paint);
    }
    canvas.restore();
}

public class ScaleListener implements OnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        float scaleFactorNew = detector.getScaleFactor();
        if (detector.isInProgress()) {
            touchX = detector.getFocusX();
            touchY = detector.getFocusY();
            scale *= scaleFactorNew;
            invalidate(0, 0, screenWidth, screenHeight);
        }
        return true;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        isScaling = true;
        mode=ZOOM;
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        mode = NORMAL;
        isScaling = false;
    }

}

public class GestureListener implements GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener {

    @Override
    public boolean onDown(MotionEvent e) {
        isScaling = false;
        return true;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2,
            float distanceX, float distanceY) {
        if (!isScaling) {
            mode = DRAG;
            isScaling = false;
            horizontalOffset -= distanceX;
            verticalOffset -= distanceY;
            invalidate(0, 0, screenWidth, screenHeight);
        } else {
            mode = ZOOM;
            isScaling = true;
        }
        return true;
    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    scaleGesture.onTouchEvent(event);
    gestures.onTouchEvent(event);
    return true;
}
}

提前致谢.

推荐答案

我已经实现了这个行为,并且我使用了一个矩阵来处理所有的缩放和滚动(在我的例子中还有旋转).它可以编写整洁的代码并且像发条一样工作.

I have implemented this behaviour, and I used a matrix to handle all the zooming and scrolling (and rotation, in my case). It makes for neat code and works like clockwork.

将矩阵存储为类成员:

Matrix drawMatrix;

存储旧焦点,用于在缩放过程中获得焦点偏移.

Store old focus point, used to get the focus shift during scaling.

float lastFocusX;
float lastFocusY;

编辑:在 onScaleBegin 中设置 lastFocus 变量

Edit: Set lastFocus variables in onScaleBegin

@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
    lastFocusX = detector.getFocusX();
    lastFocusY = detector.getFocusY();
    return true;
}

替换您的 onScale:

Replace your onScale:

@Override
public boolean onScale(ScaleGestureDetector detector) {
    Matrix transformationMatrix = new Matrix();
    float focusX = detector.getFocusX();
    float focusY = detector.getFocusY();

    //Zoom focus is where the fingers are centered, 
    transformationMatrix.postTranslate(-focusX, -focusY);

    transformationMatrix.postScale(detector.getScaleFactor(), detector.getScaleFactor());

/* Adding focus shift to allow for scrolling with two pointers down. Remove it to skip this functionality. This could be done in fewer lines, but for clarity I do it this way here */
    //Edited after comment by chochim
    float focusShiftX = focusX - lastFocusX;
    float focusShiftY = focusY - lastFocusY;
    transformationMatrix.postTranslate(focusX + focusShiftX, focusY + focusShiftY);
    drawMatrix.postConcat(transformationMatrix);
    lastFocusX = focusX;
    lastFocusY = focusY;
    invalidate();
    return true;
}

在 onScroll 中类似:

Similarly in onScroll:

@Override
public boolean onScroll(MotionEvent downEvent, MotionEvent currentEvent,
            float distanceX, float distanceY) {
    drawMatrix.postTranslate(-distanceX, -distanceY);
    invalidate();
    return true;
}

在onDraw中;使用 drawMatrix 进行绘制:

in onDraw; Draw with your drawMatrix:

canvas.drawBitmap(image, drawMatrix, paint);

快乐编码!

这篇关于使用 Android 内置的手势侦听器和缩放侦听器实现双指缩放和拖动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆