android - SurfaceView 中的缩放和拖动功能

标签 android stream surfaceview

我正在尝试创建一个可以缩放和拖动的 SurfaceView。它实现了一个直接绘制到 Canvas 中的 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;
}

最佳答案

您发布的代码不完整,因此很难说出问题所在。我确实将您的代码放入了一个快速演示项目,并且没有发现边框有任何问题。

仅通过查看屏幕截图:您的图像数据是否有可能以某种方式包装?第二个屏幕截图看起来像底部边框被绘制在图像的顶部。如果没有可复现的代码,这也很难说。

可能会在重绘位图之前尝试重绘背景

canvas.drawRect(rect, backgroundPaint);
frame.drawBitmap(canvas, b, rect);

关于android - SurfaceView 中的缩放和拖动功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34224156/

相关文章:

Android:SurfaceView 和 Z 级问题

c# - 使用 TcpListener 取消 NetworkStream.ReadAsync

xml - ENAMETOOLONG 请求 xml 文档

java - 带有动画的 WindowManager(可能吗?)

android - 如何从 ViewFlipper 获取总观看次数?

multithreading - 使用流时出现 Haskell 破管错误

android - 即使设置了正确的尺寸,相机预览也会被拉伸(stretch)

android - Surfaceview 和 TextureView 有什么区别?

android - 请求 id_token 时 Google oauth INVALID_AUDIENCE

android - RxJava 代替接口(interface)回调 (Android)