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