Подтвердить что ты не робот

Событие LongClick происходит слишком быстро. Как увеличить время щелчка, необходимое для его запуска?

В приложении, над которым я работаю, у меня есть требование, чтобы пользователь должен щелкнуть и удерживать компонент за период времени до того, как произойдет определенное действие.

В настоящее время я использую OnLongClickListener для прослушивания longclick, но я нахожу, что длина щелчка для запуска события OnLongClick слишком короткая.

Например, предположим, что событие LongClick запускается после щелчка на 400 мкс, но я хочу, чтобы пользователь должен был щелкнуть и удерживать его в течение 1200 мс до того, как событие запустится.

Можно ли каким-либо образом настроить событие LongClick для более длительного клика?
Или есть, возможно, еще одна конструкция, которая позволила бы мне слушать более длинные клики?

4b9b3361

Ответ 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...");

    }
};