Не может одновременно обрабатывать события нажатия и нажатия - PullRequest
42 голосов
/ 01 марта 2011

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

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

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

Ответы [ 10 ]

67 голосов
/ 30 октября 2013

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

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

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

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

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

Это будет работать.

39 голосов
/ 01 марта 2011

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

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

16 голосов
/ 05 июня 2015

Спасибо @urSus за отличный ответ
Но в этом случае каждое касание будет выполнять щелчок, даже ACTION_MOVE
Предполагая, что вы хотите разделить событие move и событие click, вы можете использовать небольшую хитрость
определите поле boolean и используйте его следующим образом:

 @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;
        }
5 голосов
/ 20 апреля 2011

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

5 голосов
/ 01 марта 2011

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

На заметку о том, какой смысл иметь прослушиватель как щелчка, так и касания?

1 голос
/ 17 августа 2018

Весь ответ выше говорит, что мы не можем обрабатывать как 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
0 голосов
/ 09 мая 2019

Это пример 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()
}
0 голосов
/ 14 февраля 2018
## 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;
}
0 голосов
/ 10 октября 2017

Чтобы сделать оба события возможными в 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;
    }
});**

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

0 голосов
/ 25 июля 2016
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;
}
...