JavaFX(8)警报:不同的按钮大小

时间:2017-08-24 15:59:55

标签: javafx dialog javafx-8

考虑带有两个按钮的JavaFX(8)Alert对话框:

Alert alert = new Alert(AlertType.CONFIRMATION);

ButtonType bttYes = new ButtonType("Yes");
ButtonType bttNo = new ButtonType("No, continue without restart");

alert.getButtonTypes().clear();
alert.getButtonTypes().addAll(bttYes, bttNo);

alert.showAndWait();

生成的对话框有两个按钮,两个宽度相同,看起来很傻。

有没有办法将两个按钮调整为实际文字长度?

3 个答案:

答案 0 :(得分:2)

扩展Alert的{​​{1}}包裹DialogPaneDialog分为不同的部分,其中一部分是"按钮栏"。正如预期的那样,此部分是所有按钮的所在位置。按钮栏可以是任意DialogPane,但Node的默认实现使用恰当命名的ButtonBar控件。这在DialogPane.createButtonBar()强调我的)中记录:

  

子类可以重写此方法以提供按钮栏。请注意,通过重写此方法,开发人员必须承担多项职责:

     
      
  1. 开发人员必须立即遍历所有按钮类型,并依次为每个按钮类型调用createButton(ButtonType)。
  2.   
  3. 开发人员必须在按钮类型列表中添加一个侦听器,当此列表发生更改时,请根据需要更新按钮栏。
  4.   
  5. 同样,开发人员必须注意对可扩展内容属性的更改,添加和删除详细信息按钮(通过createDetailsButton()方法创建)。
  6.         

    此方法的默认实现创建并返回一个新的ButtonBar实例。

默认情况下,DialogPane会将所有按钮的大小调整为最宽的按钮的宽度。

  

统一按钮大小调整

     

默认情况下,所有按钮在ButtonBar中的大小均匀,这意味着所有按钮都采用最宽按钮的宽度。可以在每个按钮的基础上选择退出,但是使用布尔值false调用setButtonUniformSize(Node,boolean)方法。

     

如果某个按钮被排除在均匀尺寸之外,则不会将其从尺寸调整中排除,也不会从测量过程中排除,因此其尺寸不会影响为ButtonBar中的所有按钮计算的最大尺寸。

如上面的Javadoc所述,可以使用静态方法ButtonBar.setButtonUniformSize(Node, boolean)禁用此行为。只需使用ButtonBargetButtonTypes()循环按钮,并为每个按钮设置统一大小调整为lookupButton(ButtonType)

false

请注意,如果不使用默认设置,则需要事先配置Alert alert = ...; DialogPane pane = alert.getDialogPane(); pane.getButtonTypes().stream() .map(pane::lookupButton) .forEach(btn-> ButtonBar.setButtonUniformedSize(btn, false));

如果涉及自定义ButtonType,这将变为依赖于实现。例如,自定义DialogPane可能不使用DialogPane。但是,您的代码没有显示使用自定义ButtonBar的指示,因此上述解决方案应该可以正常工作。

答案 1 :(得分:1)

以下是自定义对话框的示例。一个纯代码和一个MCV。

  

Pure Code

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;

/**
 *
 * @author blj0011
 */
public class JavaFXApplication7 extends Application {

    @Override
    public void start(Stage primaryStage) {    
        Button button = new Button("press me");
        button.setOnAction((event)->{
            showDialog(primaryStage);                
        });


        VBox root = new VBox();        
        root.getChildren().add(button);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

    private Stage showDialog(Window parent)
    {
        final Stage dialog = new Stage();
        dialog.setTitle("Test Dialog");
        dialog.initOwner(parent);
        dialog.initStyle(StageStyle.DECORATED);                

        //Create Header
        Label lblHeaderText = new Label("Header Text Goes Here!");
        lblHeaderText.setFont(new Font("System", 16));
        lblHeaderText.setPadding(new Insets(0, 0, 0, 20));
        StackPane headerLabelContainer = new StackPane();
        headerLabelContainer.getChildren().add(lblHeaderText);

        ImageView ivHeaderImageView = new ImageView();
        ivHeaderImageView.setFitHeight(80);
        ivHeaderImageView.setFitWidth(80);

        HBox hBoxHeader = new HBox();
        hBoxHeader.setMinHeight(80);
        hBoxHeader.setMaxHeight(80);
        hBoxHeader.setStyle("-fx-background-color: #E4E4E4");
        hBoxHeader.getChildren().addAll(headerLabelContainer, ivHeaderImageView);

        //Create Body
        Label lblBodyText = new Label("Body Text Goes Here!");
        lblBodyText.setPadding(new Insets(0, 0, 0, 20));
        lblBodyText.wrapTextProperty().set(true);
        AnchorPane.setTopAnchor(lblBodyText, 0.0);
        AnchorPane.setBottomAnchor(lblBodyText, 0.0);
        AnchorPane.setLeftAnchor(lblBodyText, 0.0);
        AnchorPane.setRightAnchor(lblBodyText, 0.0);

        AnchorPane apBodyLabelContainer = new AnchorPane();
        apBodyLabelContainer.setMinHeight(130);
        apBodyLabelContainer.getChildren().add(lblBodyText);        

        Button yesButton = new Button("Yes");
        Button noButton = new Button("No, continue without restart");
        yesButton.setOnAction((aEvent)->{
            System.out.println("continue with restart!");
            dialog.close();
        });
        noButton.setOnAction((aEvent)->{
            System.out.println("continue without restart!");
            //code to restart
            dialog.close();
        });   

        HBox hBoxBodyFooter = new HBox();
        hBoxBodyFooter.setSpacing(10);
        hBoxBodyFooter.setPadding(new Insets(0, 20, 10, 0));
        hBoxBodyFooter.alignmentProperty().set(Pos.CENTER_RIGHT);
        hBoxBodyFooter.getChildren().addAll(yesButton, noButton);

        VBox vBoxBody = new VBox();
        vBoxBody.getChildren().addAll(apBodyLabelContainer, hBoxBodyFooter);

        VBox rootLayout = new VBox();
        rootLayout.getChildren().addAll(hBoxHeader, vBoxBody);


        Scene scene = new Scene(rootLayout, 400, 250);//You can adjust this!

        dialog.setScene(scene);
        dialog.showAndWait();

        return dialog;      
    }
}
  

模型 - 控制器 - 视图(更像是Controller-View)

主要

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

/**
 *
 * @author blj0011
 */
public class JavaFXApplication8 extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));

        Scene scene = new Scene(root);

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

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}

