Tôi đang cố triển khai một số hoạt ảnh trong dự án của mình. Khi người dùng đang sử dụng ứng dụng, đôi khi người đó có hộp thoại Có/Không (Alert
) để xác nhận hoặc hộp thoại (Stage
) để nhập dữ liệu (và nhấn nút lưu). Sau sự kiện này, thông thường tôi sẽ hiển thị một số Alert
khác với "Thành công" (nếu thành công).Tại sao có sự khác biệt lớn về hiệu suất giữa quá trình chuyển đổi trên Alert và Stage?
Bây giờ, để loại bỏ một số cửa sổ/màn hình/cửa sổ bật lên "vô dụng", tôi muốn giảm thiểu số Alert
hoặc Stage
xuống góc dưới bên trái của màn hình, nơi sẽ xuất hiện thông báo "Thành công" trong khoảng 3 giây trong thanh trạng thái. Tôi đã thực hiện thành công này nhưng tôi nhận thấy sự khác biệt hiệu suất rất lớn giữa hoạt ảnh trên Alert
và hoạt ảnh trên Stage
.
Alert
dường như hoạt hình rất mượt mà, trong khi Stage
thực sự rất là lộn xộn (thậm chí trên máy tính tốt). Tôi đã đọc về bộ nhớ đệm và tìm kiếm các câu hỏi liên quan, nhưng không có nhiều hiệu ứng hoặc giải pháp. Tôi đã cố gắng tạo ví dụ JavaFX (Maven) (dựa trên một số ví dụ khác mà tôi đã tìm thấy) mà bạn có thể tìm thấy bên dưới.
Bạn sẽ thấy, khi bạn nhấn Hiện cảnh báo nút, và nhấn Có trong Alert
cửa sổ, các Alert
sẽ diễn ra suôn sẻ đến góc dưới bên trái của màn hình. Khi bạn nhấn nút Hiển thị nút và nhấn nút Đóng trong giai đoạn mới mở, hoạt ảnh sẽ biến mất nhiều hơn so với Alert
.
Tôi có thể làm gì để làm mượt hoạt hình của sân khấu không? Tôi cũng đã cố gắng để đặt AnchorPane hàng đầu vô hình để xem nếu có bất kỳ cải tiến hiệu suất, nhưng nó đã được chính xác như nhau.
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>
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>
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
}
}
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
}
}
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);
}
}
Cảm ơn bạn! Lạ lùng rằng sự phai màu có hiệu suất kém như vậy. – Perneel