Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Анимация: переходы и Timeline

Анимация в JavaFX — это плавное изменение свойства узла во времени: прозрачности, сдвига, масштаба. Вы задаёте начальное и конечное значение и длительность, а JavaFX сам пересчитывает промежуточные значения на каждом кадре. Классы лежат в пакете javafx.animation, отдельная зависимость не нужна. В записной книжке анимация подскажет пользователю, что произошло: таблица плавно появится после загрузки, сообщение о новой заметке погаснет само, а поле с пустым заголовком «встряхнётся».

Переходы

Переход (transition) — готовая анимация одного свойства одного узла:

КлассЧто меняет
FadeTransitionПрозрачность opacity: 0 — невидим, 1 — непрозрачен
TranslateTransitionСдвиг translateX и translateY
ScaleTransitionМасштаб scaleX и scaleY
RotateTransitionУгол поворота

Длительность задаёт класс javafx.util.Duration: Duration.millis(600), Duration.seconds(2). Смотрите, чтобы IDE не подставила java.time.Duration — у него нет метода millis. Остальные классы урока импортируются из javafx.animation.

Плавное появление

Весь код этого урока — в MainController.java. В обработчике успешной загрузки из урока о фоновых задачах добавим появление таблицы:

// src/main/java/com/example/notes/MainController.java — в конце task.setOnSucceeded(...)
FadeTransition fade = new FadeTransition(Duration.millis(600), table);
fade.setFromValue(0);
fade.setToValue(1);
fade.play();

play() запускает анимацию и сразу возвращает управление — программа не ждёт её окончания. Запустите приложение: таблица с заметками проявится из пустоты за 0,6 секунды.

Исчезающее сообщение

Пусть после добавления заметки в строке состояния появляется «Заметка добавлена: …» и через две секунды гаснет. Один переход создадим в initialize() и будем запускать заново:

// src/main/java/com/example/notes/MainController.java — новое поле, конец initialize() и новый метод
private FadeTransition statusFade;
 
// в конце initialize():
statusFade = new FadeTransition(Duration.seconds(1), statusLabel);
statusFade.setDelay(Duration.seconds(2));
statusFade.setToValue(0);
 
private void showStatus(String text) {
    statusFade.stop();
    statusLabel.setText(text);
    statusLabel.setOpacity(1);
    statusFade.playFromStart();
}

setDelay откладывает начало, а без setFromValue переход стартует с текущей прозрачности. stop() и playFromStart() перезапускают анимацию, если заметки добавляют быстрее, чем гаснет сообщение. В конце addNote() после clearForm() вызовите showStatus("Заметка добавлена: " + title).

Встряхивание поля

Вместо предупреждения Alert из урока о диалогах встряхнём поле заголовка:

// src/main/java/com/example/notes/MainController.java — начало addNote() и новый метод
if (title.isEmpty()) {
    shake(titleField);
    return;
}
 
private void shake(Node node) {
    TranslateTransition shake = new TranslateTransition(Duration.millis(50), node);
    shake.setFromX(0);
    shake.setByX(8);
    shake.setCycleCount(6);
    shake.setAutoReverse(true);
    shake.play();
}

Один проход сдвигает поле на 8 пикселей вправо, setAutoReverse(true) возвращает его назад, и так 6 раз. Число повторов чётное, поэтому поле останавливается на месте. Введите в заголовок пробел и нажмите «Добавить» — поле дёрнется из стороны в сторону.

Timeline и KeyFrame

Когда нужно несколько свойств или несколько этапов, используют Timeline. Она состоит из ключевых кадров KeyFrame: момента времени и значений KeyValue, которых свойства должны достигнуть к этому моменту. Счётчик «Заметок: N» будет пульсировать при каждом изменении списка:

// src/main/java/com/example/notes/MainController.java — новый метод pulse()
private void pulse(Node node) {
    Timeline timeline = new Timeline(
            new KeyFrame(Duration.ZERO,
                    new KeyValue(node.scaleXProperty(), 1),
                    new KeyValue(node.scaleYProperty(), 1)),
            new KeyFrame(Duration.millis(150),
                    new KeyValue(node.scaleXProperty(), 1.3),
                    new KeyValue(node.scaleYProperty(), 1.3)),
            new KeyFrame(Duration.millis(300),
                    new KeyValue(node.scaleXProperty(), 1),
                    new KeyValue(node.scaleYProperty(), 1)));
    timeline.play();
}
// src/main/java/com/example/notes/MainController.java — в initialize(), после привязки countLabel
notes.addListener((ListChangeListener<Note>) change -> pulse(countLabel));

За 0,15 секунды надпись увеличивается до 130 %, ещё за 0,15 — возвращается. Добавьте или удалите заметку — счётчик «подпрыгнет». У KeyFrame бывает и обработчик: new KeyFrame(Duration.seconds(1), event -> ...) вместе с setCycleCount(Animation.INDEFINITE) выполняет код раз в секунду — так делают часы и таймеры.

МетодЧто делает
play(), pause(), stop()Запуск, пауза, остановка
playFromStart()Запуск с начала
setCycleCount(n)Сколько раз повторить; Animation.INDEFINITE — бесконечно
setAutoReverse(true)Каждый второй проход идёт обратно
setOnFinished(...)Код после окончания анимации

Частая ошибка

Из showStatus убирают строку statusLabel.setOpacity(1). Первое сообщение появляется и гаснет, а все следующие не видны: после первой анимации прозрачность осталась 0, новый текст записывается в невидимую надпись, и переход ведёт её от 0 к 0. Ошибки в терминале нет. Анимация меняет само свойство узла и не возвращает его обратно — восстанавливайте значение сами или задавайте setFromValue.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.