Android中的DoubleTap

DoubleTap in android

本问题已经有最佳答案,请猛点这里访问。

我需要创建一个小文本区域。当我双击该文本区域时,它将移动到下一个活动。我怎么能这样做?


如果您正确设置,GestureListener中的OnDoubleTapListener非常有用。您不需要处理每个单击并计算两者之间的时间。相反,让Android为您处理点击,双击,滚动或投掷可能是什么。使用实现GestureListenerOnDoubleTapListener的辅助类SimpleGestureListener,您不需要做太多事情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
findViewById(R.id.touchableText).setOnTouchListener(new OnTouchListener() {
    private GestureDetector gestureDetector = new GestureDetector(Test.this, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            Log.d("TEST","onDoubleTap");
            return super.onDoubleTap(e);
        }
        ... // implement here other callback methods like onFling, onScroll as necessary
    });

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Log.d("TEST","Raw event:" + event.getAction() +", (" + event.getRawX() +"," + event.getRawY() +")");
        gestureDetector.onTouchEvent(event);
        return true;
    }
});

注意:我测试了很长一段时间才发现return truereturn false的正确混合物是什么。这是非常棘手的部分。

另一个注意事项:测试时,请在真实设备上进行测试,而不是模拟器。我很难让鼠标足够快以创建一个onFling事件。真实设备上的真实手指似乎要快得多。


更好的选择是创建一个轻量级的抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public abstract class DoubleClickListener implements OnClickListener {

    private static final long DOUBLE_CLICK_TIME_DELTA = 300;//milliseconds

    long lastClickTime = 0;

    @Override
    public void onClick(View v) {
        long clickTime = System.currentTimeMillis();
        if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA){
            onDoubleClick(v);
            lastClickTime = 0;
        } else {
            onSingleClick(v);
        }
        lastClickTime = clickTime;
    }

    public abstract void onSingleClick(View v);
    public abstract void onDoubleClick(View v);
}

并使用它

1
2
3
4
5
6
7
8
9
10
11
12
 view.setOnClickListener(new DoubleClickListener() {

        @Override
        public void onSingleClick(View v) {

        }

        @Override
        public void onDoubleClick(View v) {

        }
    });


在代码下面使用非常简单的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    boolean firstTouch = false;
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if(event.getAction() == event.ACTION_DOWN){
                if(firstTouch && (Helper.getCurrentTimeInMilliSeconds() - time) <= 300) {
                    //do stuff here for double tap
                    Log.e("** DOUBLE TAP**"," second tap");
                    firstTouch = false;

                } else {
                    firstTouch = true;
                    time = Helper.getCurrentTimeInMilliSeconds();
                    Log.e("** SINGLE  TAP**"," First Tap time "+time);
                    return false;
                }
            }
            return true;
    }


----------

我采用了一种不同的方法来实现对Android视图的双击。我创建了自己的逻辑来检测双击,它很容易实现。

Here are the steps for doing this:
1. Set onTouchListener on the view you want to receive the touch event.
2. Implement the onTouch(view,event) method. (In double tap the key is to detect two ACTION_DOWN and ACTION_UP events. For this we will have to calculate the time duration between two successive down-up events).

以下是实现此目的的逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
    /* variable for counting two successive up-down events */
int clickCount = 0;
/*variable for storing the time of first click*/
long startTime;
/* variable for calculating the total time*/
long duration;
/* constant for defining the time duration between the click that can be considered as double-tap */
static final MAX_DURATION = 500;
@Override
public boolean onTouch (View v, MotionEvent event)
{
    switch(event.getAction() & MotionEvent.ACTION_MASK)
    {
        case MotionEvent.ACTION_DOWN:
            startTime = System.currentTimeMillis();
            clickCount++;
            break;
        case MotionEvent.ACTION_UP:
            long time = System.currentTimeMillis() - startTime;
            duration=  duration + time;
            if(clickCount == 2)
            {
                if(totalTime <= DURATION)
                {
                    Toast.makeText(captureActivity.this,"double tap",Toast.LENGTH_LONG).show();
                }
                clickCount = 0;
                duration = 0;
                break;            
            }
    }
    return true;    
}

====编辑======

对我来说,上面的描述是不可接受的,因为上面的逻辑不会超时。

改用它

@覆盖
public boolean onTouch(查看paramView,MotionEvent事件){
switch(event.getAction()&amp; MotionEvent.ACTION_MASK)
{

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    case MotionEvent.ACTION_UP:

        clickCount++;

        if (clickCount==1){
            startTime = System.currentTimeMillis();
        }

        else if(clickCount == 2)
        {
            long duration =  System.currentTimeMillis() - startTime;
            if(duration <= ONE_SECOND)
            {                    
                    Toast.makeText(captureActivity.this,"double tap",Toast.LENGTH_LONG).show();
                clickCount = 0;
                duration = 0;
            }else{
                clickCount = 1;
                startTime = System.currentTimeMillis();
            }
            break;            
        }
}
return true;

}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
X_View.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onItemClick(View view)
            {
    long timeNow=Calendar.getInstance().getTimeInMillis();
    long timeLastTapped=Long.valueOf(view.getTag().toString()); // Initially set to zero in adapter
    final int minDurationBetweenDoubleTap=500;
    if(timeLastTapped != 0)
            if( timeNow- timeLastTapped < minDurationBetweenDoubleTap)
                        {
                        Toast.makeText(getApplicationContext(),"DoubleTapped", 10).show();
                        }
    view.setTag(""+timeNow);
}

