-2

I'm trying to get a circle to move to the direction I've defined pixel by pixel while the button is being pressed. So far I've managed to make it move one pixel by each click with this part:

button1.addEventHandler(MouseEvent.MOUSE_PRESSED,
                new EventHandler<MouseEvent>(){
            public void handle(MouseEvent e){
newX = pallo.getTranslateX()+ 1 ;
                pallo.setTranslateX(newX);

            }
        });

pallo is the circle here and button1 is the button which causes it to move. I've been reading about the timer method here https://docs.oracle.com/javase/7/docs/api/java/util/Timer.html but I've been unable to comprehend what I'm supposed to type to repeat the sequence say, once every 10 milliseconds, only when the button is being pressed. Could someone provide me with the fixed code that changes the function to what I seek so I could try and understand it with that?

  • Create an animation, e.g. with an `AnimationTimer`. Start it when the button is pressed and stop it when the button is released. – James_D Mar 04 '17 at 21:25

1 Answers1

2

If you want to stick with the Timer class you can do something like the following:

public class TimerExample {

    private final Timer timer = new Timer();
    private TimerTask timerTask;

    public void setUpButton(Button btn, Circle cir) {
        btn.addEventHandler(MouseEvent.MOUSE_PRESSED, me -> {
            timerTask = new TimerTask() {

                @Override
                public void run() {
                    Platform.runLater(() 
                         -> circle.setTranslateX(circle.getTranslateX() + 1);
                }

            };
            timer.schedule(timerTask, 0L, 10L);
        });

        btn.addEventHandler(MouseEvent.MOUSE_RELEASED, me -> {
            timerTask.cancel();
            timerTask = null;
            timer.purge(); // So TimerTasks don't build up inside the Timer
                           // I'm not 100% sure this must/should be called
                           // every time
        });
    }
}

When using a Timer you must schedule TimerTasks with it. As you can see I used timer.schedule(timerTask, 0L, 10L) which means that the timerTask will run after an initial delay of 0 milliseconds and then every 10 milliseconds. This scheduling happens when the button is pressed. When the mouse is released the TimerTask gets cancelled (won't run again) then I set the variable to null and purge the Timer to remove any references to the TimerTask.

In the run method you must manipulate the circle in Platform.runLater(Runnable) since the TimerTask will not be called on the FxApplication Thread.

Personally, if sticking with something like a timer, I would prefer to use an javafx.animation.AnimationTimer. This is due to the fact the AnimationTimer's handle(long) method is called on the FxApplication Thread once every frame.

Something like this:

public class AnimationTimerExample {

    public void setUpButton(Button btn, Circle circle) {
        AnimationTimer timer = new AnimationTimer() {

            private final long delay = 10_000_000L; // This must be in 
                                                    // nanoseconds
                                                    // since "now" is in
                                                    // nanoseconds
            private long lastExecution;

            @Override
            public void handle(long now) {
                if (now - (lastExecution + delay) <= 0L {
                    // Move circle
                    lastExecution = now;
                }
            }

        };

        btn.addEventHandler(MouseEvent.MOUSE_PRESSED, me -> timer.start());
        btn.addEventHandler(MouseEvent.MOUSE_RELEASED, me -> timer.stop());
    }
}

Here you have to manually calculate when to run based on delay, but this avoids any threading issues.

Slaw
  • 37,820
  • 8
  • 53
  • 80
  • 1
    Listening to [the `pressed` property](https://docs.oracle.com/javase/8/javafx/api/javafx/scene/Node.html#pressedProperty) would be more convenient, since it already checks, if the primary mouse button is used instead of reacting to arbitrary mouse buttons... – fabian Mar 05 '17 at 08:37