I am trying to catch double-tap events using OnTouchListener. I figure I would set a long for motionEvent.ACTION_DOWN, and a different long for a second motionEvent.ACTION_D
Another approach that supports both single click and double clicks using Kotlin Coroutine:
var lastDown = 0L
var clickCount = 0
view.setOnTouchListener {v, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
val downTime = System.currentTimeMillis()
clickCount++
if (lastDown > 0 && downTime - lastDown < 300 && clickCount == 2) {
//double clicks happens here
clickCount = 0
}
lastDown = downTime
}
MotionEvent.ACTION_UP -> {
CoroutineScope(Dispatchers.IO).launch {
//in case clickCount goes beyond than 1, here set it to 1
val upTime = System.currentTimeMillis()
if (upTime - lastDown <= 300 && clickCount > 0) {
clickCount = 1
}
delay(300)
if (System.currentTimeMillis() - lastDown > 300 && clickCount == 1) {
withContext(Dispatchers.Main) {
//single click happens here
}
clickCount = 0
}
}
}
}
true
}
With the helper class SimpleGestureListener that implements the GestureListener and OnDoubleTapListener you dont need much to do.
yourView.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;
}});
That's much easyer:
//variable for storing the time of first click
long startTime;
//constant for defining the time duration between the click that can be considered as double-tap
static final int MAX_DURATION = 200;
if (event.getAction() == MotionEvent.ACTION_UP) {
startTime = System.currentTimeMillis();
}
else if (event.getAction() == MotionEvent.ACTION_DOWN) {
if(System.currentTimeMillis() - startTime <= MAX_DURATION)
{
//DOUBLE TAP
}
}
I addressed this problem earlier. It involves using a Handler to wait a certain amount of time to wait for the second click: How can I create a Single Click Event and Double Click Event when the Menu Button is pressed?
In your class definition:
public class main_activity extends Activity
{
//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 int MAX_DURATION = 500;
}
Then in your class body:
OnTouchListener MyOnTouchListener = new OnTouchListener()
{
@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(duration<= MAX_DURATION)
{
Toast.makeText(captureActivity.this, "double tap",Toast.LENGTH_LONG).show();
}
clickCount = 0;
duration = 0;
break;
}
}
return true;
}
}
This was adapted from an answer in: DoubleTap in android by https://stackoverflow.com/users/1395802/karn
Here is my solution.
It was important for me to have a fast and clear separation of 'single tap' and 'double tap'.
I tried GestureDetector
first but had very bad results. Maybe a result of my nested use of scrollviews - who knows...
I focus on MotionEvent.ACTION_UP
and the ID of the tapped element. To keep the first tap alive I use a Handler
sending a delayed message (350ms) so the user has some time to place its second tap on the ImageView
. If the user placed a second tap on an element with the identical id I take this as double tap, remove the delayed message and rund my custom code for 'double tap'. If the user placed a tap on an element with a different ID I take this as new tap and create another Handler
for it.
Class global variables
private int tappedItemId = -1;
Handler myTapHandler;
final Context ctx = this;
Code example
ImageView iv = new ImageView(getApplicationContext());
//[...]
iv.setId(i*1000+n);
iv.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP: {
//active 'tap handler' for current id?
if(myTapHandler != null && myTapHandler.hasMessages(v.getId())) {
// clean up (to avoid single tap msg to be send and handled)
myTapHandler.removeMessages(tappedItemId);
tappedItemId = -1;
//run 'double tap' custom code
Toast.makeText(ScrollView.this, "double tap on "+v.getId(), Toast.LENGTH_SHORT).show();
return true;
} else {
tappedItemId = v.getId();
myTapHandler = new Handler(){
public void handleMessage(Message msg){
Toast.makeText(ctx, "single tap on "+ tappedItemId + " msg 'what': " + msg.what, Toast.LENGTH_SHORT).show();
}
};
Message msg = Message.obtain();
msg.what = tappedItemId;
msg.obj = new Runnable() {
public void run() {
//clean up
tappedItemId = -1;
}
};
myTouchHandler.sendMessageDelayed(msg, 350); //350ms delay (= time to tap twice on the same element)
}
break;
}
}
return true;
}
});