80

私のアプリケーションでは、移動イベントとクリックイベントの両方を処理する必要があります。

クリックは、1つのACTION_DOWNアクション、複数のACTION_MOVEアクション、および1つのACTION_UPアクションのシーケンスです。理論的には、ACTION_DOWNイベントを取得してからACTION_UPイベントを取得した場合、それはユーザーがビューをクリックしたことを意味します。

ただし、実際には、このシーケンスは一部のデバイスでは機能しません。私のSamsungGalaxyGioでは、ビューをクリックするだけでそのようなシーケンスが表示されます:ACTION_DOWN、ACTION_MOVEを数回、次にACTION_UP。つまり、ACTION_MOVEアクションコードで予期しないOnTouchEventが発生します。シーケンスACTION_DOWN->ACTION_UPを取得することはありません(またはほとんどありません)。

また、クリックの位置がわからないため、OnClickListenerを使用できません。では、クリックイベントを検出して、移動と区別するにはどうすればよいですか?

4

10 に答える 10

119

これは非常に単純で、指が動かされることを心配する必要がない別の解決策です。クリックを単に移動距離に基づいている場合、クリックとロングクリックをどのように区別できますか。

これにもっと賢くして、移動した距離を含めることもできますが、ユーザーが200ミリ秒で移動できる距離がクリックではなく移動を構成する場合、私はまだインスタンスに出くわしていません。

setOnTouchListener(new OnTouchListener() {
    private static final int MAX_CLICK_DURATION = 200;
    private long startClickTime;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                startClickTime = Calendar.getInstance().getTimeInMillis();
                break;
            }
            case MotionEvent.ACTION_UP: {
                long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
                if(clickDuration < MAX_CLICK_DURATION) {
                    //click event has occurred
                }
            }
        }
        return true;
    }
});
于 2013-04-03T22:30:11.117 に答える
58

私は以下を考慮して最良の結果を得ました:

  1. 主に、イベント間で移動した距離。さまざまな画面をより適切にサポートするために、ピクセルではなく密度に依存しないピクセルで最大許容距離を指定したかったのです。たとえば、15DPです。ACTION_DOWNACTION_UP
  2. 第二に、イベント間の期間。1秒は良い最大値のようでした。(何人かの人々はかなり「徹底的に」、すなわちゆっくりと「クリック」します;私はまだそれを認識したいです。)

例:

/**
 * Max allowed duration for a "click", in milliseconds.
 */
private static final int MAX_CLICK_DURATION = 1000;

/**
 * Max allowed distance to move during a "click", in DP.
 */
private static final int MAX_CLICK_DISTANCE = 15;

private long pressStartTime;
private float pressedX;
private float pressedY;

@Override
public boolean onTouchEvent(MotionEvent e) {
     switch (e.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            pressStartTime = System.currentTimeMillis();                
            pressedX = e.getX();
            pressedY = e.getY();
            break;
        }
        case MotionEvent.ACTION_UP: {
            long pressDuration = System.currentTimeMillis() - pressStartTime;
            if (pressDuration < MAX_CLICK_DURATION && distance(pressedX, pressedY, e.getX(), e.getY()) < MAX_CLICK_DISTANCE) {
                // Click event has occurred
            }
        }     
    }
}

private static float distance(float x1, float y1, float x2, float y2) {
    float dx = x1 - x2;
    float dy = y1 - y2;
    float distanceInPx = (float) Math.sqrt(dx * dx + dy * dy);
    return pxToDp(distanceInPx);
}

private static float pxToDp(float px) {
    return px / getResources().getDisplayMetrics().density;
}

ここでの考え方はGemのソリューションと同じですが、次の違いがあります。

アップデート(2015):ガブリエルの微調整バージョンもチェックしてください。

于 2013-11-05T12:18:39.683 に答える
32

Jonikの主導で、もう少し微調整されたバージョンを作成しました。指を動かしてから離す前にその場所に戻っても、クリックとして登録されません。

だからここに私の解決策があります:

/**
 * Max allowed duration for a "click", in milliseconds.
 */
private static final int MAX_CLICK_DURATION = 1000;

