Увеличьте масштаб на SurfaceView - PullRequest
0 голосов
/ 18 мая 2018

Пожалуйста, прочитайте РЕДАКТИРОВАТЬ 3 ..

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

public class ZoomLayout extends FrameLayout implements ScaleGestureDetector.OnScaleGestureListener {

private SurfaceView mSurfaceView;

private enum Mode {
    NONE,
    DRAG,
    ZOOM
}

private static final String TAG = "ZoomLayout";
private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = 4.0f;

private Mode mode;
private float scale = 1.0f;
private float lastScaleFactor = 0f;

// Where the finger first  touches the screen
private float startX = 0f;
private float startY = 0f;

// How much to translate the canvas
private float dx = 0f;
private float dy = 0f;
private float prevDx = 0f;
private float prevDy = 0f;

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

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

public ZoomLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init(context);
}

private void init(Context context) {
    final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, this);
    setOnTouchListener(new OnTouchListener() {
        public boolean onTouch(View view, MotionEvent motionEvent) {
            ZoomLayout.this.mSurfaceView = (SurfaceView) view.findViewById(R.id.mSurfaceView);

            switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    Log.i(TAG, "DOWN");
                    if (scale > MIN_ZOOM) {
                        mode = Mode.DRAG;
                        startX = motionEvent.getX() - prevDx;
                        startY = motionEvent.getY() - prevDy;
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (mode == Mode.DRAG) {
                        dx = motionEvent.getX() - startX;
                        dy = motionEvent.getY() - startY;
                    }
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    mode = Mode.ZOOM;
                    break;
                case MotionEvent.ACTION_POINTER_UP:
                    mode = Mode.NONE;
                    break;
                case MotionEvent.ACTION_UP:
                    Log.i(TAG, "UP");
                    mode = Mode.NONE;
                    prevDx = dx;
                    prevDy = dy;
                    break;
            }
            scaleDetector.onTouchEvent(motionEvent);

            if ((mode == Mode.DRAG && scale >= MIN_ZOOM) || mode == Mode.ZOOM) {
                getParent().requestDisallowInterceptTouchEvent(true);
                float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
                float maxDy = (child().getHeight() - (child().getHeight() / scale))/ 2 * scale;
                dx = Math.min(Math.max(dx, -maxDx), maxDx);
                dy = Math.min(Math.max(dy, -maxDy), maxDy);
                Log.i(TAG, "Width: " + child().getWidth() + ", scale " + scale + ", dx " + dx
                        + ", max " + maxDx);
                applyScaleAndTranslation();
            }

            return true;
        }
    });
}

// ScaleGestureDetector

public boolean onScaleBegin(ScaleGestureDetector scaleDetector) {
    Log.i(TAG, "onScaleBegin");
    return true;
}

public boolean onScale(ScaleGestureDetector scaleDetector) {
    float scaleFactor = scaleDetector.getScaleFactor();
    Log.i(TAG, "mode:" + this.mode + ", onScale:" + scaleFactor);
    if (this.lastScaleFactor == 0.0f || Math.signum(scaleFactor) == Math.signum(this.lastScaleFactor)) {
        this.scale *= scaleFactor;
        this.scale = Math.max(MIN_ZOOM, Math.min(this.scale, MAX_ZOOM));
        this.lastScaleFactor = scaleFactor;
    } else {
        this.lastScaleFactor = 0.0f;
    }
    if (this.mSurfaceView != null) {
        int orgWidth = getWidth();
        int _width = (int) (((float) orgWidth) * this.scale);
        int _height = (int) (((float) getHeight()) * this.scale);
        LayoutParams params = (LayoutParams) this.mSurfaceView.getLayoutParams();
        params.height = _height;
        params.width = _width;
        this.mSurfaceView.setLayoutParams(params);
        child().setScaleX(this.scale);
        child().setScaleY(this.scale);
    }
    return true;
}

