2016-07-11 14 views
6

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 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); 
    } 
} 

Trả lời

1

Sự khác biệt duy nhất là keyFrameOpacity hoạt hình trong trường hợp của sân khấu. Nếu bạn xóa nó, hoạt ảnh sân khấu sẽ mượt mà như đối với hộp thoại cảnh báo. Điều thú vị là, hình ảnh động chỉ bị lag khi sử dụng thay đổi độ mờ với tỷ lệ. Thiết lập stage.setScene(null) trước khi timeline.play() cũng làm cho hoạt hình trơn tru, nhưng nó trông không tốt lắm.
Tôi không quen thuộc với nội tuyến thời gian JavaFx và cơ chế xung của nó, nhưng tôi đã tìm thấy 2 giải pháp.Một là để xử lý thay đổi tỷ lệ và độ mờ trong các giai đoạn khác nhau:

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(); 

Thứ hai là sử dụng chuỗi riêng để cập nhật tất cả các giá trị. Một cái gì đó như thế này:

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(); 
+0

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

Các vấn đề liên quan