Zoomlayout: Toque duas vezes para ampliar

Criado em 29 set. 2017  ·  13Comentários  ·  Fonte: natario1/ZoomLayout

É comum reagir a toques duplos para controlar o zoom. Este deve ser um recurso opcional em ZoomEngine , ativado por padrão em ZoomImageView .

enhancement long term

Comentários muito úteis

Oi,

Alguma notícia sobre como obter zoom + panorâmica com um toque duplo?

Obrigado,
Christophe

Todos 13 comentários

@natario1

Por enquanto, acabei de implementar essa funcionalidade por mim mesmo com a classe Gesture Listener personalizada. Aqui está o meu trecho de código, se alguém precisar.

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

então inicialize essas variáveis:

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

            }
        };

em seguida, atribua seu touchlistener ao objeto zoomimageview:

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

e aqui está a classe 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;
//        }
    }

Faça em override onScroll (MUST) e outro método (opcional no meu caso) com o qual você não tem nada para lidar, pois entrará em conflito com o evento de toque do ZoomImageView e criará um problema.

Espero que isso irá ajudá-lo.

No momento, estou reagindo ao toque duplo para centralizar a tela onde o evento foi feito.

Simplesmente adicionando isto:

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

Agora estou tentando mover e ampliar corretamente. Posso abrir um pull request se você quiser fazer funcionar.

@AlvaroFalcon isso seria legal! Posso te dar algumas dicas.

  • Já temos um detector de gestos em ZoomEngine que reage ao rolar e arremessar
  • Acho que você pode reutilizar 99% da lógica dentro de onScale() . Trabalha com as coordenadas do centro do gesto de escala e aplica um fator de zoom. Você teria que atribuir um fator de zoom você mesmo (acho que zoomIn() usa 1,3), mas é praticamente a mesma tarefa
  • Isso deve ser configurável através de um atributo XML ( doubleTapBehavior ?) que deve ter pelo menos duas opções ("none" e "zoom"). Vamos usar none como padrão para não alterar isso para quem já está usando a lib.

@natario1 Legal! vou fazer quando tiver um tempinho...

Atualmente estou trabalhando em meu projeto com uma classe que estende seu zoomlayout então adiciono minha lógica lá.
Estou tentando fazer zoom com movimento ao mesmo tempo usando isso, mas sem sorte no momento.

Obrigado pelas dicas!

Uma ação poderia ser adicionada no método moveTo()? Para ser chamado depois que terminar, acho que seria mais flexível dessa forma para que você possa fazer várias coisas.

Bem, para moverPara, zoomBy, zoomTo, etc...

Poderia ser como:

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

@AlvaroFalcon Em Kotlin eu faria isso, mas isso ainda é Java puro. No entanto, mudaremos para Kotlin no futuro. Por enquanto, há onIdle() que qualquer um pode acessar.

@AlvaroFalcon você conseguiu fazer zoom e pan ao mesmo tempo? se importa em compartilhar seu código?

Eu implementei o toque duplo e o pinçamento para ampliar. A chave para a solução de trabalho é anexar o detector de gestos de toque/toque duplo à visualização de conteúdo dentro do layout de zoom e configurar o layout de zoom para ter filhos clicáveis ​​como true. Em seguida, no onTouchEvent da visualização de conteúdo, retorne true para consumir eventos de toque e transmita-os ao detector de gestos. Dessa forma, o pinçamento, o zoom e o toque duplo funcionam ao mesmo tempo. Aumentar o zoom no toque duplo não é problema.

No entanto, também tentei aplicar zoom na área que foi tocada duas vezes e, considerando como ZoomEngine.moveTo é implementado, a interpolação simultânea de zoom e pan não se comporta corretamente e a imagem está deslizando por diferentes caminhos para alcançar o zoom e panorâmica do destino. Não parece visualmente agradável, mas não consigo descobrir o que está acontecendo. Eu acho que o algoritmo de interpolação deve calcular de maneira diferente a panorâmica real com base no zoom real, não apenas na fração da animação.

zoom na área que foi tocada duas vezes

Sim, é exatamente isso que tenho tentado fazer. Alguma sugestão de como fazer isso @natario1?

Oi @mman

Também implementamos uma maneira de aumentar o zoom automaticamente quando o usuário toca na tela e a animação para aumentar o zoom em uma espécie de redemoinho em direção à posição final.

Seria muito bom, se pudéssemos controlar essa animação - você ou outros têm sugestões de como fazer isso?

Desde já, obrigado :)

Eu consegui fazer isso com um hack sujo. Estou enganando o mecanismo de zoom para pensar que uma pitada está ocorrendo.

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()
    }
}

Oi,

Obrigado pelo esforço! :)

Isso definitivamente parece uma maneira um pouco hacky de alcançá-lo. Acho que, no entanto, escolheremos viver com a animação muito animada por enquanto.

Obrigado,
Nikolaj

Oi,

Alguma notícia sobre como obter zoom + panorâmica com um toque duplo?

Obrigado,
Christophe

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

aouledissa picture aouledissa  ·  10Comentários

YiHaoHuang picture YiHaoHuang  ·  10Comentários

Yahor10 picture Yahor10  ·  5Comentários

kuoliangkwong picture kuoliangkwong  ·  4Comentários

wakaztahir picture wakaztahir  ·  5Comentários