@Override
public void onScaleEnd(ScaleGestureDetector scaleDetector) {
    Log.i(TAG, "onScaleEnd");
}

private void applyScaleAndTranslation() {
    child().setScaleX(scale);
    child().setScaleY(scale);
    child().setTranslationX(dx);
    child().setTranslationY(dy);
}

private View child() {
    return getChildAt(0);
}

и я внедрил его в свой макет следующим образом:

<pacageName.control.ZoomLayout
    android:id="@+id/mZoomLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:measureAllChildren="true">

        <SurfaceView
            android:id="@+id/mSurfaceView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

</pacageName.control.ZoomLayout>

Проблема, с которой я столкнулся, заключается в том, что масштабирование не является «плавным»и поведение зума отличается на разных устройствах (Samsung S4 (Lollipop) и J7Pro (Nougat)).

Я не уверен, почему пинч-зум "глючит" и почему он не увеличиваетодинаково на двух разных устройствах ..

Кто-нибудь имел дело с этим, что может, возможно, направить меня в правильном направлении?


Редактировать 1: Память иИзображение потребления процессора добавлено ниже -

CPU


РЕДАКТИРОВАТЬ 2: После попытки чего-то другого я столкнулся с новой проблемой-

Хорошо, поэтому я полностью изменил свой ZoomLayout на следующее:

public class ZoomableSurfaceView extends SurfaceView {

private ScaleGestureDetector SGD;
private Context context;
private boolean isSingleTouch;
private float width, height = 0;
private float scale = 1f;
private float minScale = 1f;
private float maxScale = 5f;
int left, top, right, bottom;

public ZoomableSurfaceView(Context context) {
    super(context);
    this.context = context;
    init();
}

public ZoomableSurfaceView(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.context = context;
    init();
}

public ZoomableSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.context = context;
    init();
}

private void init() {
    setOnTouchListener(new MyTouchListeners());
    SGD = new ScaleGestureDetector(context, new ScaleListener());
    this.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {

        }
    });
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    if (width == 0 && height == 0) {
        width = ZoomableSurfaceView.this.getWidth();
        height = ZoomableSurfaceView.this.getHeight();
        this.left = left;
        this.right = right;
        this.top = top;
        this.bottom = bottom;
    }

}

private class MyTouchListeners implements View.OnTouchListener {

    float dX, dY;

    MyTouchListeners() {
        super();
    }

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        SGD.onTouchEvent(event);
        if (event.getPointerCount() > 1) {
            isSingleTouch = false;
        } else {
            if (event.getAction() == MotionEvent.ACTION_UP) {
                isSingleTouch = true;
            }
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                dX = ZoomableSurfaceView.this.getX() - event.getRawX();
                dY = ZoomableSurfaceView.this.getY() - event.getRawY();
                break;

            case MotionEvent.ACTION_MOVE:
                if (isSingleTouch) {
                    ZoomableSurfaceView.this.animate()
                            .x(event.getRawX() + dX)
                            .y(event.getRawY() + dY)
                            .setDuration(0)
                            .start();
                    checkDimension(ZoomableSurfaceView.this);
                }
                break;
            default:
                return true;
        }
        return true;
    }
}

private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        //Log.e("onGlobalLayout: ", scale + " " + width + " " + height);
        scale *= detector.getScaleFactor();
        scale = Math.max(minScale, Math.min(scale, maxScale));
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams((int) (width * scale), (int) (height * scale));
        Log.e("onGlobalLayout: ", (int) (width * scale) + " " + (int) (height * scale));
        ZoomableSurfaceView.this.setLayoutParams(params);
        checkDimension(ZoomableSurfaceView.this);
        return true;
    }
}

