
The application of an interesting class in a JavaFX application is to prevent the window from “freezing” during a long process. At the same time a little bit about the features of drawing scenes in JavaFX.
Suppose there is a desire (and / or necessity) to set up a Java desktop application with a long process mapping. Well, you never know, there is a long cycle of calculations, and the desire to see the result of each iteration, at least briefly. To make sure that the process goes in the right direction, maybe a histogram of which to build.
Suppose the interface is decided to do on JavaFX. Somehow:
file main.javapackage romeogolf.example1; import java.util.ArrayList; import javafx.animation.AnimationTimer; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Stage; public class Main extends Application {

')
This will result in a window that so far nothing can. For test purposes, I’ll add wildly complex calculations as an increment to a counter variable. The longProcess method will then look, let's say, like this:
private void longProcess(){ int digit; for(int i = 0; i <= this.maxCount; i++){ for(int j = 0; j <= digitCount; j++){ digit = (int) (i % (Math.pow(10.0, (double)(j + 1)))); digit = (int) (digit / (Math.pow(10.0, (double)j))); this.aLabels.get(digitCount - j).setText(Integer.toString(digit)); } } }
Here, a certain number i is plainly broken down into numbers and displayed in the familiarity. Launch this creation by an insane programmer genius and press a single button. Very quickly get the result, almost instantly:

Very happy that so fast. But bad luck is that this is not the problem. The goal was to see the whole process, albeit slowly. That would somehow slow down each pass of the cycle and give the command to display the result ... Something like repaint () in Swing, or some refresh, update, maybe,
Application.ProcessMessages .
Not provided. In Swing, when you call a redraw, it is immediately implemented. In JavaFX, the redraw requirement is reflected in the scene graph, and the scene is redrawn when the time comes when pulse is ticked. Pulse is an event that tells the scene graph that it’s time to draw. It ticks no more than 60 fps, if the animation is running, and, if necessary, if changes occurred in the scene graph. This is the so-called Retained Mode, which is often translated as “abstract mode”, implying that this approach increases the level of abstraction, in contrast to Immediate Mode - the direct mode.
Article
Retained Mode Versus Immediate Mode describes the difference between the approaches on the example of Direct2D and WPF. I will translate the last paragraph:
Retained Mode API is easier to use, because this API does more work for you: initialization, state maintenance, cleaning. But on the other hand, such an approach is often less flexible, since this API imposes its own scene model. In addition, the Retained Mode API may have increased memory requirements, as it is necessary to provide a general-purpose scene model. Using the Immediate Mode API you can implement targeted optimization.
What to do? Let's try this. We take out the counter from the loop variable, make it a class field (let's say, right before the new method):
private int counter = 0;
Let's make a new method a little differently (you can simply change the old one, but if you leave, it will be easier to switch between the old and new versions). Remove the outer loop altogether:
private void longProcess2(){ int digit;
Add the following code at the end of the Main class:
protected AnimationTimer at = new AnimationTimer(){ @Override public void handle(long now) { longProcess2(); } };
To start the process in processing a button instead of
longProcess();
set
at.start();
And for a timely stop, we insert into the longProcess2 () method itself, which implements the process, the following lines (at the very beginning):
if(counter > maxCount){ at.stop(); return; }
Now about why this was all needed. As a result, an at-instance of the AnimationTimer class is created. Its handle () method is called each time the application window is redrawn. That is, by calling longProcess2 (), we ordered to redraw the familiarity of numbers. Caused the need to redraw the scene. It calls handle (), and starts longProcess2 () again. And so on until in the method itself longProcess2 () there is a stop condition for at.stop ().
Now when you press the button in the "windows" tsiferki will flicker. Flicker at different speeds, depending on the price of the discharge.

If, for example, increase digitCount to 7, and maxCount - to 98765432, the difference between the options without a timer and with a timer will become significantly more noticeable. In the initial version, you will have to contemplate the crosses in the familiarity until the process is completed, and even close the window in the traditional way (the “cross” in the upper right corner) does not work. In the version with the timer, the process is wonderfully displayed, the window can be dragged behind the title and closed at any time, but the process itself will stretch incredibly. Here the question is what more is needed - speed or display.
In fact, the speed can be slightly increased by skipping the display of individual iterations, and the more we skip, the faster we finish, although we will see less. To do this, you can insert into longProcess2 (), at the very end, instead of
counter++;
something like
for(int skip = 0; skip < 100000; skip++){ if(counter >= maxCount){ break; } counter++; }
Then the result of each 100-thousandth iteration will be displayed, and thanks to the stop condition, the final result will be displayed at the end. True, in the process, the lower digits will be stupidly zeros, but for clarity, they can also be washed out somehow. However, these are details from the field of whistles, for example, it has no relation.
It would be possible to add the reset of the counter before each start of the timer, the inadmissibility of the secondary start of the timer when it is already running (and this is possible!) And some other useful whistles, but this is a brute force to show an example.
A little bit about AnimationTimer: someone Mike wrote a very good article about
using the JavaFX AnimationTimer on his blog.
Mike believes that it was not a particularly good idea to call this class. After all, it can be used not only for animation: for measuring fps-rate, for detecting collisions, for calculating modeling steps, as the main loop in games, etc. Personally, he most often sees AnimationTimer applications that are not related to animation . AnimationTimer provides an extremely simple, but very flexible and useful feature. The timer allows you to define the method that will be called for each frame. What this method will do is not only unlimited, but, as mentioned above, may have nothing to do with animation. The only requirement is that this method should be fast enough, otherwise it will simply become a bottleneck in the system.
Of course, there is not the only way to solve this problem, but I liked this timer - it's simple and quite convenient. Who did not know him - please love and favor.