<强>控制器

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.ResourceBundle;
import java.util.stream.Collectors;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

/**
 *
 * @author blj0011
 */
public class FXMLDocumentController implements Initializable {

    @FXML
    private void handleButtonAction(ActionEvent event) {
        showDialog((Node)event.getSource());
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
    }    

    private Stage showDialog(Node sourceNode)//You can add String title and String headerText and String bodyText
    {
        Stage dialog = new Stage();
        try 
        {
            Parent rootDialog = FXMLLoader.load(getClass().getResource("test.fxml"));
            dialog.setTitle("Title of Alert");
            dialog.initOwner(sourceNode.getScene().getWindow());
            dialog.initStyle(StageStyle.UTILITY);
            dialog.setScene(new Scene(rootDialog));

            ArrayList<Node> childrenNodes = getAllNodes(rootDialog);
            Button yesButton = (Button)childrenNodes.stream().filter((node) -> (node instanceof Button && ((Button)node).getText().equals("Yes"))).collect(Collectors.toList()).get(0);//Find the yes button in the test fxml
            Button noButton = (Button)childrenNodes.stream().filter((node) -> (node instanceof Button && ((Button)node).getText().contains("No"))).collect(Collectors.toList()).get(0);//Fin the no button in the test fxml

            //Yes button event handler
            yesButton.setOnAction((aEvent)->{
                System.out.println("continue with restart!");
                dialog.close();
            });

            //No button event handler
            noButton.setOnAction((aEvent)->{
                System.out.println("continue without restart!");
                //code to restart
                dialog.close();
            });  

            dialog.show();
        } 
        catch (IOException ex) 
        {
            System.out.println(ex.toString());
        }

        return dialog;
    }

    public static ArrayList<Node> getAllNodes(Parent root) {
        ArrayList<Node> nodes = new ArrayList();
        addAllDescendents(root, nodes);
        return nodes;
    }

    private static void addAllDescendents(Parent parent, ArrayList<Node> nodes) {
        for (Node node : parent.getChildrenUnmodifiable()) {
            nodes.add(node);
            if (node instanceof Parent)
                addAllDescendents((Parent)node, nodes);
        }
    }    
}

<强> 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" fx:controller="javafxapplication8.FXMLDocumentController">
    <children>
        <Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
        <Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
    </children>
</AnchorPane>

Dialog FXML

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>

<VBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="250.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <HBox maxHeight="80.0" style="-fx-background-color: #E4E4E4;">
         <children>
            <StackPane prefWidth="300.0" style="-fx-background-color: #E4E4E4;">
               <children>
                  <Label text="Header Text Goes Here!" StackPane.alignment="CENTER_LEFT">
                     <padding>
                        <Insets left="20.0" />
                     </padding>
                     <font>
                        <Font size="16.0" />
                     </font>
                  </Label>
               </children>
            </StackPane>
            <ImageView fitHeight="80.0" fitWidth="80.0" pickOnBounds="true" preserveRatio="true" />
         </children>
      </HBox>
      <VBox maxHeight="170.0" style="-fx-background-color: #F8F8F8;">
         <children>
            <AnchorPane minHeight="130.0">
               <children>
                  <Label alignment="TOP_LEFT" text="Body Text Goes Here!" wrapText="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
                     <padding>
                        <Insets left="20.0" top="15.0" />
                     </padding>
                  </Label>
               </children>
            </AnchorPane>
            <HBox alignment="CENTER_RIGHT" spacing="10.0">
               <children>
                  <Button mnemonicParsing="false" text="Yes" />
                  <Button mnemonicParsing="false" text="No, continue without restart" />
               </children>
               <padding>
                  <Insets bottom="10.0" right="20.0" />
               </padding>
            </HBox>
         </children>
      </VBox>
   </children>
</VBox>

enter image description here

这可能会成为一种解决方法,直到有人提出答案。

答案 2 :(得分:0)

部分解决方案(不完全是文本长度,但您可以设置自己的大小):

((Button) alert.getDialogPane().lookupButton(bttYes)).setMaxWidth(50);