private void checkDimension(View vi) {
    if (vi.getX() > left) {
        vi.animate()
                .x(left)
                .y(vi.getY())
                .setDuration(0)
                .start();
    }

    if ((vi.getWidth() + vi.getX()) < right) {
        vi.animate()
                .x(right - vi.getWidth())
                .y(vi.getY())
                .setDuration(0)
                .start();
    }

    if (vi.getY() > top) {
        vi.animate()
                .x(vi.getX())
                .y(top)
                .setDuration(0)
                .start();
    }

    if ((vi.getHeight() + vi.getY()) < bottom) {
        vi.animate()
                .x(vi.getX())
                .y(bottom - vi.getHeight())
                .setDuration(0)
                .start();
    }
}
}

ПРОБЛЕМА Я сталкиваюсь сейчас с тем, что она приближается к X -0 и Y - 0 означает, что он увеличивает левую верхнюю часть экрана вместо увеличения до точки между пальцами. Я думаю, что это может быть связано сиз-за:

@Override
    public boolean onScale(ScaleGestureDetector detector) {
        //Log.e("onGlobalLayout: ", scale + " " + width + " " + height);
        scale *= detector.getScaleFactor();
        scale = Math.max(minScale, Math.min(scale, maxScale));
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams((int) (width * scale), (int) (height * scale));
        Log.e("onGlobalLayout: ", (int) (width * scale) + " " + (int) (height * scale));
        ZoomableSurfaceView.this.setLayoutParams(params);
        checkDimension(ZoomableSurfaceView.this);
        return true;
    }

У меня есть идея, что я должен определить центральную точку двух пальцев и добавить это к OnScale, но мне нужна помощь, есть идеи, пожалуйста?


РЕДАКТИРОВАТЬ 3:

Хорошо, наконец-то у меня отлично работает зум на моей J7 Pro под управлением Nougat.Но проблема сейчас в том, что мой SurfaceView в моем S4 Lollipop не увеличивается, вместо этого мой SurfaceView перемещается в верхний левый угол.Я проверил размещение ImageView внутри моего собственного zoomView, и изображение стало масштабироваться совершенно, как и ожидалось.Вот как теперь выглядит мой пользовательский класс увеличения:

public class ZoomLayout extends FrameLayout implements ScaleGestureDetector.OnScaleGestureListener {

private enum Mode {
    NONE,
    DRAG,
    ZOOM
}

private static final String TAG = "ZoomLayout";
private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = 4.0f;

private Mode mode = Mode.NONE;
private float scale = 1.0f;
private float lastScaleFactor = 0f;

// Where the finger first  touches the screen
private float startX = 0f;
private float startY = 0f;

// How much to translate the canvas
private float dx = 0f;
private float dy = 0f;
private float prevDx = 0f;
private float prevDy = 0f;

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

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

public ZoomLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init(context);
}

private void init(Context context) {
    final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, this);
    setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    Log.i(TAG, "DOWN");
                    if (scale > MIN_ZOOM) {
                        mode = Mode.DRAG;
                        startX = motionEvent.getX() - prevDx;
                        startY = motionEvent.getY() - prevDy;
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (mode == Mode.DRAG) {
                        dx = motionEvent.getX() - startX;
                        dy = motionEvent.getY() - startY;
                    }
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    mode = Mode.ZOOM;
                    break;
                case MotionEvent.ACTION_POINTER_UP:
                    mode = Mode.NONE; // changed from DRAG, was messing up zoom
                    break;
                case MotionEvent.ACTION_UP:
                    Log.i(TAG, "UP");
                    mode = Mode.NONE;
                    prevDx = dx;
                    prevDy = dy;
                    break;
            }
            scaleDetector.onTouchEvent(motionEvent);

            if ((mode == Mode.DRAG && scale >= MIN_ZOOM) || mode == Mode.ZOOM) {
                getParent().requestDisallowInterceptTouchEvent(true);
                float maxDx = child().getWidth() * (scale - 1);  // adjusted for zero pivot
                float maxDy = child().getHeight() * (scale - 1);  // adjusted for zero pivot
                dx = Math.min(Math.max(dx, -maxDx), 0);  // adjusted for zero pivot
                dy = Math.min(Math.max(dy, -maxDy), 0);  // adjusted for zero pivot
                Log.i(TAG, "Width: " + child().getWidth() + ", scale " + scale + ", dx " + dx
                        + ", max " + maxDx);
                applyScaleAndTranslation();
            }

            return true;
        }
    });
}

