SurfaceView中的缩放和拖动功能

23

我正在尝试创建一个可缩放和拖动的SurfaceView。它实现了一个HTTP图像流,直接绘制到画布上。

我尝试了以下代码,有点起作用...但在边界处给我带来了问题。不知道原因是什么。有人能帮忙吗?

完整流:

full stream image

放大的图像:

zoomed image

在第二张图片中,您可以看到多条不必要的绿线。
这是处理此流的类:
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Display;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.SurfaceView;
import android.view.WindowManager;

/**
 * Created by fil on 07/12/15.
 */
public class ZoomSurfaceView extends SurfaceView {
    //These two constants specify the minimum and maximum zoom
    private static float MIN_ZOOM = 1f;
    private static float MAX_ZOOM = 5f;

    private float scaleFactor = 1.f;
    private ScaleGestureDetector detector;

    //These constants specify the mode that we're in
    private static int NONE = 0;
    private static int DRAG = 1;
    private static int ZOOM = 2;

    private boolean dragged = false;
    private float displayWidth;
    private float displayHeight;

    private int mode;

    //These two variables keep track of the X and Y coordinate of the finger when it first
    //touches the screen
    private float startX = 0f;
    private float startY = 0f;

    //These two variables keep track of the amount we need to translate the canvas along the X
    //and the Y coordinate
    private float translateX = 0f;
    private float translateY = 0f;

    //These two variables keep track of the amount we translated the X and Y coordinates, the last time we
    //panned.
    private float previousTranslateX = 0f;
    private float previousTranslateY = 0f;

    private final Paint p = new Paint();

    private void init(Context context){
        detector = new ScaleGestureDetector(getContext(), new ScaleListener());
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();

        displayWidth = display.getWidth();
        displayHeight = display.getHeight();
    }

    public ZoomSurfaceView(Context context) {
        super(context);
        init(context);
    }

    public ZoomSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public ZoomSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public ZoomSurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    public void resetZoom() {

    }

    public void drawBitmap(Canvas canvas, Bitmap b, Rect rect){

        canvas.save();

        //If translateX times -1 is lesser than zero, letfs set it to zero. This takes care of the left bound
        if((translateX * -1) > (scaleFactor - 1) * displayWidth)
        {
            translateX = (1 - scaleFactor) * displayWidth;
        }

        if(translateY * -1 > (scaleFactor - 1) * displayHeight)
        {
            translateY = (1 - scaleFactor) * displayHeight;
        }

        //We need to divide by the scale factor here, otherwise we end up with excessive panning based on our zoom level
        //because the translation amount also gets scaled according to how much we've zoomed into the canvas.
        canvas.translate(translateX / scaleFactor, translateY / scaleFactor);

        //We're going to scale the X and Y coordinates by the same amount
        canvas.scale(scaleFactor, scaleFactor);

        canvas.drawBitmap(b, null, rect, p);

        /* The rest of your canvas-drawing code */
        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;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        switch (event.getAction() & MotionEvent.ACTION_MASK)
        {
            case MotionEvent.ACTION_DOWN:
                mode = DRAG;

                //We assign the current X and Y coordinate of the finger to startX and startY minus the previously translated
                //amount for each coordinates This works even when we are translating the first time because the initial
                //values for these two variables is zero.
                startX = event.getX() - previousTranslateX;
                startY = event.getY() - previousTranslateY;
                break;

            case MotionEvent.ACTION_MOVE:
                translateX = event.getX() - startX;
                translateY = event.getY() - startY;

                //We cannot use startX and startY directly because we have adjusted their values using the previous translation values.
                //This is why we need to add those values to startX and startY so that we can get the actual coordinates of the finger.
                double distance = Math.sqrt(Math.pow(event.getX() - (startX + previousTranslateX), 2) +
                        Math.pow(event.getY() - (startY + previousTranslateY), 2));

                if(distance > 0)
                {
                    dragged = true;
                    distance *= scaleFactor;
                }
                break;

            case MotionEvent.ACTION_POINTER_DOWN:
                break;

            case MotionEvent.ACTION_UP:
                mode = NONE;
                dragged = false;

                //All fingers went up, so letfs save the value of translateX and translateY into previousTranslateX and
                //previousTranslate
                previousTranslateX = translateX;
                previousTranslateY = translateY;
                break;

            case MotionEvent.ACTION_POINTER_UP:
                mode = DRAG;

                //This is not strictly necessary; we save the value of translateX and translateY into previousTranslateX
                //and previousTranslateY when the second finger goes up
                previousTranslateX = translateX;
                previousTranslateY = translateY;
                break;
        }

        detector.onTouchEvent(event);

        //We redraw the canvas only in the following cases:
        //
        // o The mode is ZOOM
        // OR
        // o The mode is DRAG and the scale factor is not equal to 1 (meaning we have zoomed) and dragged is
        // set to true (meaning the finger has actually moved)
        if ((mode == DRAG && scaleFactor != 1f && dragged) || mode == ZOOM)
        {
            invalidate();
        }

        return true;
    }
}

添加帧到表面的代码如下:
if (!b.isRecycled()){
    try {
        Rect rect = new Rect(0, 0, frame.getWidth(), frame.getHeight());
        Canvas canvas = frame.getHolder().lockCanvas();
        synchronized (frame.getHolder()) {
            if (!b.isRecycled()) {
                frame.drawBitmap(canvas, b, rect);
                b.recycle();
            }
        }
        frame.getHolder().unlockCanvasAndPost(canvas);
    } catch (java.lang.RuntimeException exc){
        Dbg.d("ERROR", exc);
    }
    lastBitmap = b;
}
1个回答

1
您发的代码不完整,很难说出问题在哪里。我将您的代码放入一个快速演示项目中,并没有看到边框方面的任何问题。
仅凭看截图:你的图片数据有没有可能出现了换行?第二个截图看起来像是底部边框被绘制在图像顶部。再次强调,没有可复现的代码很难确定问题所在。
可以尝试在重新绘制位图之前重新绘制背景。
canvas.drawRect(rect, backgroundPaint);
frame.drawBitmap(canvas, b, rect);

我已经添加了添加图像的代码。实际上,使用指向HTTP图像资源的流会更好...但现在我每次都只是下载位图(不知道如何更改)。 - Filnik
你每次绘制时都清除背景了吗?(更新的答案) - ayvazj
清除背景确实有效!唯一的问题是当我使用触摸移动缩放时,它不限于图像的边界。正在解决中。虽然现在缩放效果已经起作用了 :) 谢谢。我很快会发布完整的解决方案。 - Filnik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接