使用Android内置的手势侦听器和缩放侦听器实现捏缩放和拖动
image-processing
7
0

我正在尝试使用Android的手势侦听器和缩放侦听器来实现捏缩放和拖动。问题是,当我执行捏缩放时,图像(我正在尝试缩放)会反弹到特定位置。变焦位置也没有居中。以下代码演示了我要实现的目标。知道为什么图像跳动(以及如何校正)吗?

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;
}
}

提前致谢。

参考资料:
Stack Overflow
收藏
评论
共 1 个回答
高赞 时间 活跃

我已经实现了这种行为,并且使用矩阵来处理所有缩放和滚动(以我为例)。它使代码更简洁,并且像发条一样工作。

将矩阵存储为类成员:

Matrix drawMatrix;

编辑:存储旧的焦点,用于在缩放期间获取焦点偏移。

float lastFocusX;
float lastFocusY;

编辑 :在onScaleBegin中设置lastFocus变量

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

替换您的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中:

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

在onDraw;用您的drawMatrix绘制:

canvas.drawBitmap(image, drawMatrix, paint);

编码愉快!

收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号