Невозможно одновременно обрабатывать события щелчка и касания

Я пытаюсь обрабатывать события касания и события нажатия на кнопку. Я делаю следующее:

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

Когда зарегистрирован какой-либо один слушатель, все работает нормально, но когда я пытаюсь использовать их оба, запускаются только события касания. Любое обходное решение? Что я делаю неправильно?


person Ragunath Jawahar    schedule 01.03.2011    source источник


Ответы (13)


Между ClickListener и TouchListener есть тонкое, но очень важное различие. TouchListener выполняется до того, как представление сможет ответить на событие. ClickListener получит свое событие только после того, как его обработает представление.

Поэтому, когда вы касаетесь экрана, сначала выполняется TouchListener, а когда вы возвращаете true для своего события, ClickListener никогда его не получит. Но если вы нажмете на трекбол своего устройства, ClickListener должен сработать, потому что TouchListener на него не отреагирует.

person Chris    schedule 01.03.2011

Это немного сложно.

Если вы установили onTouchListener, вам нужно вернуть true в ACTION_DOWN, чтобы сообщить системе, что я воспользовался событием, и оно не будет передано другим слушателям.

Но тогда OnClickListener не уволят.

Так что вы можете подумать, что я просто сделаю там свое дело и верну false, чтобы тоже получать клики. Если вы это сделаете, это будет работать, но вы не будете подписаны на другие предстоящие события касания (ACTION_MOVE, ACTION_UP). Поэтому единственный вариант — вернуть туда true, но тогда вы не будете получать никаких событий кликов, как мы сказали. ранее.

Поэтому вам нужно выполнить щелчок вручную в ACTION_UP с view.performClick()

Это сработает.

person urSus    schedule 29.10.2013
comment
Ключевая часть этого заключается в том, что вам нужно выполнить щелчок вручную в ACTION_UP, спасибо, сэр. - person Juan Cortés; 26.02.2015
comment
Это действительно лучший ответ. В моем ACTION_MOVE я начинаю перехватывать события касания, если достигается определенный порог deltaX. Если я доберусь до ACTION_UP и этот порог deltaX никогда не был достигнут, я запускаю listView.performClick(), в противном случае я выполняю свои действия смахивания. - person Taylor Kline; 12.06.2015
comment
Очевидно, это должен быть ответ. - person stdout; 19.11.2015
comment
Отличный ответ. Так держать - person VVB; 28.01.2016

Вы должны вернуть false в свой OnTouchListener, тогда ваш OnClickListener также будет обработан.

person Yvan RAJAONARIVONY    schedule 20.04.2011

Я полагаю, вы возвращаете true в свой OnTouchListener? Это приведет к потреблению события, поэтому оно не будет отправлено для дальнейшей обработки.

С другой стороны, какой смысл иметь прослушиватель кликов и касаний?

person EboMike    schedule 01.03.2011
comment
Клиент хочет, чтобы звук воспроизводился при нажатии и отпускании кнопки, я делаю это в событиях «ACTION_UP» и «ACTION_DOWN». И событие щелчка выполняет логику. Теперь мне придется обрабатывать логику самого события касания. - person Ragunath Jawahar; 02.03.2011
comment
Кнопку, которую можно перетащить, а затем нажать, когда закончите? - person milosmns; 02.09.2014

Во всем приведенном выше ответе говорится, что мы не можем обрабатывать как setOnTouchListener, так и setOnClickListener.
Однако я вижу, что мы можем обработать оба с помощью return false в setOnTouchListener

Пример

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

Когда я нажимаю на Button, logcat будет отображаться как

I/TAG: onTouch 0
I/TAG: onTouch 1
I/TAG: onClick
person Linh    schedule 17.08.2018
comment
возвращение false из onTouch равнозначно тому, что вообще не устанавливается onTouchListener. - person Nasib; 12.06.2021

Это пример TouchListener, который не дублирует ClickListener.

import android.graphics.PointF
import android.view.MotionEvent
import android.view.MotionEvent.*
import android.view.View
import kotlin.math.abs

object TouchAndClickListener : View.OnTouchListener {

    /** Those are factors you can change as you prefer */
    private const val touchMoveFactor = 10
    private const val touchTimeFactor = 200


    private var actionDownPoint = PointF(0f, 0f)
    private var previousPoint = PointF(0f, 0f)
    private var touchDownTime = 0L

