Zoomlayout: Toca dos veces para hacer zoom

Creado en 29 sept. 2017  ·  13Comentarios  ·  Fuente: natario1/ZoomLayout

Es común reaccionar a los dobles toques para controlar el zoom. Esta debería ser una función opcional en ZoomEngine , habilitada de forma predeterminada en ZoomImageView .

enhancement long term

Comentario más útil

Hola,

¿Alguna noticia sobre cómo lograr zoom + panorámica con un doble toque?

Gracias,
Cristóbal

Todos 13 comentarios

@natario1

Por ahora, acabo de implementar esta funcionalidad por mi cuenta con la clase Gesture Listener personalizada. Aquí está mi fragmento de código si alguien lo necesita.

private GestureDetector gestureDetector; 
private View.OnTouchListener touchListener;
private ZoomImageView selectedImage;

luego inicialice estas variables:

    gestureDetector = new GestureDetector(mContext, new MyGestureListener());

        touchListener = new View.OnTouchListener() {
            <strong i="11">@Override</strong>
            public boolean onTouch(View v, MotionEvent event) {
                // pass the events to the gesture detector
                // a return value of true means the detector is handling it
                // a return value of false means the detector didn't
                // recognize the event
                selectedImage = (ZoomImageView) v;
                return gestureDetector.onTouchEvent(event);

            }
        };

luego asigne su oyente táctil al objeto zoomimageview:

    ZoomImageView image = layout.findViewById(R.id.imageViewItemImageSlider);
    image.setOnTouchListener(touchListener);

y aquí está la clase MyGestureListener:

    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {

//        <strong i="18">@Override</strong>
//        public boolean onDown(MotionEvent event) {
//            Log.d("TAG","onDown: ");
//
//            // don't return false here or else none of the other
//            // gestures will work
//            return true;
//        }
//
//        <strong i="19">@Override</strong>
//        public boolean onSingleTapConfirmed(MotionEvent e) {
//            Log.i("TAG", "onSingleTapConfirmed: ");
//            return true;
//        }
//
//        <strong i="20">@Override</strong>
//        public void onLongPress(MotionEvent e) {
//            Log.i("TAG", "onLongPress: ");
//        }

        <strong i="21">@Override</strong>
        public boolean onDoubleTap(MotionEvent e) {
            Log.i("TAG", "onDoubleTap: ");
            if((selectedImage.getEngine().getZoom() >= 2.75F)) {
                selectedImage.getEngine().zoomTo(1F, true);
            } else if((selectedImage.getEngine().getZoom() < 1F)) {
                selectedImage.getEngine().zoomTo(1F, true);
            }  else {
                selectedImage.getEngine().zoomBy(2F, true);
            }

            return true;
        }

//        <strong i="22">@Override</strong>
//        public boolean onScroll(MotionEvent e1, MotionEvent e2,
//                                float distanceX, float distanceY) {
//            Log.i("TAG", "onScroll: ");
//            return true;
//        }

//        <strong i="23">@Override</strong>
//        public boolean onFling(MotionEvent event1, MotionEvent event2,
//                               float velocityX, float velocityY) {
//            Log.d("TAG", "onFling: ");
//            return true;
//        }
    }

Anule onScroll (DEBE) y otro método (opcional en mi caso) con el que no tenga que lidiar, ya que entrará en conflicto con el evento táctil de ZoomImageView y creará un problema.

Espero que esto te ayudará.

Actualmente estoy reaccionando al doble toque para centrar la pantalla donde se realizó el evento.

Simplemente agregando esto:

    private val simpleGestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {


        //here is the method for double tap


        override fun onDoubleTap(e: MotionEvent): Boolean {

            Log.d("OnDoubleTapListener", "onDoubleTap")
            centerElement(e.x.absoluteValue,e.y.absoluteValue)
            return true
        }

    })

    fun centerElement(clickedX: Float, clickedY: Float) {
        val offsetX = (width.absoluteValue) / 2
        val offsetY = (height.absoluteValue) / 2
        val displacedX = engine.panX.absoluteValue
        val displacedY = engine.panY.absoluteValue
        val x = (displacedX + clickedX / engine.realZoom) - (offsetX / engine.realZoom)
        val y = (displacedY + clickedY / engine.realZoom) - (offsetY / engine.realZoom)
        val desiredX = if (x > 0) -x else 0f
        val desiredY = if (y > 0) -y else 0f
        engine.moveTo(engine.zoom, desiredX, desiredY, true)
    }

Ahora estoy tratando de moverme y hacer zoom correctamente. Puedo abrir una solicitud de extracción si quieres que funcione.

@AlvaroFalcon eso sería genial! Puedo darte algunos consejos.

  • Ya tenemos un detector de gestos en ZoomEngine que reacciona para desplazarse y lanzar
  • Creo que puedes reutilizar el 99% de la lógica dentro onScale() . Funciona con coordenadas del centro del gesto de escala y aplica un factor de zoom. Tendría que asignar un factor de zoom usted mismo (creo que zoomIn() usa 1.3) pero es más o menos la misma tarea
  • Esto debería ser configurable a través de un atributo XML ( doubleTapBehavior ?) que debería tener al menos dos opciones ("ninguna" y "zoom"). Usemos none como predeterminado para que no cambiemos esto para quién ya está usando lib.

