OnClickListener - localização x, y do evento?

85

Eu tenho uma visão customizada derivada de View. Gostaria de ser notificado quando a visualização for clicada e o local x, y de onde o clique aconteceu. O mesmo para cliques longos.

Parece que vou fazer isso, preciso substituir onTouchEvent(). Mas não há como obter a localização x, y do evento de um OnClickListener?

Se não, qual é uma boa maneira de saber se um evento de movimento é um clique 'real' versus um clique longo etc? O onTouchEventgera muitos eventos em rápida sucessão etc.

Marca
fonte

Respostas:

92

Obrigado. Isso era exatamente o que eu estava procurando. Meu código agora é:

imageView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN){
                textView.setText("Touch coordinates : " +
                        String.valueOf(event.getX()) + "x" + String.valueOf(event.getY()));
            }
            return true;
        }
    });

que faz exatamente o que Marcos pediu ...

Vering
fonte
4
Essas coordenadas são locais para a vista?
Nigel,
7
O manipulador onTouch não deveria retornar falsepara não bloquear eventos de toque? (View.OnTouchListener.onTouch retorna verdadeiro se o ouvinte consumiu o evento, caso contrário, falso.)
ehartwell
5
Bem, não "precisamente" porque OnClickListeneré disparado quando o dedo é liberado, portanto, ACTION_UPmas com limitações: se o gesto de atirar (deslizar) foi executado, OnClickListenernão é disparado, mas ACTION_UPainda será processado.
Alex Semeniuk
Estou tentando escrever um texto nas coordenadas clicadas. Tentei escrever o texto nas coordenadas, (event.getX(), event.getY()) mas não está escrevendo na posição correta.
Prince
Aqui está minha pergunta: stackoverflow.com/questions/40199539/…
Príncipe
26

Exemplo completo

As outras respostas estão faltando alguns detalhes. Aqui está um exemplo completo.

public class MainActivity extends AppCompatActivity {

    // class member variable to save the X,Y coordinates
    private float[] lastTouchDownXY = new float[2];

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // add both a touch listener and a click listener
        View myView = findViewById(R.id.my_view);
        myView.setOnTouchListener(touchListener);
        myView.setOnClickListener(clickListener);
    }

    // the purpose of the touch listener is just to store the touch X,Y coordinates
    View.OnTouchListener touchListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {

            // save the X,Y coordinates
            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
                lastTouchDownXY[0] = event.getX();
                lastTouchDownXY[1] = event.getY();
            }

            // let the touch event pass on to whoever needs it
            return false;
        }
    };

    View.OnClickListener clickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // retrieve the stored coordinates
            float x = lastTouchDownXY[0];
            float y = lastTouchDownXY[1];

            // use the coordinates for whatever
            Log.i("TAG", "onLongClick: x = " + x + ", y = " + y);
        }
    };
}

Resumo

  • Adicione uma variável de classe para armazenar as coordenadas
  • Salve as coordenadas X, Y usando um OnTouchListener
  • Acesse as coordenadas X, Y no OnClickListener
Suragch
fonte
1
Em vez de um Array flutuante de 2 espaços, um Point () poderia ser usado.
ZooMagic de
O ponto possui X e Y do tipo inteiro. As coordenadas são valores flutuantes. Se eu usar a classe Point, acho que vou perder a precisão ... Sou muito novo no Android e posso ter perdido algo.
mboada
@mboada, você está certo. PointF seria melhor.
Suragch
Deve haver uma maneira de evitar onTouchListener e usar apenas onClickListener. De alguma forma, ContextMenu não precisa disso. Ao clicar em um item e mostrar o menu com a visualização como âncora, ele sabe onde colocá-lo, próximo ao local de toque. Veja aqui: stackoverflow.com/q/57290574/878126 . Você sabe como isso é possível? Em caso afirmativo, considere responder lá.
desenvolvedor Android de
3

Substituir onTouchEvent (MotionEvent ev)

Então você pode fazer:

ev.getXLocation()

Ou algo assim. Tenha um butches.

Ao Sr
fonte
2
Olá, Tom, sim, podemos usar onTouchEvent, mas precisamos lidar com o discernimento entre cliques e cliques longos, não é? Não faz sentido que eles não forneçam a você uma maneira de saber onde o clique ou o clique longo ocorreu no OnClickListener etc? Obrigado
Marcos
21
Tudo o que você precisa fazer é registrar a posição x e y do último evento ACTION_UP ou ACTION_MOVE e usar esses valores em seu OnClickListener.
Romain Guy
@RomainGuy Mas você também deve calcular a distância entre os dois pontos para determinar um clique. Se você receber o evento ACTION_DOWN em (0,0) e o evento ACTION_UP em (100,100), isso definitivamente não é um clique.
TheRealChx101
@ chx101, o que RomainGuy quis dizer (eu acho) é que se você apenas no touchevent registrar as últimas coordenadas de action_up, então essas são as coordenadas que você pode usar em onclick. no entanto, acho que seria necessário criar uma subclasse da view na prática, ou o onclick não será chamado. você também pode detectar o toque como um detector de gestos em stackoverflow.com/questions/19538747/… answer
Lassi Kinnunen
0

Você pode fazer uma função de extensão em Kotlin, assim:

fun View.setOnClickListenerWithPoint(action: (Point) -> Unit) {
    val coordinates = Point()
    val screenPosition = IntArray(2)
    setOnTouchListener { v, event ->
        if (event.action == MotionEvent.ACTION_DOWN) {
            v.getLocationOnScreen(screenPosition)
            coordinates.set(event.x.toInt() + screenPosition[0], event.y.toInt() + screenPosition[1])
        }
        false
    }
    setOnClickListener {
        action.invoke(coordinates)
    }
}

Se você precisar de um clique longo - basta substituir setOnClickListener

Eu uso getLocationOnScreen()porque preciso de coordenadas para ViewHolder do RecyclerView

Vitalii Malyi
fonte