    override fun onTouch(v: View, event: MotionEvent) = when (event.action) {
        ACTION_DOWN -> PointF(event.x, event.y).let {

            actionDownPoint = it  // Store it to compare position when ACTION_UP
            previousPoint = it  // Store it to compare position when ACTION_MOVE
            touchDownTime = now() // Store it to compare time when ACTION_UP

            /* Do other stuff related to ACTION_DOWN you may whant here */

            true
        }

        ACTION_UP -> PointF(event.x, event.y).let {

            val isTouchDuration = now() - touchDownTime < touchTimeFactor  // short time should mean this is a click
            val isTouchLength = abs(it.x - actionDownPoint.x) + abs(it.y - actionDownPoint.y) < touchMoveFactor  // short length should mean this is a click

            val shouldClick = isTouchLength && isTouchDuration  // Check both

            if (shouldClick) yourView.performClick() //Previously define setOnClickListener{ } on yourView, then performClick() will call it

            /* Do other stuff related to ACTION_UP you may whant here */

            true
        }

        ACTION_MOVE -> PointF(event.x, event.y).let {

            /* Do other stuff related to ACTION_MOVE you may whant here */

            previousPoint = it
            true
        }

        else -> false // Nothing particular with other event
    }

    private fun now() = System.currentTimeMillis()
}
person Nicolas Duponchel    schedule 09.05.2019
comment
Отличный ответ, но, пожалуйста, это должно было быть в Java, а не в Котлине. - person Nasib; 02.04.2020

благодаря @Nicolas Duponchel именно так я достиг событий onClick и onTouch

`private short touchMoveFactor = 10;
    private short touchTimeFactor = 200;
    private PointF actionDownPoint = new PointF(0f, 0f);
    private long touchDownTime = 0L;

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN: {
            Log.d(TAG, "onTouch: _____________ACTION_DOWN");
                    actionDownPoint.x = event.getX();
                    actionDownPoint.y = event.getY();
                    touchDownTime = System.currentTimeMillis();
                    break;
                }
                case MotionEvent.ACTION_UP: {
                    Log.d(TAG, "onTouch: _____________ACTION_UP");
           //check if the user's finger is still close to the point he/she clicked 
                        boolean isTouchLength = (Math.abs(event.getX() - actionDownPoint.x)
                                + Math.abs(event.getY() - actionDownPoint.y)) < touchMoveFactor;
        //check if it's not been more than @touchTimeFactor=200 ms
                        boolean isClickTime = System.currentTimeMillis() - touchDownTime < touchTimeFactor;
                             if (isTouchLength && isClickTime) performClick();
                    break;
                }
    }

}`

person Nasib    schedule 02.04.2020
comment
Хотел бы я голосовать больше, чем один раз! - person Ameer Moaaviah; 10.06.2021

button.setOnTouchListener(this);

Реализовать интерфейс и код здесь:

@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;
}
person Geet Choubey    schedule 25.07.2016

Чтобы сделать оба события возможными в gridview, у меня это сработало, только сделав возврат сенсорного слушателя «ложным» следующим образом.

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

таким образом, оба события могут быть достигнуты

person amyShamna    schedule 10.10.2017

В ACTION_UP выполните onClick вручную, используя условие

boolean shouldClick = event.eventTime - event.downTime <= 200 // compares the time with some threshold

Итак, попробуйте в течение MotionEvent.ACTION_UP

if(event.eventTime - event.downTime <= 200) { // case or when statement of action Touch listener
    view.performClick();
}
person Santanu Sur    schedule 23.03.2020

Я знаю, что уже слишком поздно, но если кто-то борется с этим за чистое решение, вот оно.

Они используются для измерения времени между касанием и удалением пальца.

    private long clickTime = 0;
    public static final long CLICK_TIMEOUT = 200; // 200ms

Это мой onTouchListner. Работает как шарм

    private final View.OnTouchListener onTouchListener = (v, event) -> {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        clickTime = System.currentTimeMillis();
        return true;
    } else if(event.getAction() ==  MotionEvent.ACTION_UP) {
        if(System.currentTimeMillis()-clickTime < Constants.CLICK_TIMEOUT)
        {
            Toast.makeText(getContext(), "clicked", Toast.LENGTH_SHORT).show();
            return true;
        }
        return false;
    }
    else if(event.getAction() == MotionEvent.ACTION_MOVE){
        if(System.currentTimeMillis()-clickTime > Constants.CLICK_TIMEOUT)
        {
            ClipData data = ClipData.newPlainText("" , "");
            View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);
            v.startDrag(data , shadowBuilder , v , 0);
            return false;
        }
        return false;
    }
    return false;
};
person Vikram Baliga    schedule 18.05.2021

person    schedule
comment
Отличный ответ. Так держать - person VVB; 28.01.2016
comment
выполнить щелчок - это метод просмотра? или пользовательский метод, в котором мы должны выполнить щелчок - person Ankita Singh; 22.12.2016
comment
@ user3475052 performClick() - это метод каждого представления и его подклассов. - person Sepehr Behroozi; 23.12.2016
comment
Работает, если return true; в ветке MotionEvent.ACTION_DOWN, удалить ветку MotionEvent.ACTION_MOVE, получить разницу движения в ветке MotionEvent.ACTION_UP. См. stackoverflow.com/questions/17831395/ - person CoolMind; 16.02.2017
comment
на самом деле этот ответ лучший во всех подобных вопросах - person tej shah; 09.09.2018

person    schedule
comment
Что такое параметры? - person Aditya; 15.11.2018