No puede manejar eventos de clic y toque simultáneamente

Estoy tratando de manejar eventos táctiles y hacer clic en eventos en un botón. Yo hago lo siguiente:

button.setOnClickListener(clickListener); button.setOnTouchListener(touchListener); 

Cuando se registra a cualquier oyente, las cosas funcionan bien, pero cuando bash usarlas, solo se activan los eventos táctiles. Cualquier solución? ¿Qué estoy haciendo mal?

Hay una diferencia sutil pero muy importante entre ClickListener y TouchListener . TouchListener se ejecuta antes de que la vista pueda responder al evento. ClickListener recibirá su evento solo después de que la vista lo haya manejado.

Por lo tanto, cuando tocas la pantalla, TouchListener se ejecuta primero y cuando vuelves true para tu evento, ClickListener nunca lo obtendrá. Pero si presiona la bola de seguimiento de su dispositivo, el ClickListener debería ClickListener porque el TouchListener no responderá.

Es un poco complicado.

Si configuras en onTouchListener necesitas devolver true en ACTION_DOWN , para decirle al sistema que he consumido el evento y que no se filtrará a otros oyentes.

Pero entonces OnClickListener no se disparará.

Entonces, podría pensar, haré lo mío y devolveré false para poder recibir clics también. Si lo haces, funcionará, pero no estarás suscrito a otros próximos eventos táctiles ( ACTION_MOVE , ACTION_UP ) Por lo tanto, la única opción es devolver true allí, pero luego no recibirás ningún evento de clic como dijimos previamente.

Por lo tanto, debe realizar el clic manualmente en ACTION_UP con view.performClick()

Esto funcionará

Gracias a @urSus por una excelente respuesta
Pero en ese caso, cada toque realizará clic, incluso ACTION_MOVE
Asumiendo que quiera separar evento de move y click evento, puede usar un pequeño truco
define un campo boolean y usa así:

  @Override public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: shouldClick = true; . . break; case MotionEvent.ACTION_UP: if (shouldClick) view.performClick(); break; case MotionEvent.ACTION_POINTER_DOWN: break; case MotionEvent.ACTION_POINTER_UP: break; case MotionEvent.ACTION_MOVE: //Do your stuff shouldClick = false; break; } rootLayout.invalidate(); return true; } 

¿Supongo que estás volviendo true en tu OnTouchListener ? Eso consumirá el evento, por lo que no se enviará para ningún procesamiento posterior.

En una nota al margen: ¿de qué sirve tener un solo clic y un oyente?

Debería devolver false en su OnTouchListener luego su OnClickListener también será manejado.

 button.setOnTouchListener(this); 

Implementar la interfaz y el código aquí:

 @Override public boolean onTouch(View view, MotionEvent motionEvent) { switch (view.getId()) { case R.id.send: switch(motionEvent.getAction()){ case MotionEvent.ACTION_DOWN: //when the user has pressed the button //do the needful here break; case MotionEvent.ACTION_UP: //when the user releases the button //do the needful here break; } break; } return false; } 

Para hacer que ambos eventos sean posibles en la vista de cuadrícula, solo haciendo que el retorno del oyente táctil sea “falso” de la siguiente manera, esto funcionó para mí.

 **GridView myView = findViewById(R.id.grid_view); myView.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { // ... Respond to touch events return false; } });** 

de esta manera, se pueden lograr ambos eventos

 ## Exact working solution for both click action and touch listener(dragging) ## private int initialX; private int initialY; private float initialTouchX; private float initialTouchY; private float CLICK_ACTION_THRESHOLD = 0.5f; private float startX; private float startY; @Override public boolean onTouch(View view, MotionEvent event) { switch (view.getId()) { case R.id.chat_head_profile_iv: switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //remember the initial position. initialX = params.x; initialY = params.y; startX = event.getX(); startY = event.getY(); //get the touch location initialTouchX = event.getRawX(); initialTouchY = event.getRawY(); return true; case MotionEvent.ACTION_UP: float endX = event.getX(); float endY = event.getY(); if (shouldClickActionWork(startX, endX, startY, endY)) { openScreen();// WE HAVE A CLICK!! } return true; case MotionEvent.ACTION_MOVE: //Calculate the X and Y coordinates of the view. params.x = initialX + (int) (event.getRawX() - initialTouchX); params.y = initialY + (int) (event.getRawY() - initialTouchY); //Update the layout with new X & Y coordinate mWindowManager.updateViewLayout(mChatHeadView, params); return true; } break; } return true; } private boolean shouldClickActionWork(float startX, float endX, float startY, float endY) { float differenceX = Math.abs(startX - endX); float differenceY = Math.abs(startY - endY); if ((CLICK_ACTION_THRESHOLD > differenceX) && (CLICK_ACTION_THRESHOLD > differenceY)) return true; else return false; } 

Toda la respuesta anterior se dice que no podemos manejar tanto setOnTouchListener como setOnClickListener .
Sin embargo, veo que podemos manejar ambos por return false en setOnTouchListener

Ejemplo

 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) button = findViewById(R.id.button) button.setOnClickListener { Log.i("TAG", "onClick") } button.setOnTouchListener { v, event -> Log.i("TAG", "onTouch " + event.action) false } } 

Cuando hago clic en el Button , Logcat se mostrará como

 I/TAG: onTouch 0 I/TAG: onTouch 1 I/TAG: onClick