Статьи

Использование JavaFX AnimationTimer

Оглядываясь назад, вероятно, было бы не очень хорошей идеей дать AnimationTimer его имя, поскольку его можно использовать не только для анимации: измерения частоты кадров, определения столкновений, вычисления шагов симуляции, основного цикла игры. и т.д. На самом деле, большую часть времени, когда я видел AnimationTimer в действии, он вообще не был связан с анимацией. Тем не менее, есть случаи, когда вы хотите рассмотреть возможность использования AnimationTimer для вашей анимации. Этот пост объяснит класс и покажет пример, где AnimationTimer используется для вычисления анимации.
AnimationTimer предоставляет чрезвычайно простую, но очень полезную и гибкую функцию. Позволяет указать метод, который будет вызываться в каждом кадре. То, для чего используется этот метод, не ограничено и, как уже упоминалось, не имеет ничего общего с анимацией. Единственное требование состоит в том, что он должен возвращаться быстро, потому что в противном случае он может легко стать узким местом системы.
Чтобы использовать его, разработчик должен расширить AnimationTimer и реализовать абстрактный метод handle (). Это метод, который будет вызываться в каждом кадре, пока активен AnimationTimer. Один параметр передается в handle (). Он содержит текущее время в наносекундах, такое же, как и при вызове System.nanoTime ().
Почему следует использовать переданное значение вместо вызова System.nanoTime () или его младшего брата System.currentTimeMillis ()? Есть несколько причин, но наиболее важной, вероятно, является то, что это облегчает вашу жизнь во время отладки. Если вы когда-нибудь пытались отладить код, который зависел от этих двух методов, вы знаете, что вы в основном испорчены. Но среда выполнения JavaFX переходит в состояние паузы, когда она ожидает выполнения следующего шага во время отладки, и внутренние часы не работают во время этой паузы. Другими словами, независимо от того, подождете ли вы две секунды или два часа, прежде чем возобновите остановленную программу во время отладки, приращение параметра будет примерно одинаковым!
AnimationTimer имеет два метода start () и stop () для его активации и деактивации. Если вы переопределяете их, важно, чтобы вы вызывали эти методы в суперклассе.
Animation API поставляется с множеством многофункциональных классов, которые делают определение анимации очень простым. Существуют предопределенные классы переходов, можно определить анимацию на основе ключевых кадров с помощью временной шкалы, и можно даже легко написать пользовательский переход. Но в каких случаях имеет смысл использовать AnimationTimer? — Почти всегда вы хотите использовать один из стандартных классов. Но если вы хотите указать много простых анимаций, лучше использовать AnimationTimer.
Богатство возможностей стандартных классов анимации имеет свою цену. Каждая отдельная анимация требует отслеживания целого набора переменных — переменных, которые вам часто не нужны для простых анимаций. Кроме того, эти классы оптимизированы для скорости, а не для небольшого объема памяти. Некоторые из переменных хранятся дважды, один раз в формате, который требуется общедоступному API, и один раз в формате, который помогает быстрее вычислять во время воспроизведения.
Ниже приведен простой пример, показывающий звездное поле. Он оживляет тысячи прямоугольников, летящих от центра к внешним краям. Использование AnimationTimer позволяет хранить только те значения, которые необходимы. Вычисление чрезвычайно просто по сравнению с вычислением на временной шкале, например, потому что не нужно рассматривать дополнительные функции (петли, скорость анимации, направление и т. Д.).
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package fxsandbox;
 
import java.util.Random;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
 
public class FXSandbox extends Application {
     
    private static final int STAR_COUNT = 20000;
     
    private final Rectangle[] nodes = new Rectangle[STAR_COUNT];
    private final double[] angles = new double[STAR_COUNT];
    private final long[] start = new long[STAR_COUNT];
     
    private final Random random = new Random();
 
    @Override
    public void start(final Stage primaryStage) {
        for (int i=0; i<STAR_COUNT; i++) {
            nodes[i] = new Rectangle(1, 1, Color.WHITE);
            angles[i] = 2.0 * Math.PI * random.nextDouble();
            start[i] = random.nextInt(2000000000);
        }
        final Scene scene = new Scene(new Group(nodes), 800, 600, Color.BLACK);
        primaryStage.setScene(scene);
        primaryStage.show();
         
        new AnimationTimer() {
            @Override
            public void handle(long now) {
                final double width = 0.5 * primaryStage.getWidth();
                final double height = 0.5 * primaryStage.getHeight();
                final double radius = Math.sqrt(2) * Math.max(width, height);
                for (int i=0; i<STAR_COUNT; i++) {
                    final Node node = nodes[i];
                    final double angle = angles[i];
                    final long t = (now - start[i]) % 2000000000;
                    final double d = t * radius / 2000000000.0;
                    node.setTranslateX(Math.cos(angle) * d + width);
                    node.setTranslateY(Math.sin(angle) * d + height);
                }
            }
        }.start();
    }
     
    public static void main(String[] args) {
        launch(args);
    }
     
}

Ссылка: Использование JavaFX AnimationTimer от нашего партнера по JCG Майкла Хайнрихса в блоге Mike’s Blog .