/**
 * Max allowed distance to move during a "click", in DP.
 */
private static final int MAX_CLICK_DISTANCE = 15;

private long pressStartTime;
private float pressedX;
private float pressedY;
private boolean stayedWithinClickDistance;

@Override
public boolean onTouchEvent(MotionEvent e) {
     switch (e.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            pressStartTime = System.currentTimeMillis();                
            pressedX = e.getX();
            pressedY = e.getY();
            stayedWithinClickDistance = true;
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            if (stayedWithinClickDistance && distance(pressedX, pressedY, e.getX(), e.getY()) > MAX_CLICK_DISTANCE) {
                stayedWithinClickDistance = false;
            }
            break;
        }     
        case MotionEvent.ACTION_UP: {
            long pressDuration = System.currentTimeMillis() - pressStartTime;
            if (pressDuration < MAX_CLICK_DURATION && stayedWithinClickDistance) {
                // Click event has occurred
            }
        }     
    }
}

private static float distance(float x1, float y1, float x2, float y2) {
    float dx = x1 - x2;
    float dy = y1 - y2;
    float distanceInPx = (float) Math.sqrt(dx * dx + dy * dy);
    return pxToDp(distanceInPx);
}

private static float pxToDp(float px) {
    return px / getResources().getDisplayMetrics().density;
}
于 2015-04-29T02:08:04.737 に答える
25

検出器を使用してください、それは動作します、そしてそれはドラッグの場合には上がりません

分野:

private GestureDetector mTapDetector;

初期化:

mTapDetector = new GestureDetector(context,new GestureTap());

インナークラス:

class GestureTap extends GestureDetector.SimpleOnGestureListener {
    @Override
    public boolean onDoubleTap(MotionEvent e) {

        return true;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        // TODO: handle tap here
        return true;
    }
}

onTouch:

@Override
public boolean onTouch(View v, MotionEvent event) {
    mTapDetector.onTouchEvent(event);
    return true;
}

楽しみ :)

于 2016-08-04T13:50:07.150 に答える
6

クリックイベントの認識を最適化するには、次の2つのことを考慮する必要があります。

  1. ACTION_DOWNとACTION_UPの時差。
  2. ユーザーがタッチしたときと指を離したときのx、yの違い。

実際、私はStimsoniとNeethirajanによって与えられたロジックを組み合わせています

だからここに私の解決策があります:

        view.setOnTouchListener(new OnTouchListener() {

        private final int MAX_CLICK_DURATION = 400;
        private final int MAX_CLICK_DISTANCE = 5;
        private long startClickTime;
        private float x1;
        private float y1;
        private float x2;
        private float y2;
        private float dx;
        private float dy;

        @Override
        public boolean onTouch(View view, MotionEvent event) {
            // TODO Auto-generated method stub

                    switch (event.getAction()) 
                    {
                        case MotionEvent.ACTION_DOWN: 
                        {
                            startClickTime = Calendar.getInstance().getTimeInMillis();
                            x1 = event.getX();
                            y1 = event.getY();
                            break;
                        }
                        case MotionEvent.ACTION_UP: 
                        {
                            long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
                            x2 = event.getX();
                            y2 = event.getY();
                            dx = x2-x1;
                            dy = y2-y1;

                            if(clickDuration < MAX_CLICK_DURATION && dx < MAX_CLICK_DISTANCE && dy < MAX_CLICK_DISTANCE) 
                                Log.v("","On Item Clicked:: ");

                        }
                    }

            return  false;
        }
    });
于 2013-07-28T19:12:42.320 に答える
6

onSingleTapUp()Gil SHの回答を使用して、ではなく実装することで改善しましたonSingleTapConfirmed()。はるかに高速で、ドラッグ/移動してもビューをクリックしません。

GestureTap:

public class GestureTap extends GestureDetector.SimpleOnGestureListener {
    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        button.performClick();
        return true;
    }
}

次のように使用します。

