Подтвердить что ты не робот

Невозможно одновременно обрабатывать события click и touch

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

Немного сложно.

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

Но тогда OnClickListener не будет запущен.

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

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

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

Ответ 3

Спасибо @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;
        }

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

Ответ 7

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

Ответ 8

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

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

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

Ответ 9

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

Ответ 10

Это пример 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 */

            previousPoint = it
            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()
}