Zooming and drag feature in SurfaceView

Filnik

I'm trying to create a SurfaceView that can be zoomed and dragged. It implements an HTTP image stream that draws directly into the canvas

I've tried the following code and it kinda work... but it gives me problems in the borders. No idea of the reason why. Any help?

Full stream:

full stream image

Zoomed image:

zoomed image

In the second image you can see multiple green lines that doesn't need to be there.

This is the class that handles this stream:

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

The code for adding the frames to the surface is the following:

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

The code you posted is incomplete so its difficult to say what the problem is. I did drop your code into a quick demo project and didn't see any issues with the borders.

Just by looking at the screenshots: any chance that your image data is somehow wrapping? The 2nd screenshot looks like the bottom border is being drawn at the top of the image. Again tough to say without reproducible code.

Might try repainting the background before redrawing the bitmap

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

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

drag and drop feature in AngularJs

From Dev

Drag and drop feature in JFreeChart

From Dev

Image Zooming Feature using jQuery CSS

From Dev

Drag Drop Feature and Arrow Map

From Dev

How to drag images / objects within Canvas after zooming / scaling them?

From Dev

How to Drag a bitmap over another Bitmap on Canvas with Surfaceview in Android

From Dev

Drag/Drop feature in Android studio not working

From Dev

Angular2 typescript drag and drop feature

From Dev

How to disable Drag feature in android rating bar

From Dev

Drag a feature POLYGON OL3

From Dev

Drag and drop feature in jsp using jquery

From Dev

Angular2 typescript drag and drop feature

From Dev

How to get chart min and max values after Drag zooming in highChart chart?

From Dev

How to implement Facebook drag and drop feature in uploading files

From Dev

Apple Macbook Pro - Remote desktop to Windows 7 with drag & drop feature

From Dev

SurfaceView on top of SurfaceView

From Dev

Zooming an activity

From Dev

Open Layers 3 Drag & Drop feature, get file name of file dropped

From Dev

Can the default size of Windows 7's "drag to the left or right to resize window" feature be changed?

From Dev

Zooming in and zooming out in one div

From Dev

SurfaceView in widget

From Dev

XML to SurfaceView

From Dev

SurfaceView Example

From Dev

ViewController animation like zooming and zooming out

From Dev

Zooming in overflow: scroll

From Dev

Zooming an SKNode inconsistent

From Dev

zooming in and out an image in android

From Dev

Zooming in stops coloring of the div

From Dev

Google maps embed not zooming