final GestureDetector gestureDetector = new GestureDetector(getApplicationContext(), new GestureTap());
button.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        gestureDetector.onTouchEvent(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                return true;
            case MotionEvent.ACTION_UP:
                return true;
            case MotionEvent.ACTION_MOVE:
                return true;
        }
        return false;
    }
});
于 2017-01-08T23:53:34.103 に答える
4

以下のコードはあなたの問題を解決します

    @オーバーライド
        public boolean onTouchEvent(MotionEvent event){
            switch(event.getAction()){
                case(MotionEvent.ACTION_DOWN):
                    x1 = event.getX();
                    y1 = event.getY();
                    壊す;
                case(MotionEvent.ACTION_UP):{
                    x2 = event.getX();
                    y2 = event.getY();
                    dx = x2-x1;
                    dy = y2-y1;

                if(Math.abs(dx)> Math.abs(dy))
                {{
                    if(dx> 0)move(1); //右
                    else if(dx == 0)move(5); //クリック
                    そうでなければmove(2); //左
                }
                そうしないと
                {{
                    if(dy> 0)move(3); // 下
                    else if(dy == 0)move(5); //クリック
                    そうでなければmove(4); //上
                }
                }
            }
            trueを返します。
        }

于 2013-02-20T14:39:16.510 に答える
3

ACTION_MOVEが発生せずにACTION_DOWNが発生することは非常に困難です。最初のタッチが発生した場所とは異なる場所で画面上で指を少しひねると、MOVEイベントがトリガーされます。また、指の圧力の変化もMOVEイベントをトリガーすると思います。Action_Moveメソッドでifステートメントを使用して、元のDOWNモーションから移動が発生した距離を特定しようとします。移動が設定された半径の外側で発生した場合、MOVEアクションが発生します。それはおそらくあなたの試みを行うための最良の、リソース効率の良い方法ではありませんが、それはうまくいくはずです。

于 2012-04-01T15:46:06.953 に答える
2

上記の回答に加えて、onClickアクションとDragアクションの両方を実装したい場合は、以下の私のコードを使用できます。@Stimsoniからの助けを借りて:

     // assumed all the variables are declared globally; 

    public boolean onTouch(View view, MotionEvent event) {

      int MAX_CLICK_DURATION = 400;
      int MAX_CLICK_DISTANCE = 5;


        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN: {
                long clickDuration1 = Calendar.getInstance().getTimeInMillis() - startClickTime;


                    startClickTime = Calendar.getInstance().getTimeInMillis();
                    x1 = event.getX();
                    y1 = event.getY();


                    break;

            }
            case MotionEvent.ACTION_UP:
            {
                long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
                x2 = event.getX();
                y2 = event.getY();
                dx = x2-x1;
                dy = y2-y1;

                if(clickDuration < MAX_CLICK_DURATION && dx < MAX_CLICK_DISTANCE && dy < MAX_CLICK_DISTANCE) {
                    Toast.makeText(getApplicationContext(), "item clicked", Toast.LENGTH_SHORT).show();
                    Log.d("clicked", "On Item Clicked:: ");

               //    imageClickAction((ImageView) view,rl);
                }

            }
            case MotionEvent.ACTION_MOVE:

                long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
                x2 = event.getX();
                y2 = event.getY();
                dx = x2-x1;
                dy = y2-y1;

                if(clickDuration < MAX_CLICK_DURATION && dx < MAX_CLICK_DISTANCE && dy < MAX_CLICK_DISTANCE) {
                    //Toast.makeText(getApplicationContext(), "item clicked", Toast.LENGTH_SHORT).show();
                  //  Log.d("clicked", "On Item Clicked:: ");

                    //    imageClickAction((ImageView) view,rl);
                }
                else {
                    ClipData clipData = ClipData.newPlainText("", "");
                    View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);

                    //Toast.makeText(getApplicationContext(), "item dragged", Toast.LENGTH_SHORT).show();
                    view.startDrag(clipData, shadowBuilder, view, 0);
                }
                break;
        }

        return  false;
    }
于 2016-12-02T06:11:32.873 に答える
1

クリックのみで反応したい場合は、以下を使用してください。

if (event.getAction() == MotionEvent.ACTION_UP) {

}
于 2016-05-11T10:27:01.700 に答える