web-dev-qa-db-ja.com

どうすればonTouchリスナーのクリックを検出できますか?

ViewPagerの中にScrollViewがあります。垂直方向だけでなく水平方向にもスクロールできる必要があります。これを達成するために、私のViewPagerがタッチされるたびに垂直スクロールを無効にする必要があり(v.getParent().requestDisallowInterceptTouchEvent(true);)、水平にスクロールできるようにしました。

しかし同時に、viewPagerをクリックして全画面モードで開くことができるようにする必要があります。

問題は、onTouchがonClickの前に呼び出され、OnClickが呼び出されないことです。

onClickとonClickの両方を実装するにはどうすればよいですか?

viewPager.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        System.out.println("TOUCHED ");
        if(event.getAction() == MotionEvent.???){
            //open fullscreen activity
        }
        v.getParent().requestDisallowInterceptTouchEvent(true); //This cannot be removed
        return false;
    }
});

viewPager.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        System.out.println("CLICKED ");
        Intent fullPhotoIntent = new Intent(context, FullPhotoActivity.class);
        fullPhotoIntent.putStringArrayListExtra("imageUrls", imageUrls);
        startActivity(fullPhotoIntent);
    }
});
26

マソウダダシの答えは私がそれを理解するのに役立ちました。

最終的には次のようになります。

viewPager.setOnTouchListener(new OnTouchListener() {
    private int CLICK_ACTION_THRESHOLD = 200;
    private float startX;
    private float startY;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            startX = event.getX();
            startY = event.getY();
            break;
        case MotionEvent.ACTION_UP: 
            float endX = event.getX();
            float endY = event.getY();
            if (isAClick(startX, endX, startY, endY)) { 
                launchFullPhotoActivity(imageUrls);// WE HAVE A CLICK!!
            }
            break;
        }
        v.getParent().requestDisallowInterceptTouchEvent(true); //specific to my project
        return false; //specific to my project
    }

    private boolean isAClick(float startX, float endX, float startY, float endY) {
        float differenceX = Math.abs(startX - endX);
        float differenceY = Math.abs(startY - endY);
        return !(differenceX > CLICK_ACTION_THRESHOLD/* =5 */ || differenceY > CLICK_ACTION_THRESHOLD);
    } 
}
59

ユーザーが画面に触れた時間を記録することで、本当に簡単なことをしました。

private long lastTouchDown;
private static int CLICK_ACTION_THRESHHOLD = 200;

@Override
public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            lastTouchDown = System.currentTimeMillis();
            break;
        case MotionEvent.ACTION_UP:
            if (System.currentTimeMillis() - lastTouchDown < CLICK_ACTION_THRESHHOLD) {
                Log.w("App", "You clicked!");
            }
            break;
    }
    return true;
}

GestureDetector にはこれが組み込まれていることにも注意してください。onSingleTapUpを見てください

24
nilsi

両方を開発するのは間違った考えです。ユーザーが画面に触れることでさまざまなことを行う可能性がある場合、ユーザーの目的を理解することは少し気の利いたものであり、そのためのコードを開発する必要があります。

2つのソリューション:

1-(より良いアイデア)onTouchイベントチェックでモーションがあるかどうかを確認します。あなたは何かを使用して動きがあるかどうかをチェックすることでそれを行うことができます:

ACTION_UP
ACTION_DOWN
ACTION_MOVE

このようにしてください

if(event.getAction() != MotionEvent.ACTION_MOVE)

画面上のユーザーの指の動きの距離をチェックして、クリック中に誤って動くのではなく、動きが起こったことを確認することもできます。このようにしてください:

switch(event.getAction())
 {
     case MotionEvent.ACTION_DOWN:
              if(isDown == false)
              {
                     startX = event.getX();
                     startY = event.getY();
                     isDown = true;
              }
              Break;
        case MotionEvent.ACTION_UP
              {
                     endX = event.getX();
                     endY = event.getY();
                     break;
          }
}

上記のいずれも起こらなかった場合はクリックとみなし、クリックでやりたいことを行います。

2)UIと縁がある場合は、ボタンや画像ボタンなどを作成してフルスクリーニングを行い、onClickを設定します。

幸運を

10
Masoud Dadashi

ユーザーのクリックとロングクリックを区別する必要がある場合があります。そうしないと、両方が同じものとして検出されます。私はそれを可能にするためにこれをしました:

