如何在JavaFX中对话框面板中对齐确定按钮?

8

我想对话框中的“确定”按钮居中对齐,即位置居中。我尝试了下面的代码,但它没有起作用。

Dialog dialog = new Dialog();
DialogPane dialogPane = dialog.getDialogPane();
dialogPane.setStyle("-fx-background-color: #fff;");

        // Set the button types.
ButtonType okButtonType = new ButtonType("Ok", ButtonBar.ButtonData.OK_DONE);
ButtonType cancelButtonType = new ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE);
dialog.getDialogPane().getButtonTypes().addAll(okButtonType, cancelButtonType);
dialogPane.lookupButton(cancelButtonType).setVisible(false);

        // Testing
Button okButton = (Button) dialog.getDialogPane().lookupButton(okButtonType);
okButton.setAlignment(Pos.CENTER);
        // End Testing

dialog.showAndWait();

确定按钮通常位于对话框的右下角,这是其默认位置,但我希望它显示在对话框的底部中心。 - Vikas Singh
请使用 [编辑] 链接将额外信息添加到问题中。谢谢!(并且请将代码编写为完整程序 - 这有助于人们复制您所做的内容,并为您提供良好的答案)。查看 [帮助] 了解如何编写 [mcve]。 - Toby Speight
6个回答

5
在对话框的ButtonBar中居中按钮实际上是非常难以以一种不偏离主题的方式实现的。下面是我能想到的最佳解决方案。它依赖于对button容器的HBox进行动态CSS查找,然后在右侧添加一个间隔区域来将按钮推向左侧(默认的ButtonSkin实现已经在左侧放置了一个隐式间隔,将按钮推向右侧,我使用ScenicView确定了这一点)。左侧和右侧间隔的组合最终将按钮对齐在中心。该解决方案还覆盖了ButtonBar创建,以阻止ButtonSkin内部重新排序和执行其他按钮布局,因为当它这样做时,你不能真正可靠地自定义布局。

centered button

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

import java.util.Optional;

