Need a button by clicking on which you can perform the action and if the button is squeezed further than, say, 200 ms., Then perform the action until the button is released.
But it turns out not so simple. I can't just write something like this in the call:

if(pressed){ Thread.sleep(100); doIt(); } 

This will stop the flow of the user interface. So this should be transferred to another thread.

That is, by pressing the button, a new thread should be created, which will monitor whether the button is pressed, how much time has passed, to perform the desired action.

In addition, you will definitely have to make sure that the flow was completed with the closure of the application, etc.

Maybe there are other reasonable ways to solve this problem? Ideally, if there was some functional of the delayed task, like __Platform.runLater__ , but only to complete the task in 100 ms, but I did not find anything like that.

    1 answer 1

    Wrote just such a design.

     private Thread pressingThread; @FXML void onPress() { System.out.println("do something"); final int initDelay = 200; final int repeatDelay = 50; pressingThread = new Thread(() -> { try { Thread.sleep(initDelay); while (true) { Platform.runLater(() -> System.out.println("do something again")); Thread.sleep(repeatDelay); } } catch (InterruptedException ignored) { } }); pressingThread.setDaemon(true); pressingThread.start(); } @FXML void onRelease() { pressingThread.interrupt(); } 

    A thread is created that waits for the required time, then, in an infinite loop, transfers control to the UI thread the desired action. To shut down, you must call the interrupt () method.

    I tried to implement through the pressed variable, which would keep the state of the button, and then check if the button is pressed, but there is a subtlety, that if you quickly press the button twice, you can create two threads that will think that the button was not released. Therefore, you still have to use interrupt () .

    Ed. Rewrote in a more convenient form.

     import javafx.application.Platform; public class DoWhilePressed { private int initDelay = 200; private int repeatDelay = 50; private Runnable doWhilePressed = ()->{}; private Thread pressingThread; private pressed = false; public void press(){ if(pressed)return; pressed = true; doWhilePressed.run(); pressingThread = new Thread(() -> { try { Thread.sleep(initDelay); while (true) { Platform.runLater(() -> doWhilePressed.run()); Thread.sleep(repeatDelay); } } catch (InterruptedException ignored) { } }); pressingThread.setDaemon(true); pressingThread.start(); } public void release(){ pressed = false; pressingThread.interrupt(); } public int getInitDelay() { return initDelay; } public DoWhilePressed setInitDelay(int initDelay) { this.initDelay = initDelay; return this; } public int getRepeatDelay() { return repeatDelay; } public DoWhilePressed setRepeatDelay(int repeatDelay) { this.repeatDelay = repeatDelay; return this; } public Runnable getDoWhilePressed() { return doWhilePressed; } public DoWhilePressed setDoWhilePressed(Runnable doWhilePressed) { this.doWhilePressed = doWhilePressed; return this; } } 

    You can use as

     private DoWhilePressed doWhilePressed = new DoWhilePressed() .setInitDelay(300) .setRepeatDelay(30) .setDoWhilePressed(() -> System.out.println("pressed")); @FXML void onPress() { doWhilePressed.press(); } @FXML void onRelease() { doWhilePressed.release(); } 
    • Approximately such a structure comes to mind. Whatever the second stream is created, when releasing the button, set the disable At the time of initDelay. - Maxim
    • @Maxim And still it seems that there exists somewhere a “right”, simple and elegant solution - Serhii Dikobrazko
    • one
      Well, you can not create a stream right on the spot, but send an event that the subscriber will catch and already perform the desired task (Publish & Subscribe Pattern). - Maxim