我有类似的问题和解决方案,直到我想做其他触摸事件,如刷卡和onLongPress。从未调用过这些方法,所以我必须实现一个OnDoubleTapListener。我做了如下:

1
public class MainActivity extends Activity implements OnDoubleTapListener

然后只需实现三种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
    if(e.getAction()==1)
    {
        Toast.makeText(getApplicationContext(),"DOUBLE TAP",Toast.LENGTH_SHORT).show();
        // TODO Auto-generated method stub
        // Implement code here!!!
    }
    return true;
}

@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
    return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
    return true;
}

只需实现onDoubleTapEvent方法即可。
我不知道何时调用其他两个方法,但这对我有用


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import android.app.Activity;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.widget.Toast;
public class SimpleGestureFilter extends SimpleOnGestureListener
{
 public final static int SWIPE_UP    = 1;
 public final static int SWIPE_DOWN  = 2;
 public final static int SWIPE_LEFT  = 3;
 public final static int SWIPE_RIGHT = 4;  
 public final static int MODE_TRANSPARENT = 0;
 public final static int MODE_SOLID = 1;
 public final static int MODE_DYNAMIC = 2;  
 private final static int ACTION_FAKE = -13;  
 private int swipe_Min_Distance = 100;
 private int swipe_Max_Distance = 350;
 private int swipe_Min_Velocity = 100;  
 private int mode = MODE_DYNAMIC;
 private boolean running = true;
 private boolean tapIndicator = false;  
 private Activity context;
 private GestureDetector detector;
 private SimpleGestureListener listener;  
 public SimpleGestureFilter(Activity context,SimpleGestureListener sgf)
 {  
  this.context = context;  
  this.detector = new GestureDetector(context, this);  
  this.listener = sgf;  
 }
 public void onTouchEvent(MotionEvent me)
 {
  // TODO Auto-generated method stub
  if(!this.running)
   return;
  boolean result=this.detector.onTouchEvent(me);
  if(this.mode==MODE_SOLID)
   me.setAction(MotionEvent.ACTION_CANCEL);
  else if(this.mode==MODE_DYNAMIC)
  {
   if(me.getAction()==ACTION_FAKE)
    me.setAction(MotionEvent.ACTION_UP);
   else if(result)
    me.setAction(MotionEvent.ACTION_CANCEL);
   else if(this.tapIndicator)
   {
    me.setAction(MotionEvent.ACTION_DOWN);
    this.tapIndicator=false;
   }
  }
 }
 public void setMode(int m)
 {
  this.mode=m;
 }
 public int getMode()
 {
  return this.mode;
 }
 public void setEnabled(boolean status)
 {
  this.running=status;
 }
 public void setSwipeMaxDistance(int distance)
 {
  this.swipe_Max_Distance=distance;
 }
 public void setSwipeMinDistance(int distance)
 {
  this.swipe_Min_Distance=distance;
 }
 public int getSwipeMaxDistance()
 {  
  return this.swipe_Max_Distance;
 }  
 public int getSwipeMinDistance()
 {  
  return this.swipe_Min_Distance;
 }  
 public int getSwipeMinVelocity()
 {  
  return this.swipe_Min_Velocity;
 }

 public boolean onFling(MotionEvent e1,MotionEvent e2,float velocityX,float velocityY)
 {
  final float xDistance=Math.abs(e1.getX()-e2.getX());
  final float yDistance=Math.abs(e1.getY()-e2.getY());
  if(xDistance>this.swipe_Max_Distance || yDistance> this.swipe_Max_Distance)

   return false;
  velocityX = Math.abs(velocityX);
  velocityY = Math.abs(velocityY);
  boolean result=false;
  if(velocityX > this.swipe_Min_Velocity && xDistance > this.swipe_Min_Distance)
  {
   if(e1.getX() > e2.getX()) // right to left Move
    this.listener.onSwipe(SWIPE_LEFT);
   else
    this.listener.onSwipe(SWIPE_RIGHT);
   result=true;
  }
  else if(velocityY > this.swipe_Min_Velocity && yDistance > this.swipe_Min_Distance)
  {
   if(e1.getY() > e2.getY()) // bottom to top Move
    this.listener.onSwipe(SWIPE_UP);
   else
    this.listener.onSwipe(SWIPE_DOWN);
   result=true;
  }
  return result;
 }
 public boolean onSingleTapUp(MotionEvent e)
 {
  this.tapIndicator=true;
  return false;
 }
 public boolean onDoubleTap(MotionEvent e)
 {
  this.listener.onDoubleTap();
  return false;
 }
 public boolean onDoubleTapEvent(MotionEvent e)
 {    
  return true;
 }
 public boolean onSingleTapConfirmed(MotionEvent e)
 {
  if(this.mode==MODE_DYNAMIC)
  {
   e.setAction(ACTION_FAKE);
   this.context.dispatchTouchEvent(e);
  }
  return false;
 }
 static interface SimpleGestureListener
 {    
  void onSwipe(int direction);    
  void onDoubleTap();
 }
}