2016-07-11 1 views
6

私は私のプロジェクトでいくつかのアニメーションを実装しようとしています。ユーザがアプリケーションを使用しているときに、確認のためにYes/Noダイアログ(Alert)を入力するか、またはデータを入力するためのダイアログボックス(Stage)を取得して保存ボタンを押します。イベントの後、通常、私は別のAlertを "成功"と表示します(成功すれば成功します)。今AlertとStageの移行のパフォーマンスに大きな違いがあるのはなぜですか?

、余分な「役に立たない」窓/画面/ポップアップの束を排除するために、私は約3秒間「成功」のメッセージが表示されます、画面の左下隅にAlertまたはStageを最小限にしたかったですステータスバーに表示されます。私はこれをうまく実装しましたが、AlertのアニメーションとStageのアニメーションのパフォーマンスに大きな違いがあることがわかりました。

Alertは非常にスムーズに動くようですが、Stageは実際には非常に途切れています(良いPCでも)。私はキャッシングについて読んだことがあり、関連する質問を検索しましたが、大きな効果や解決策はありません。私はJavaFX(Maven)の例を(私が見つけたいくつかの他の例に基づいて)作成しようとしましたが、これは以下で見つけることができます。あなたは表示、アラートボタンを押すと、参照、およびAlertウィンドウではいを押します

Alertは、画面の左下隅にスムーズに行きます。 ノードの表示ボタンを押して、新しく開かれた段階で閉じるボタンを押すと、アニメーションはAlertと比べてはるかに不安定になります。

ステージのアニメーションをスムーズにするためにできることはありますか?パフォーマンスの向上が見られるかどうかを確認するために、アンカーペインを非表示にしましたが、まったく同じでした。

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

答えて

1

唯一の違いは、ステージの場合にkeyFrameOpacityアニメーションです。それを削除すると、ステージのアニメーションはアラートダイアログほど滑らかになります。しかし、興味深いのは、不透明度の変更をスケーリングで使用する場合のみアニメーションが遅くなることです。 stage.setScene(null)timeline.play()の前に設定すると、アニメーションもスムーズになりますが、見栄えが良くありません。
私はJavaFxのタイムラインの内部構造に精通していませんし、パルスメカニクスもありますが、私は2つの解決策を見つけました。1つは、異なるフェーズでスケーリングと不透明度の変更を処理することです。

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

2つ目は、別のスレッドを使用してすべての値を更新することです。このようなもの:

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

ありがとうございます!フェージングがそのようなパフォーマンスが悪いというのは不思議です。 – Perneel

関連する問題