我希望应用程序左侧的菜单在用户选择切换按钮时滑入框架,并在未选择切换按钮时再次退出。
我实现了我想要的一切,但我遇到的问题是布局不随动画而改变。
当菜单超出框架(你看不到它)时,布局就像它在框架中一样。
这是我菜单的初始化:
modulesBar = new ModulesBar();
modulesBar.setTranslateX(-250);
GridPane.setMargin(modulesBar, new Insets(10, 0, 0, 5));
GridPane.setRowSpan(modulesBar, GridPane.REMAINING);
root.add(modulesBar, 0, 1);
这是切换方法:
Timeline modulesBarAnimation = new Timeline();
if (mainBar.getModulesButton().isSelected()) {
modulesBarAnimation.getKeyFrames().add(new KeyFrame(Duration.millis(250), new KeyValue(modulesBar.translateXProperty(), 0)));
} else {
modulesBarAnimation.getKeyFrames().add(new KeyFrame(Duration.millis(250), new KeyValue(modulesBar.translateXProperty(), -250)));
}
modulesBarAnimation.playFromStart();
我也尝试使用setLayoutX()
,但这并没有将菜单移出框架......
如何通过翻译更改布局?最好的是与动画相结合的平滑变化。
答案 0 :(得分:2)
布局中不考虑Node
上的任何变换。 (这是故意的;除了其他功能之外,它允许您修改Node
相对于其通常布局位置的位置。)您的动画通过修改标准平移变换来工作;因此,这不会改变布局。
为了获得所需的效果,您需要动画来修改用于计算布局的属性。例如,由于您似乎使用的是GridPane
,因此您可以将ColumnConstraints
应用于GridPane
并修改其中一列的prefWidth
。
SSCCE:
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ObservableBooleanValue;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class AnimatingGridPane extends Application {
@Override
public void start(Stage primaryStage) {
GridPane root = new GridPane();
ColumnConstraints leftCol = new ColumnConstraints();
leftCol.setPrefWidth(100);
ColumnConstraints rightCol = new ColumnConstraints();
root.getColumnConstraints().addAll(leftCol, rightCol);
ObservableBooleanValue expanded = leftCol.prefWidthProperty().greaterThan(0);
Button expandButton = new Button("");
expandButton.textProperty().bind(
Bindings.when(expanded).then("Hide options").otherwise("Show options"));
expandButton.setOnAction(event -> {
KeyFrame start ;
KeyFrame end ;
if (expanded.get()) {
start = new KeyFrame(Duration.ZERO, new KeyValue(leftCol.prefWidthProperty(), 100));
end = new KeyFrame(Duration.millis(250), new KeyValue(leftCol.prefWidthProperty(), 0));
} else {
start = new KeyFrame(Duration.ZERO, new KeyValue(leftCol.prefWidthProperty(), 0));
end = new KeyFrame(Duration.millis(250) ,new KeyValue(leftCol.prefWidthProperty(), 100));
}
Timeline timeline = new Timeline(start, end);
timeline.play();
});
Node content = createContent();
root.setVgap(5);
root.add(content, 1, 0);
Region optionsPane = createOptionsPane();
root.add(optionsPane, 0, 0);
optionsPane.visibleProperty().bind(expanded);
optionsPane.setPadding(new Insets(10));
GridPane.setHalignment(expandButton, HPos.CENTER);
root.add(expandButton, 1, 1);
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private GridPane createContent() {
GridPane contentPane = new GridPane();
contentPane.setHgap(5);
contentPane.setVgap(5);
contentPane.addRow(0, new Label("First Name:"), new TextField());
contentPane.addRow(1, new Label("Last Name:"), new TextField());
contentPane.addRow(2, new Label("Email:"), new TextField());
ColumnConstraints leftCol = new ColumnConstraints();
leftCol.setHgrow(Priority.NEVER);
leftCol.setHalignment(HPos.RIGHT);
ColumnConstraints rightCol = new ColumnConstraints();
rightCol.setHgrow(Priority.ALWAYS);
rightCol.setFillWidth(true);
contentPane.getColumnConstraints().addAll(leftCol, rightCol);
return contentPane;
}
private Region createOptionsPane() {
VBox optionsPane = new VBox(10,
createLabel("New"),
createLabel("Open"),
createLabel("Edit"),
createLabel("Delete")
);
return optionsPane;
}
private Label createLabel(String text) {
Label label = new Label(text);
label.setEllipsisString("");
return label ;
}
public static void main(String[] args) {
launch(args);
}
}
答案 1 :(得分:0)
翻译不会影响布局,您必须使用影响布局的方法。
(在这种情况下)使用modulesBar
更改setPrefWidth(...)
的宽度会影响布局并获得所需的结果。
菜单初始化的代码如下:
modulesBar = new ModulesBar();
modulesBar.setPrefWidth(0);
modulesBar.setMinWidth(0);
modulesBar.setTranslateX(-200);
GridPane.setMargin(modulesBar, new Insets(10, 0, 0, 5));
root.add(modulesBar, 0, 1);
更改宽度不会使setTranslateX()
变得多余,仍然需要让按钮移出屏幕,否则它们只会调整到最小尺寸。
动画方法的代码如下:
private void switchModulesBar() {
// Slides in the modules bar when "Modules"-toggle button is pressed
if (mainBar.getModulesButton().isSelected()) {
Animation modulesBarAnimation = new Transition() {
{
setCycleDuration(Duration.millis(250));
}
@Override
protected void interpolate(double frac) {
final double curWidth = 200 * frac;
modulesBar.setPrefWidth(curWidth);
modulesBar.setTranslateX(-200 + curWidth);
}
};
modulesBarAnimation.play();
} else { // Slides out the modules bar
Animation modulesBarAnimation = new Transition() {
{
setCycleDuration(Duration.millis(250));
}
@Override
protected void interpolate(double frac) {
final double curWidth = 200 * (1.0 - frac);
modulesBar.setPrefWidth(curWidth);
modulesBar.setTranslateX(-200 + curWidth);
}
};
modulesBarAnimation.play();
}
}