web-dev-qa-db-ja.com

クリックイベントとタッチイベントの両方を同時に処理することはできません

タッチイベントとボタンのクリックイベントを処理しようとしています。私は次のことを行います:

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

リスナーが1つ登録されている場合は問題なく動作しますが、リスナーを使用しようとすると、両方のタッチイベントのみが発生します。回避策はありますか?私は何を間違えていますか?

43

ClickListenerTouchListenerには微妙ですが、非常に重要な違いがあります。 TouchListenerは、ビューがイベントに応答する前に実行されます。 ClickListenerは、ビューが処理した後にのみイベントを受け取ります。

したがって、画面に触れると、TouchListenerが最初に実行され、イベントにtrueを返すと、ClickListenerはそれを取得しません。ただし、デバイスのトラックボールを押すと、ClickListenerが応答しないため、TouchListenerが起動されます。

39
Chris

少し注意が必要です。

onTouchListenerを設定した場合、ACTION_DOWN_でtrueを返す必要があります。これは、システムにイベントを消費したことを通知し、他のリスナーにトリクルダウンしません。

ただし、OnClickListenerは実行されません。

だからあなたは思うかもしれません、私はそこで自分のことをして、falseを返すので、クリックも受け取ることができます。そうすれば動作しますが、他の今後のタッチイベント(_ACTION_MOVE_、_ACTION_UP_)にサブスクライブされません。したがって、唯一のオプションはtrueを返すことです。ただし、前述のようにクリックイベントは発生しません。

したがって、view.performClick()を使用して_ACTION_UP_で手動でクリックを実行する必要があります

これは動作します。

67
urSus

すばらしい回答をありがとう@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;
        }
16
Sepehr Behroozi

OnTouchListenerでfalseを返す必要があります。そうすると、OnClickListenerも処理されます。

5

trueOnTouchListenerを返していると思いますか?それはイベントを消費するので、それ以上の処理のために送信されることはありません。

サイドノートで-クリックとタッチの両方のリスナーを持つことのポイントは何ですか?

5
EboMike

上記の答えはすべて、できないsetOnTouchListenersetOnClickListenerの両方を処理すると言われています。
しかし、私はできると思いますhandle both by return false in 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
2
Phan Van Linh
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;
}
0
Geet Choubey
## 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
manas.abrol

これは、ClickListenerをシャドウしないTouchListenerの例です。

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

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

このようにして、両方のイベントを達成できます

0
amyShamna