我正在尝试在我的项目中实现一些动画.当用户使用该应用程序时,有时他或她得到是/否对话框(Alert)用于确认或对话框(Stage)输入数据(并按下保存按钮).事件发生后,通常我会向另一个人展示Alert"成功"(如果成功的话).
现在,为了消灭一堆额外的"无用"窗口/屏幕/弹出窗口,我想尽量减少Alert或Stage到哪里会有一个状态栏出现约3秒的"成功"的消息在屏幕的左下角.我已成功实现了这一点,但我注意到动画Alert和动画之间存在巨大的性能差异Stage.
Alert似乎动画非常流畅,而Stage实际上非常不稳定(甚至在一台好的电脑上).我已阅读有关缓存和搜索相关问题,但没有太多影响或解决方案.我尝试制作JavaFX(Maven)示例(基于我发现的其他一些示例),您可以在下面找到它们.
您将看到,当您按下显示提醒按钮,然后在窗口中按是Alert,Alert将顺利进入屏幕的左下角.当您按下" 显示节点"按钮,然后在新打开的舞台中按" 关闭"按钮时,与动画相比,动画会变得更加不连贯Alert.
我能做些什么来平滑舞台的动画吗?我还试图让顶级的AnchorPane看不见,看看是否有任何性能提升,但它完全相同.
Scene.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40" fx:controller="proj.mavenproject1.FXMLController">
<children>
<Button fx:id="button" layoutX="52.0" layoutY="90.0" onAction="#handleButtonAction" text="Show Alert" />
<Label fx:id="label" layoutX="126" layoutY="120" minHeight="16" minWidth="69" />
<Button fx:id="button1" layoutX="217.0" layoutY="90.0" onAction="#handleButtonAction2" text="Show Node" />
</children>
</AnchorPane>
Run Code Online (Sandbox Code Playgroud)
testNode.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40" fx:controller="proj.mavenproject1.TestNodeController">
<children>
<Button layoutX="262.0" layoutY="188.0" mnemonicParsing="false" onAction="#handleButtonAction" text="Close node" />
</children>
</AnchorPane>
Run Code Online (Sandbox Code Playgroud)
FXMLController.java:
package proj.mavenproject1;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
public class FXMLController implements Initializable {
@FXML
private Label label;
@FXML
private void handleButtonAction(ActionEvent event) {
Utilities.showYesNo("test", "this to test the closing animation of an alert", true);
System.out.println("You clicked me!");
label.setText("Hello World!");
}
@FXML
private void handleButtonAction2(ActionEvent event) {
try {
URL url = getClass().getResource("/fxml/testNode.fxml");
Utilities.showDialog(url);
} catch (IOException ex) {
Logger.getLogger(FXMLController.class.getName()).log(Level.SEVERE, null, ex);
}
}
@Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
Run Code Online (Sandbox Code Playgroud)
TestNodeController.java:
package proj.mavenproject1;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
public class TestNodeController implements Initializable {
@FXML
private void handleButtonAction(ActionEvent event) {
Utilities.closeStage(event, true);
}
/**
* Initializes the controller class.
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
Run Code Online (Sandbox Code Playgroud)
Utilities.java:
package proj.mavenproject1;
import java.io.IOException;
import java.net.URL;
import java.util.Optional;
import java.util.ResourceBundle;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.beans.value.WritableValue;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.CacheHint;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.DialogEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBoxBuilder;
import javafx.stage.Modality;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.WindowEvent;
import javafx.util.Duration;
public class Utilities {
public static boolean showYesNo(String title, String content, boolean animation) {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle(title);
alert.setHeaderText(null);
alert.setContentText(content);
alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO);
alert.setOnCloseRequest((DialogEvent we) -> {
if (animation) {
minimizeAlert(alert, animation);
we.consume();
}
});
Optional<ButtonType> result = alert.showAndWait();
return result.get() == ButtonType.YES;
}
public static void showDialog(URL url) throws IOException {
final Stage myDialog = new Stage();
myDialog.initStyle(StageStyle.UTILITY);
myDialog.initModality(Modality.APPLICATION_MODAL);
Node n = (Node) FXMLLoader.load(url);
Scene myDialogScene = new Scene(VBoxBuilder.create().children(n).alignment(Pos.CENTER).padding(new Insets(0)).build());
myDialog.setScene(myDialogScene);
myDialog.showAndWait();
}
private static void minimizeNode(Scene scene, boolean animation) {
final int MILLIS = 750;
//Node src = (Node) event.getSource();
AnchorPane rootPane = (AnchorPane) scene.lookup("#rootPane");
final Stage stage = (Stage) scene.getWindow();
//animation = false; //TODO: check if this thing slows down the program, seems like context menu slows down because of it
if (animation) {
WritableValue<Double> writableHeight = new WritableValue<Double>() {
@Override
public Double getValue() {
return stage.getHeight();
}
@Override
public void setValue(Double value) {
stage.setHeight(value);
}
};
WritableValue<Double> writableWidth = new WritableValue<Double>() {
@Override
public Double getValue() {
return stage.getWidth();
}
@Override
public void setValue(Double value) {
stage.setWidth(value);
}
};
WritableValue<Double> writableOpacity = new WritableValue<Double>() {
@Override
public Double getValue() {
return stage.getOpacity();
}
@Override
public void setValue(Double value) {
stage.setOpacity(value);
}
};
EventHandler onFinished = new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
stage.close();
}
};
double currentX = stage.getX();
double currentY = stage.getY();
DoubleProperty x = new SimpleDoubleProperty(currentX);
DoubleProperty y = new SimpleDoubleProperty(currentY);
x.addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
stage.setX(newValue.doubleValue());
}
});
y.addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
stage.setY(newValue.doubleValue());
}
});
KeyFrame keyFrameMove = new KeyFrame(Duration.millis(MILLIS), onFinished, new KeyValue(x, 0d), new KeyValue(y, Screen.getPrimary().getBounds().getMaxY() - 25));
KeyFrame keyFrameScale = new KeyFrame(Duration.millis(MILLIS), new KeyValue(writableWidth, 0d), new KeyValue(writableHeight, 0d));
KeyFrame keyFrameOpacity = new KeyFrame(Duration.millis(MILLIS), new KeyValue(writableOpacity, 0d));
Timeline timeline = new Timeline(keyFrameMove, keyFrameScale, keyFrameOpacity);
if (rootPane != null) {
rootPane.setVisible(false);
//rootPane.getChildren().clear();
}
timeline.play();
} else {
stage.close();
}
}
public static void minimizeAlert(Alert alert, boolean animation) {
final int MILLIS = 750;
if (animation) {
WritableValue<Double> writableHeight = new WritableValue<Double>() {
@Override
public Double getValue() {
return alert.getHeight();
}
@Override
public void setValue(Double value) {
alert.setHeight(value);
}
};
WritableValue<Double> writableWidth = new WritableValue<Double>() {
@Override
public Double getValue() {
return alert.getWidth();
}
@Override
public void setValue(Double value) {
alert.setWidth(value);
}
};
EventHandler onFinished = new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
alert.setOnCloseRequest(null);
alert.close();
}
};
double currentX = alert.getX();
double currentY = alert.getY();
DoubleProperty x = new SimpleDoubleProperty(currentX);
DoubleProperty y = new SimpleDoubleProperty(currentY);
x.addListener((obs, oldX, newX) -> alert.setX(newX.doubleValue()));
y.addListener((obs, oldY, newY) -> alert.setY(newY.doubleValue()));
KeyFrame keyFrameMove = new KeyFrame(Duration.millis(MILLIS), onFinished, new KeyValue(x, 0d), new KeyValue(y, Screen.getPrimary().getBounds().getMaxY() - 25));
KeyFrame keyFrameScale = new KeyFrame(Duration.millis(MILLIS), new KeyValue(writableWidth, 0d), new KeyValue(writableHeight, 0d));
Timeline timeline = new Timeline(keyFrameMove, keyFrameScale);
timeline.play();
} else {
alert.close();
}
}
public static void closeStage(Event event, boolean animation) {
Node src = (Node) event.getSource();
src.setCache(true);
src.setCacheHint(CacheHint.SPEED);
minimizeNode(src.getScene(), animation);
}
}
Run Code Online (Sandbox Code Playgroud)
唯一的区别是keyFrameOpacity舞台上的动画。如果将其删除,舞台动画将与警报对话框一样流畅。但有趣的是,仅当使用不透明度变化和缩放时动画才会出现延迟。stage.setScene(null)之前的设置timeline.play()也可以让动画流畅,但是看起来不太好。
我不太熟悉 JavaFx 时间轴内部结构及其脉冲机制,但我找到了 2 个解决方案。一是处理不同阶段的缩放和不透明度变化:
double currentWidth = stage.getWidth();
double currentHeight = stage.getHeight();
WritableValue<Double> writableValue = new WritableValue<Double>() {
private Double internal = 1.0;
private boolean flag = true;
@Override
public Double getValue() {
return internal;
}
@Override
public void setValue(Double value) {
if(flag) {
stage.setWidth(currentWidth * value);
stage.setHeight(currentHeight * value);
} else {
stage.setOpacity(value);
}
internal = value;
flag = !flag;
}
};
KeyFrame keyFrameMove = new KeyFrame(Duration.millis(MILLIS), onFinished, new KeyValue(x, 0d), new KeyValue(y, Screen.getPrimary().getBounds().getMaxY() - 25));
KeyFrame keyFrame = new KeyFrame(Duration.millis(MILLIS), new KeyValue(writableValue, 0d));
Timeline timeline = new Timeline(keyFrame, keyFrameMove);
timeline.play();
Run Code Online (Sandbox Code Playgroud)
第二种是使用单独的线程来更新所有值。像这样的东西:
double currentX = stage.getX();
double currentY = stage.getY();
double currentWidth = stage.getWidth();
double currentHeight = stage.getHeight();
new Thread(()->{
long initial = System.currentTimeMillis();
while(true) {
long current = System.currentTimeMillis();
long delta = current - initial;
if(delta > MILLIS) {
break;
}
double prc = 1 - delta/(double)MILLIS;
Platform.runLater(()->{
stage.setX(currentX*prc);
stage.setY(currentY*prc+(1-prc)*(Screen.getPrimary().getBounds().getMaxY() - 25));
stage.setWidth(currentWidth*prc);
stage.setHeight(currentHeight*prc);
stage.setOpacity(prc);
});
try {
Thread.sleep(1000/60);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Platform.runLater(()-> stage.close());
}).start();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
485 次 |
| 最近记录: |