@Override
public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
        startX = event.getX();
        startY = event.getY();

        bClick = true;
        tmrClick = new Timer();
        tmrClick.schedule(new TimerTask() {
            public void run() {
                if (bClick == true) {
                    bClick = false;
                    Log.d(LOG_TAG, "Hey, a long press event!");
                    //Handle the longpress event.
                }
            }
        }, 500); //500ms is the standard longpress response time. Adjust as you see fit.

        return true;
    case MotionEvent.ACTION_UP:
        endX = event.getX();
        endY = event.getY();

        diffX = Math.abs(startX - endX);
        diffY = Math.abs(startY - endY);

        if (diffX <= 5 && diffY <= 5 && bClick == true) {
            Log.d(LOG_TAG, "A click event!");
            bClick = false;
        }
        return true;
    default:
        return false;
    }
}
5
Adam Komar

上記の答えは主に時間を覚えています。ただし、MotionEventはすでにカバーしています。ここでは、オーバーヘッドの少ないソリューションです。 kotlinで記述されていますが、それでも理解できるはずです。

private const val ClickThreshold = 100

override fun onTouch(v: View, event: MotionEvent): Boolean {
    if(event.action == MotionEvent.ACTION_UP 
      && event.eventTime - event.downTime < ClickThreshold) {
        v.performClick()
        return true // If you don't want to do any more actions
    }

    // do something in case its not a click

    return true // or false, whatever you need here
}

このソリューションは、ほとんどのアプリケーションには十分ですが、クリックと非常に速いスワイプを区別するのにはあまり適していません。

したがって、これを上記の回答と組み合わせて、ポジションも考慮に入れることがおそらく最良の方法です。

4
Smogen

ソリューションの時間と位置を組み合わせた方が良いと思います:

 private float initialTouchX;
 private float initialTouchY;
 private long lastTouchDown;
 private static int CLICK_ACTION_THRESHHOLD = 100;
 @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:

                        lastTouchDown = System.currentTimeMillis();

                        //get the touch location
                        initialTouchX = event.getRawX();
                        initialTouchY = event.getRawY();
                        return true;
                    case MotionEvent.ACTION_UP:
                        int Xdiff = (int) (event.getRawX() - initialTouchX);
                        int Ydiff = (int) (event.getRawY() - initialTouchY);

                        if (System.currentTimeMillis() - lastTouchDown < CLICK_ACTION_THRESHHOLD &&  (Xdiff < 10 && Ydiff < 10))  {
                           //clicked!!!
                        }
                        return true;
                }
                return false;
            }
        });
2
Mihuilk

それを行うエレガントな方法

public class CustomView extends View {

    private GestureDetectorCompat mDetector;

    public CustomView(Context context) {
        super(context);
        mDetector = new GestureDetectorCompat(context, new MyGestureListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        return this.mDetector.onTouchEvent(event);
    }

    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onDown(MotionEvent e) {return true;}

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            //...... click detected !
            return false;
        }
    }
}
1
ucMedia

MotionEvent.ACTION_UPは、センサーの最初の時間をタッチすることを意味します

MotionEvent.ACTION_DOWNは、タッチセンサーがもう検出しないことを意味します

たとえば、MotionEvent.ACTION_MOVEは、センサーが移動(スワイプ、スクロールなど)を検出することを意味します

したがって、リスナーの以前のイベントタイプの状態を保存し、最新のイベントと比較して、ユーザーが何をしたかを理解する必要があります。

すべてのビューはその親をカバーします。したがって、ビューにTouchリスナーがある場合、インターセプターのように機能します。 boolean onTouch(View v、MotionEvent event)メソッドからfalseを返す親ビューへのタッチ呼び出しのチェーンを継続することが可能です。

追加:View.performClick()は、OnClickListenerがある場合にのみtrueを返します

yourView.setOnTouchListener(new View.OnTouchListener() {
                int lastEvent;
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if (event.getAction() == MotionEvent.ACTION_UP && lastEvent == MotionEvent.ACTION_DOWN) {
                        return ~ /**Click detected. Do something and return result**/
                    }
                    lastEvent = event.getAction();
                    return v.performClick();
                }
            });
0
Oleg Bozhko

TouchListenerがインターセプトするため、ビューがこのようにタッチイベントを受け取るのを妨げていると思います。あなたはどちらか

  • v.onTouchEvent(event)を呼び出して、ToucheListener内でイベントをディスパッチします
  • 代わりにViewPager.onTouchEvent(MotionEvent)をオーバーライドして、イベントをインターセプトしない

また、trueを返すと、イベントを消費しなかったため、次のイベントに邪魔されず、ジェスチャーが完了するまで(つまり、指が離されるまで)次のイベントを受け取りません。再び)。

0
Antoine Marques

onTouchClickListenerを使用できます

https://github.com/hoffergg/BaseLibrary/blob/master/src/main/Java/com/dailycation/base/view/listener/OnTouchClickListener.Java

使用法:

view.setOnTouchListener(new OnTouchClickListener(new OnTouchClickListener.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //perform onClick
                }
            },5));
0
hoffer