Impossible de gérer les événements de clic et de toucher simultanément

J’essaie de gérer les événements tactiles et de cliquer sur les événements sur un bouton. Je fais ce qui suit:

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

Lorsqu’un auditeur est enregistré, tout fonctionne correctement, mais lorsque je tente de les utiliser, seuls les événements tactiles sont déclenchés. Une solution de contournement? Qu’est-ce que je fais mal?

Il existe une différence subtile mais très importante entre ClickListener et TouchListener . Le TouchListener est exécuté avant que la vue puisse répondre à l’événement. ClickListener ne recevra son événement qu’une fois la vue traitée.

Ainsi, lorsque vous touchez votre écran, le TouchListener est exécuté en premier et lorsque vous revenez à true pour votre événement, ClickListener ne l’obtiendra jamais. Mais si vous appuyez sur la boule de commande de votre appareil, le ClickListener doit être déclenché car le TouchListener ne répondra pas.

C’est un peu difficile.

Si vous définissez onTouchListener vous devez retourner true dans ACTION_DOWN , pour indiquer au système que j’ai consommé l’événement et qu’il ne sera pas transmis aux autres écouteurs.

Mais alors OnClickListener ne sera pas renvoyé.

Donc, vous pourriez penser que je vais juste faire mon truc là-bas et retourner false pour que je puisse recevoir des clics aussi. Si vous le faites, cela fonctionnera, mais vous ne serez pas abonné aux autres événements tactiles à venir ( ACTION_MOVE , ACTION_UP ). Par conséquent, la seule option est d’y retourner true , mais vous ne recevrez aucun événement de clic comme nous l’avons dit précédemment.

Vous devez donc effectuer le clic manuellement dans ACTION_UP avec view.performClick()

Cela fonctionnera.

Merci à @urSus pour une excellente réponse
Mais dans ce cas, chaque touche effectuera un clic, même ACTION_MOVE
En supposant que vous voulez séparer move événement de move et click événement, vous pouvez utiliser un petit tour
définir un champ boolean et utiliser comme ceci:

  @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; } 

Je suppose que vous retournez true dans votre OnTouchListener ? Cela consumra l’événement afin qu’il ne soit pas envoyé pour un traitement ultérieur.

Sur une note de côté – quel est l’intérêt d’avoir à la fois un auditeur de clic et de toucher?

Vous devriez retourner false dans votre OnTouchListener puis votre OnClickListener sera également traité.

 button.setOnTouchListener(this); 

Implémenter l’interface et le code ici:

 @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; } 

Pour rendre les deux événements possibles en gridview, seulement en rendant le retour de l’auditeur tactile “faux” comme suit, cela a fonctionné pour moi.

 **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 cette façon, les deux événements peuvent être réalisés

 ## 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; } 

Tout ce qui précède répond que nous ne pouvons pas gérer à la fois setOnTouchListener et setOnClickListener .
Cependant, je vois que nous pouvons gérer les deux par return false dans setOnTouchListener

Exemple

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

Lorsque je clique sur Button , logcat affichera comme

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