Событие LongClick происходит слишком быстро. Как увеличить время щелчка, необходимое для его запуска?
В приложении, над которым я работаю, у меня есть требование, чтобы пользователь должен щелкнуть и удерживать компонент за период времени до того, как произойдет определенное действие.
В настоящее время я использую OnLongClickListener для прослушивания longclick, но я нахожу, что длина щелчка для запуска события OnLongClick слишком короткая.
Например, предположим, что событие LongClick запускается после щелчка на 400 мкс, но я хочу, чтобы пользователь должен был щелкнуть и удерживать его в течение 1200 мс до того, как событие запустится.
Можно ли каким-либо образом настроить событие LongClick для более длительного клика?
Или есть, возможно, еще одна конструкция, которая позволила бы мне слушать более длинные клики?
Ответы
Ответ 1
Невозможно изменить таймер на событии onLongClick, он управляется самим андроидом.
Возможно использовать .setOnTouchListener().
Затем зарегистрируйтесь, когда MotionEvent является ACTION_DOWN.
Обратите внимание на текущее время в переменной.
Затем, когда зарегистрирован MotionEvent с ACTION_UP, а current_time - actionDown time > 1200 ms, сделайте что-нибудь.
так много:
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;
}
})
Ответ 2
Я разработал решение с помощью Rohan:)
Я адаптировал свой ответ, чтобы соответствовать моим требованиям.
Когда пользователь нажимает кнопку, запускается поток. Поток спит для моей желаемой задержки, а затем, когда он просыпается, он выполняет любой код, который мне нужен. Когда пользователь отпускает, поток уничтожается. Это выполняет то, что я хочу, потому что если пользователь отпустит до того, как поток проснется, поток прерывается и действие не происходит.
Мне нравится этот подход, потому что он позволяет мне выполнять свою бизнес-логику, как только задержка заканчивается, что хорошо, потому что я могу дать пользователю некоторую обратную связь, давая им знать, что они достаточно долго толкали (телефон может вибрировать, для пример).
Недостатком этого подхода является: существует риск того, что пользователь отпустит кнопку, пока ваше искомое действие будет запущено, и уничтожит поток до того, как все будет сделано. Это не большая проблема для моего случая, потому что моя бизнес-логика очень мало; он просто запускает событие для какого-либо другого класса для обработки. Если действие не завершено полностью, допустимо, чтобы пользователь снова попробовал.
Код немного длиннее, чем хотелось бы, но если это обычная функция в вашем приложении, она легко перерабатывается. Вот пример кода:
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.
}
}
}
Ответ 3
Это самый простой способ, который я нашел для достижения такого поведения. Он имеет несколько преимуществ по сравнению с принятым в настоящее время ответом.
- Проверяя
view.isPressed
, мы гарантируем, что onClick
и onLongClick
не запускаются, если событие касания покидает представление. Это имитирует поведение системы по умолчанию onClick
и onLongClick
.
- Событие уже хранит информацию о времени, поэтому нет необходимости хранить время начала на
ACTION_DOWN
или вычислять текущее время на ACTION_UP
самих себя. Это означает, что мы можем использовать его для нескольких представлений одновременно, потому что мы не отслеживаем время начала события в одной переменной вне onTouch
.
ПРИМЕЧАНИЕ: ViewConfiguration.getLongPressTimeout()
по умолчанию, и вы можете изменить эту проверку, чтобы использовать любое значение, которое вы хотите.
ПРИМЕЧАНИЕ. Если просмотр не является обычно кликабельным, вам необходимо вызвать view.setClickable(true)
для проверки view.isPressed()
.
@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;
}
Если вы, как и @ampersandre, хотели бы, чтобы событие с длинным щелчком запускалось сразу после достижения периода задержки, а не для ожидания ACTION_UP
, тогда следующее работает хорошо для меня.
@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;
}
Ответ 4
final boolean[] isLongPress = {false};
final int duration = 3000;
final Handler someHandler = new Handler();
final Runnable someCall = new Runnable() {
@Override
public void run() {
if(isLongPress[0]) {
// your code goes here
}
}
};
someButton.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int eventAction = event.getAction();
if(eventAction == MotionEvent.ACTION_DOWN){
isLongPress[0] = true;
someHandler.postDelayed(someCall, duration);
}
else if (eventAction == MotionEvent.ACTION_UP) {
isLongPress[0] = false;
someHandler.removeCallbacks(someCall);
}
return false;
}
});
Ответ 5
Один метод может сделать это. Это простой в использовании, легко настроить продолжительность времени, вовремя, чтобы вызвать обратный вызов.
public static void setOnLongClickListener(final View view, final View.OnLongClickListener longClickListener, final long delayMillis)
{
view.setOnTouchListener(new View.OnTouchListener()
{
final Handler handler = new Handler();
final Runnable runnable = new Runnable()
{
@Override
public void run()
{
longClickListener.onLongClick(view);
mRunning = false;
}
};
boolean mRunning;
boolean mOutside;
RectF mRect = new RectF();
@Override
public boolean onTouch(View v, MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
{
handler.postDelayed(runnable, delayMillis);
mRunning = true;
mOutside = false;
mRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
break;
}
case MotionEvent.ACTION_MOVE:
if (!mOutside)
{
mOutside = !mRect.contains(v.getLeft() + event.getX(), v.getTop() + event.getY());
if (mOutside)
{
handler.removeCallbacks(runnable);
mRunning = false;
}
}
break;
case MotionEvent.ACTION_UP:
{
if (mRunning)
v.performClick();
handler.removeCallbacks(runnable);
mRunning = false;
break;
}
case MotionEvent.ACTION_CANCEL:
{
handler.removeCallbacks(runnable);
mRunning = false;
break;
}
}
return true; // !!!
}
});
}
Ответ 6
Я нашел простое решение, изучающее, как работает длинное пресс-мероприятие.
При каждом щелчке по просмотру в очередь с задержкой добавляется Runnable
типа CheckForLongPress
. Если задержка заканчивается, вызывается OnLongClickListener
. Если перед завершением задержки происходит другое событие, то CheckForLongPress Runnable
удаляется из очереди.
Я просто переопределяю общедоступный метод postDelayed(Runnable action, long delayMillis)
представления, чтобы изменить задержку ОС
@Override public boolean postDelayed(Runnable action, long delayMillis) {
boolean isLongPress = action.getClass().getSimpleName().equals("CheckForLongPress");
return super.postDelayed(action, isLongPress ? LONG_PRESS_MILLIS : delayMillis);
}
Я установил LONG_PRESS_MILLIS
в 100, и он работает!
Надеюсь, это поможет!!!;)
Ответ 7
Я нашел этот метод. Это так просто.
private static final long HOLD_PRESS_TIME = 1200; //ms unit
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_control);
//Declare your button and add listener
ImageView iconImg = findViewById(R.id.more_icon);
iconImg.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()){
case MotionEvent.ACTION_DOWN:
Log.w("Button", "Button is pressed...");
//Start timer
timer.start();
break;
case MotionEvent.ACTION_UP:
Log.w("Button", "Button is released...");
//Clear timer
timer.cancel();
break;
}
return false;
}
});
}
private CountDownTimer timer = new CountDownTimer(HOLD_PRESS_TIME, 200) {
@Override
public void onTick(long l) {
Log.w("Button", "Count down..."+l); //It call onFinish() when l = 0
}
@Override
public void onFinish() {
//TODO your action here!
Log.w("Button", "Count finish...");
}
};