@natario1 Genial! Lo haré cuando tenga un rato...

Actualmente estoy trabajando en mi proyecto con una clase que amplía su diseño de zoom, así que agrego mi lógica allí.
Estoy tratando de hacer zoom con movimiento al mismo tiempo usando eso, pero no tuve suerte por el momento.

¡Gracias por los consejos!

¿Se podría agregar una acción en el método moveTo()? Para ser llamado después de que termine, creo que sería más flexible de esa manera para que pueda hacer varias cosas.

Bueno, para moveTo, zoomBy, zoomTo, etc...

Podría ser como:

moveTo(zoom, x, y, action : ()->Unit={})

@AlvaroFalcon En Kotlin, haría eso, pero esto sigue siendo Java puro. Sin embargo, nos mudaremos a Kotlin en el futuro. Por ahora, hay onIdle() que cualquiera puede acceder.

@AlvaroFalcon , ¿pudiste hacer zoom y pan al mismo tiempo? ¿Te importaría compartir tu código?

He implementado doble toque y pellizcar para hacer zoom. La clave para la solución de trabajo es adjuntar el detector de gestos de toque/toque doble a la vista de contenido dentro del diseño de zoom y configurar el diseño de zoom para que los elementos secundarios en los que se pueda hacer clic sean verdaderos. Luego, en onTouchEvent de la vista de contenido, devuelva true para consumir eventos táctiles y pasarlos al detector de gestos. De esa manera, tanto pellizcar, hacer zoom y tocar dos veces funcionan al mismo tiempo. Entonces, hacer zoom con un doble toque no es un problema.

Sin embargo, también intenté hacer zoom en el área que se tocó dos veces y, dada la implementación ZoomEngine.moveTo , la interpolación simultánea de zoom y pan no se comporta correctamente y la imagen se desliza por diferentes caminos para llegar al zoom y panorámica de destino. No se ve agradable a la vista, pero no puedo entender lo que está sucediendo. Creo que el algoritmo de interpolación debería calcular de manera diferente la panorámica real en función del zoom real, no solo una fracción de la animación.

hacer zoom en el área que se tocó dos veces

Sí, eso es exactamente lo que he estado tratando de hacer. ¿Alguna sugerencia de cómo hacer esto @natario1?

Hola @mman

También hemos implementado una forma de hacer zoom automáticamente cuando el usuario toca la pantalla y la animación para hacer zoom gira hacia la posición final.

Sería muy bueno si pudiéramos controlar esta animación. ¿Usted u otros tienen sugerencias sobre cómo hacerlo?

Gracias por adelantado :)

Me las arreglé para hacerlo con un truco sucio. Estoy engañando al motor de zoom para que piense que se está produciendo un pellizco.

class ReflectionHelper {
    <strong i="6">@Nullable</strong>
    static ScaleGestureDetector.OnScaleGestureListener getScaleDetectorListenerUsingReflection(ZoomEngine zoomEngine) {
        try {
            Field mScaleDetector = zoomEngine.getClass().getDeclaredField("mScaleDetector");
            mScaleDetector.setAccessible(true);
            ScaleGestureDetector detector = (ScaleGestureDetector) mScaleDetector.get(zoomEngine);
            Field mListener = detector.getClass().getDeclaredField("mListener");
            mListener.setAccessible(true);
            return (ScaleGestureDetector.OnScaleGestureListener) mListener.get(detector);
        } catch (Exception e) {
            return null;
        }
    }
}

private fun simulatePinch(fromScale: Float, toScale: Float, focusX: Float, focusY: Float) {
    class MockDetector(var mockScaleFactor: Float, var mockFocusX: Float, var mockFocusY: Float) : ScaleGestureDetector(context, null) {
        override fun getScaleFactor() = mockScaleFactor
        override fun getFocusX() = mockFocusX
        override fun getFocusY() = mockFocusY
    }

    val mockDetector = MockDetector(fromScale, focusX, focusY)
    ValueAnimator.ofFloat(fromScale, toScale).apply {
        addUpdateListener { animation ->
            mockDetector.mockScaleFactor = animation.animatedValue as Float
            zoomEngineScaleListener?.onScale(mockDetector)
        }
        doOnEnd { zoomEngineScaleListener?.onScaleEnd(mockDetector) }
        start()
    }
}

Hola,

¡Gracias por el esfuerzo! :)

Eso definitivamente parece una forma un poco complicada de lograrlo. Sin embargo, creo que elegiremos vivir con la animación muy animada por ahora.

Gracias,
Nikolaj

Hola,

¿Alguna noticia sobre cómo lograr zoom + panorámica con un doble toque?

Gracias,
Cristóbal

¿Fue útil esta página
0 / 5 - 0 calificaciones