public class CenteredDialogButtons extends Application {
    @Override
    public void start(Stage stage) {
        Button show = new Button("Show Dialog");

        Dialog<ButtonType> dialog = new Dialog<>();
        DialogPane dialogPane = new DialogPane() {
            @Override
            protected Node createButtonBar() {
                ButtonBar buttonBar = (ButtonBar) super.createButtonBar();
                buttonBar.setButtonOrder(ButtonBar.BUTTON_ORDER_NONE);

                return buttonBar;
            }
        };
        dialog.setDialogPane(dialogPane);
        dialogPane.getButtonTypes().addAll(ButtonType.OK);
        dialogPane.setContentText("Centered Button");

        Region spacer = new Region();
        ButtonBar.setButtonData(spacer, ButtonBar.ButtonData.BIG_GAP);
        HBox.setHgrow(spacer, Priority.ALWAYS);
        dialogPane.applyCss();
        HBox hbox = (HBox) dialogPane.lookup(".container");
        hbox.getChildren().add(spacer);

        show.setOnAction(e -> {
            Optional<ButtonType> result = dialog.showAndWait();
            if (result.isPresent() && result.get() == ButtonType.OK) {
                System.out.println("OK");
            }
        });

        StackPane layout = new StackPane(
                show
        );
        layout.setPadding(new Insets(50));

        Scene scene = new Scene(layout);

        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

我不太喜欢这种解决方案的原因是动态CSS查找有点违反API封装,因为JavaFX场景图控件(如按钮栏)的CSS结构实际上并不是它们的公共API的一部分。然而,我认为在JavaFX 8的现有公共API和默认ButtonBar皮肤中,无法获得居中的按钮。

另一种方法是为与对话框相关联的ButtonBar创建自定义皮肤,但这种方法非常困难,我不建议在此任务中使用。

基本上,从所有这些中得出的结论是,只要可以,请保留对话框的默认按钮布局和顺序,而不是尝试自定义对话框按钮布局。如果您确实想要完全自定义布局,例如按钮放置位置之类的内容,那么您可能最好只需通过子类化Stage来创建自己的自定义对话框类,而不是基于内置对话框类构建自己的自定义对话框实现。

相关但略有不同的信息在以下内容中:


4

我尝试将“Alert”中的OK按钮居中,但我不确定这是否是一个缺陷或特性(Java8)。然而,通过设置新的按钮,仍然可以将单个按钮居中:

alert.getButtonTypes().set(0, new ButtonType("OK", ButtonBar.ButtonData.LEFT));

只要存在一个带有 ButtonData.LEFT 的按钮,它就会位于按钮面板的中心。显然,对于多个按钮的面板,这种解决方案无法工作,但它可能有助于定位单个确认按钮。

这真的很奇怪,因为Lorenzo的解决方案对于2个按钮运行良好,但对于3个按钮则不行,所以在添加按钮后,我按照这个确切的方法进行了操作,现在所有3个按钮都居中了。编辑:然而,一旦我更改按钮数据,它们就会重新排列并偏离中心。 - Dahlin

2

在您的代码中添加此方法,并在需要对话框或警报中对齐按钮时调用它:

将此方法添加到您的代码中,并在需要对齐对话框或警报中的按钮时调用它:

    private void centerButtons(DialogPane dialogPane) {
      Region spacer = new Region();
      ButtonBar.setButtonData(spacer, ButtonBar.ButtonData.BIG_GAP);
      HBox.setHgrow(spacer, Priority.ALWAYS);
      dialogPane.applyCss();
      HBox hboxDialogPane = (HBox) dialogPane.lookup(".container");
      hboxDialogPane.getChildren().add(spacer);
   }

以这种方式调用:centerButtons(dialog.getDialogPane);,将按钮居中。最初的回答。

这个只适用于不超过2个按钮吗? - Dahlin

1

这是一种hack方法,但你可以像这样做:

okButton.translateXProperty().bind(okButton.prefWidthProperty().divide(-2));

对话框面板是水平居中的,因此减去okButton的一半宽度就可以解决问题。 但我认为这是一个非常不好的解决方案;-)

1

居中一个或多个按钮实际上非常容易(尽管可能更简单和更好地记录)。这一切都归结于传递给ButtonBar的“神奇”buttonOrder字符串,该字符串通过setButonOrder(String)方法设置。

但首先,是必要的代码片段:

@Override public void start(final Stage stage) throws Exception {
    // …
    var button = new ButtonType("Roger!", ButtonBar.ButtonData.OK_DONE);
    var dialog = new Dialog<>();
    dialog.setDialogPane(new DialogPane() {
        @Override protected Node createButtonBar() {
            var buttonBar = (ButtonBar)super.createButtonBar();
            buttonBar.setButtonOrder("L_O_R");
            return buttonBar;
        }
    });
    dialog.getButtonTypes.addAll(button);
    var pane = dialog.getDialogPane();
    // Add whatever is needed to the pane here…
    // …
    dialog.showAndWait();
}
解释:包装start(Stage)方法当然是Application的开始方法。首先我创建了一个OK/DONE类型的按钮,然后是一个Dialog实例。到目前为止还没有什么不寻常的。

现在:通常,在对话框创建后,我们只需将按钮和内容添加到对话框的面板中。但是在这里,我实际上是先创建并设置面板,稍微修改了一下。

正如您所看到的,我创建了一个扩展DialogPane的匿名类,并立即将其设置为对话框的面板。在这里需要做的就是覆盖一个单一的方法createButtonBar,仅针对一个关键的期望目的:更改按钮栏布局其子项(即按钮)的方式。在这种特殊情况下,我使用了字符串“'L_O_R'”(不是LOTR;-))。

那么它是什么?虽然没有明确记录,但如果查看ButtonBar Javadoc,您会注意到以下几行:

Windows: L_E+U+FBXI_YNOCAH_R

Mac OS: L_HE+U+FBIX_NCYOA_R

Linux: L_HE+UNYACBXIO_R

并附有在这些操作系统上布局的按钮图片。每个字母代表一个按钮类型,根据 ButtonData.OK_DONEOK_DONE 类型被分配为字母 O,而 LR 分别表示左侧和右侧位置。下划线 _ 是一个间隔符或空格。因此,对于字符串 L_O_R,我们的意思是:将标记为 LEFT 的按钮放在左侧,标记为 RIGHT 的按钮放在右侧,标记为 OK_DONE 的按钮放在中间(因为两个相邻的间隔符)。当然,您可以混合其他类型,以满足您的需要,例如您可以传递 L_YXOCN_R 或其他任何您需要的内容。

如果DialogsetButtonOrder(String)方法,实现所需的效果会简单得多。无论如何,这比创建人工间隔或搞乱水平转换要简单得多。

1

基于 @ManuelSeiche 的回答,这里是计算到中心点的精确距离的方法:

@FXML private Dialog<ButtonType> dialog;
@FXML private ButtonType btClose;

@FXML
private void initialize()
{
    dialog.setOnShown(event ->
    {
        Platform.runLater(() ->
        {
            Button btnClose = (Button) dialog.getDialogPane().lookupButton(btClose);
            HBox hBox = (HBox) btnClose.getParent();
            double translateAmount = hBox.getWidth() / 2.0 - btnClose.getWidth() / 2.0 - hBox.getPadding().getLeft();
            btnClose.translateXProperty().set(-translateAmount);
        });
    });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接