LongClick event happens too quickly. How can I increase the clicktime required to trigger it?
It is not possible to change the timer on the onLongClick event, it is managed by android itself.
What is possible is to use .setOnTouchListener().
Then register when the MotionEvent is a ACTION_DOWN.
Note the current time in a variable.
Then when a MotionEvent with ACTION_UP is registered and the current_time - actionDown time > 1200 ms then do something.
so pretty much:
Button button = new Button();
long then = 0;
button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
then = (Long) System.currentTimeMillis();
}
else if(event.getAction() == MotionEvent.ACTION_UP){
if(((Long) System.currentTimeMillis() - then) > 1200){
return true;
}
}
return false;
}
})
This is the simplest way that I have found to achieve this behavior. It has a couple of advantages over the currently accepted answer.
- By checking
view.isPressed
we ensure that theonClick
andonLongClick
are not triggered if the touch event leaves the view. This mimics the defaultonClick
andonLongClick
behavior of the system. - The event already stores timing information so there is no need to store the start time on
ACTION_DOWN
or calculate the current time onACTION_UP
ourselves. This means that we can use it for multiple views at the same time because we aren't tracking the event start time in a single variable outside ofonTouch
.
NOTE: ViewConfiguration.getLongPressTimeout()
is the default and you can change that check to use any value you want.
NOTE: If the view is not normally clickable you will need to call view.setClickable(true)
for the view.isPressed()
check to work.
@Override
public boolean onTouch(View view, MotionEvent event) {
if (view.isPressed() && event.getAction() == MotionEvent.ACTION_UP) {
long eventDuration = event.getEventTime() - event.getDownTime();
if (eventDuration > ViewConfiguration.getLongPressTimeout()) {
onLongClick(view);
} else {
onClick(view);
}
}
return false;
}
If you, like @ampersandre, would like the long click event to trigger immediately once the delay period is reached instead of waiting for ACTION_UP
then the following works well for me.
@Override
public boolean onTouch(View view, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
view.setTag(true);
} else if (view.isPressed() && (boolean) view.getTag()) {
long eventDuration = event.getEventTime() - event.getDownTime();
if (eventDuration > ViewConfiguration.getLongPressTimeout()) {
view.setTag(false);
onLongClick(view);
} else if (event.getAction() == MotionEvent.ACTION_UP) {
onClick(view);
}
}
return false;
}
I worked out a solution with the help of Rohan :)
I adapted his answer to fit my requirements.
When the user pushes the button, a thread is started. The thread sleeps for my desired delay, then when it wakes up, it executes whatever code I need it to do. When the user lets go, the thread is killed. This accomplishes what I want, because if the user lets go before the thread wakes up, the thread is interrupted and the action doesn't occur.
I like this approach because it lets me execute my business logic as soon as the delay is up, which is good because I can give the user some feedback letting them know they've pushed long enough (the phone can vibrate, for example).
The downside to this approach is: there is a risk that the user lets go of the button while your desired action is running, and kills the thread before everything is done. This isn't a huge problem for my case, because my business logic does very little; it just fires an event for some other class to process. If the action didn't complete fully, it's acceptable for the user to have to try again.
The code is a little longer than I'd like, but if this is a common feature in your application, it's easily re-useable. Here's a code example:
protected class MyLongClickListener implements View.OnTouchListener {
private Thread longClickSensor;
public boolean onTouch(View view, MotionEvent event) {
// If the user is pressing down and there is no thread, make one and start it
if (event.getAction() == MotionEvent.ACTION_DOWN && longClickSensor == null) {
longClickSensor = new Thread(new MyDelayedAction());
longClickSensor.start();
}
// If the user has let go and there was a thread, stop it and forget about the thread
if (event.getAction() == MotionEvent.ACTION_UP && longClickSensor != null) {
longClickSensor.interrupt();
longClickSensor = null;
}
return false;
}
private class MyDelayedAction implements Runnable {
private final long delayMs = 1200;
public void run() {
try {
Thread.sleep(delayMs); // Sleep for a while
doBusinessLogic(); // If the thread is still around after the sleep, do the work
} catch (InterruptedException e) { return; }
}
private void doBusinessLogic() {
// Make sure this logic is as quick as possible, or delegate it to some other class
// through Broadcasted Intents, because if the user lets go while the work is happenening,
// the thread will be interrupted.
}
}
}