Подтвердить что ты не робот

Функция масштабирования и перетаскивания в SurfaceView

Я пытаюсь создать SurfaceView, который можно масштабировать и перетаскивать. Он реализует поток HTTP-изображений, который рисуется непосредственно в холсте

Я пробовал следующий код, и это вроде как работает... но это создает проблемы в границах. Не знаю, почему. Любая помощь?

Полный поток:

full stream 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;
}
4b9b3361

Ответ 1

Код, который вы опубликовали, является неполным, поэтому трудно сказать, в чем проблема. Я сбросил ваш код в быстрый демонстрационный проект и не видел никаких проблем с границами.

Просто посмотрев на скриншоты: любой шанс, что ваши данные изображения каким-то образом обертываются? Второй скриншот выглядит как нижняя граница рисуется в верхней части изображения. Снова трудно сказать без воспроизводимого кода.

Можно перерисовать фон перед перерисованием растрового изображения

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