// ScaleGestureDetector
@Override
public boolean onScaleBegin(ScaleGestureDetector scaleDetector) {
    Log.i(TAG, "onScaleBegin");
    return true;
}

@Override
public boolean onScale(ScaleGestureDetector scaleDetector) {
    float scaleFactor = scaleDetector.getScaleFactor();
    Log.i(TAG, "onScale(), scaleFactor = " + scaleFactor);
    if (lastScaleFactor == 0 || (Math.signum(scaleFactor) == Math.signum(lastScaleFactor))) {
        float prevScale = scale;
        scale *= scaleFactor;
        scale = Math.max(MIN_ZOOM, Math.min(scale, MAX_ZOOM));
        lastScaleFactor = scaleFactor;
        float adjustedScaleFactor = scale / prevScale;
        // added logic to adjust dx and dy for pinch/zoom pivot point
        Log.d(TAG, "onScale, adjustedScaleFactor = " + adjustedScaleFactor);
        Log.d(TAG, "onScale, BEFORE dx/dy = " + dx + "/" + dy);
        float focusX = scaleDetector.getFocusX();
        float focusY = scaleDetector.getFocusY();
        Log.d(TAG, "onScale, focusX/focusy = " + focusX + "/" + focusY);
        dx += (dx - focusX) * (adjustedScaleFactor - 1);
        dy += (dy - focusY) * (adjustedScaleFactor - 1);
        Log.d(TAG, "onScale, dx/dy = " + dx + "/" + dy);
    } else {
        lastScaleFactor = 0;
    }
    return true;
}

@Override
public void onScaleEnd(ScaleGestureDetector scaleDetector) {
    Log.i(TAG, "onScaleEnd");
}

private void applyScaleAndTranslation() {
    child().setScaleX(scale);
    child().setScaleY(scale);
    child().setPivotX(0f);  // default is to pivot at view center
    child().setPivotY(0f);  // default is to pivot at view center
    child().setTranslationX(dx);
    child().setTranslationY(dy);
}

private View child() {
    return getChildAt(0);
}

Уже три дня я борюсь с этим, кто-то, пожалуйста, помогите мне избавиться от моих страданий;) Я могу загрузить видео, демонстрирующее проблему, еслинеобходимо ..


Изменить 4: Добавлено видео о поведении:

Посмотрите, как выглядит текущий масштаб после обновления моего класса zoomLayout для редактирования 3 -

Видео поведения увеличения SurfaceView

Ответы [ 2 ]

0 голосов
/ 30 марта 2019

масштабирование SurfaceView путем масштабирования его родительского FrameLayout будет работать только в Nougat (API 24) и выше

это масштабирование не меняет разрешение SurfaceView, т.е. оно намного безопаснее и плавнее, чем масштабирование SurfaceView путем изменения его LayoutParams (но разрешение исправлено).Это единственный вариант для пользовательского просмотра, например GStreamerSurfaceView

0 голосов
/ 21 мая 2018

Вместо использования SurfaceView используйте TextureView, который имеет больше функций и предпочтительнее, чем SurfaceView.См. Документация TextureView


Ваш код действительно правильный и должен работать, но в некоторых случаях MediaPlayer, нарисованный на SurfaceView, не изменяет размер и создает иллюзиюзум происходит в положении 0,0 телефона.Если вы установите размер меньше родительского для SurfaceView и установите фон для родительского элемента, вы заметите это


РЕШЕНИЕ В КОДЕ

Пример проекта: Pinch Zoom

Проект расширяет TextureView и реализует прикосновение для создания эффекта масштабирования.

ИЗОБРАЖЕНИЕ: ДЕМОНСТРАЦИЯ

...