【问题标题】:javafx automatic resizing and button paddingjavafx 自动调整大小和按钮填充
【发布时间】:2014-06-07 09:24:28
【问题描述】:

我正在尝试使用 Javafx 为布局制作一个屏幕键盘。我正在使用 Scene Builder 制作 FXML 文件。

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.text.*?>

<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="186.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2">
  <children>
    <VBox layoutX="0.0" layoutY="0.0" prefHeight="186.0" prefWidth="600.0" rotate="0.0" spacing="2.0">
      <children>
        <HBox minHeight="33.0" prefHeight="33.0" prefWidth="600.0" spacing="2.0">
          <children>
            <Label maxWidth="-Infinity" prefHeight="33.0" prefWidth="35.0" text="Milk" textAlignment="CENTER" />
            <Label maxWidth="-Infinity" prefHeight="33.0" text="Mister" />
            <Label maxWidth="-Infinity" prefHeight="35.0" text="Minimum" />
          </children>
          <padding>
            <Insets left="5.0" />
          </padding>
        </HBox>
        <HBox prefHeight="29.0" prefWidth="600.0">
          <children>
            <Button minWidth="29.0" mnemonicParsing="false" prefHeight="27.0" prefWidth="29.0" text="Esc" textAlignment="CENTER" underline="false">
              <font>
                <Font size="9.0" />
              </font>
            </Button>
            <Button mnemonicParsing="false" text="`" textAlignment="CENTER" />
            <Button mnemonicParsing="false" text="1" />
            <Button mnemonicParsing="false" text="2" />
            <Button mnemonicParsing="false" text="3" />
            <Button mnemonicParsing="false" text="4" />
            <Button mnemonicParsing="false" text="5" />
            <Button mnemonicParsing="false" text="6" />
            <Button mnemonicParsing="false" text="7" />
            <Button mnemonicParsing="false" text="9" />
            <Button mnemonicParsing="false" text="0" />
            <Button mnemonicParsing="false" text="-" />
            <Button mnemonicParsing="false" text="=" />
            <Button mnemonicParsing="false" text="Backspace" />
          </children>
        </HBox>
        <HBox prefHeight="57.0" prefWidth="600.0">
          <children>
            <VBox minHeight="50.0" prefHeight="78.0" prefWidth="505.0" HBox.hgrow="ALWAYS">
              <children>
                <HBox prefHeight="29.0" prefWidth="-1.0">
                  <children>
                    <Button mnemonicParsing="false" prefHeight="27.0" text="Tab" />
                    <Button mnemonicParsing="false" text="q" />
                    <Button mnemonicParsing="false" text="w" />
                    <Button mnemonicParsing="false" text="e" />
                    <Button mnemonicParsing="false" text="r" />
                    <Button mnemonicParsing="false" text="t" />
                    <Button mnemonicParsing="false" text="y" />
                    <Button mnemonicParsing="false" text="u" />
                    <Button mnemonicParsing="false" text="i" />
                    <Button mnemonicParsing="false" text="o" />
                    <Button mnemonicParsing="false" text="p" />
                    <Button mnemonicParsing="false" text="[" />
                    <Button mnemonicParsing="false" text="]" />
                  </children>
                </HBox>
                <HBox minHeight="-1.0" prefHeight="28.0" prefWidth="-1.0">
                  <children>
                    <Button mnemonicParsing="false" prefWidth="71.0" text="Caps" />
                    <Button mnemonicParsing="false" text="a" />
                    <Button mnemonicParsing="false" text="s" />
                    <Button mnemonicParsing="false" text="d" />
                    <Button mnemonicParsing="false" text="f" />
                    <Button mnemonicParsing="false" text="g" />
                    <Button mnemonicParsing="false" text="h" />
                    <Button mnemonicParsing="false" text="j" />
                    <Button mnemonicParsing="false" text="k" />
                    <Button mnemonicParsing="false" text="l" />
                    <Button mnemonicParsing="false" text=";" />
                    <Button mnemonicParsing="false" text="'" />
                    <Button mnemonicParsing="false" text="\\" />
                  </children>
                </HBox>
              </children>
            </VBox>
            <Button mnemonicParsing="false" prefHeight="57.0" prefWidth="132.0" text="Enter" />
          </children>
        </HBox>
        <HBox prefHeight="27.000099999997474" prefWidth="600.0">
          <children>
            <Button mnemonicParsing="false" prefWidth="85.0" text="Shift" />
            <Button mnemonicParsing="false" text="z" />
            <Button mnemonicParsing="false" text="x" />
            <Button mnemonicParsing="false" text="c" />
            <Button mnemonicParsing="false" text="v" />
            <Button mnemonicParsing="false" text="b" />
            <Button mnemonicParsing="false" text="n" />
            <Button mnemonicParsing="false" text="m" />
            <Button mnemonicParsing="false" text="," />
            <Button mnemonicParsing="false" text="." />
            <Button mnemonicParsing="false" text="/" />
            <Button mnemonicParsing="false" text="^" />
            <Button mnemonicParsing="false" prefWidth="56.0" text="Shift" />
            <Button mnemonicParsing="false" text="Del" />
          </children>
        </HBox>
        <HBox prefHeight="29.0" prefWidth="600.0" VBox.vgrow="ALWAYS">
          <children>
            <Button mnemonicParsing="false" text="Fn" />
            <Button mnemonicParsing="false" text="Ctrl">
              <HBox.margin>
                <Insets />
              </HBox.margin>
            </Button>
            <Button mnemonicParsing="false" text="Win" />
            <Button mnemonicParsing="false" text="Alt" />
            <Button mnemonicParsing="false" prefWidth="105.0" text="Space" HBox.hgrow="ALWAYS" />
            <Button mnemonicParsing="false" text="Alt" />
            <Button mnemonicParsing="false" prefWidth="-1.0" text="Ctrl" textAlignment="LEFT" />
            <Button mnemonicParsing="false" text="&lt;" />
            <Button mnemonicParsing="false" text="v" />
            <Button mnemonicParsing="false" text="&gt;" />
            <Button mnemonicParsing="false" text="Menu" />
          </children>
        </HBox>
      </children>
    </VBox>
  </children>
</AnchorPane>

看起来像这样:

但是当调整窗口大小时,内容不会。我希望按钮获得/减小大小,直到它适合窗口。打开 Hgrow 和 Vgrow 不起作用。

而且我找不到如何设置按钮的填充。我想让像 Ctrl 这样的按钮更小而不丢失文本。

【问题讨论】:

    标签: java javafx fxml


    【解决方案1】:

    根据字体大小调整大小

    对于您的特定情况,与其尝试使用填充或其他布局约束来调整按钮大小,不如尝试调整用于虚拟键盘的父布局容器的字体大小 (-fx-font-size)。如果您使字体大小变大,按钮将自动更改其首选大小以匹配此较大大小,并且所有文本将自动呈现并适合首选大小,并以该字体大小的建议填充布局显示(这可能是你想要的)。

    按钮大小调整规则

    使按钮可调整大小的基本规则是:

    1. 从按钮button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE) 中删除 maxSize 约束。这是必需的,因为按钮的默认限制是其最大尺寸是其首选尺寸,因此它不会自动增长以填满可用空间。
    2. 将按钮的首选大小设置为您想要的大小。当您使用屏幕键盘时,实现此目的的一种方法是增大或减小字体大小,然后按钮会自动调整大小。
    3. 如果您想在按钮中添加额外的填充,您可以在 CSS 中使用 -fx-padding 或在代码中使用 button.setPadding(new Insets(...))
    4. 将按钮放在可调整大小的父级中。
    5. 确保实际调整了可调整大小的父对象的大小(类似于 StackPane 的东西会自动调整大小以填充可用区域,我不经常使用 AnchorPane,所以我不熟悉它的大小调整行为)。

    为了更好地理解 JavaFX 中的布局管理,我建议查看过去的 JavaOne Interface Layout with JavaFX 2.0 presentation

    可调整大小的按钮网格示例

    如果您希望继续尝试创建自己的实现,sample color chooser 可能会有所帮助。颜色选择实现基于可调整大小按钮的可调整大小网格,因此当您更改可用于颜色选择器网格的区域时,网格和网格中的按钮都会扩展或收缩。颜色选择器的代码不是基于 FXML,也不是直接实现您想要的键盘,但它确实演示了您在问题中询问的按钮的自动调整大小。

    考虑使用 JavaFX 虚拟键盘

    JavaFX 已经有一个built-in virtual keyboard内置键盘没有官方支持和记录的公共 API,并且不保证在 Java 版本之间得到维护。但是,使用内置虚拟键盘可能仍然是比使用内置虚拟键盘更好的方法试图创造你自己的。创建高质量的通用虚拟键盘是一项非常艰巨的任务 (IMO)。

    Oracle JavaFX forums 上有关于这个话题的一些讨论。

    JavaFX 是开源的,所以即使你不直接使用内置的虚拟键盘,你也可以查看JavaFX source 看看它是如何实现的。

    虚拟键盘代码示例

    演示在“桌面”环境中使用内置 JavaFX 虚拟键盘的示例代码。

    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.StackPane;
    import javafx.stage.Stage;
    
    public class EmbeddedSample extends Application {
        @Override public void start(Stage stage) {
            stage.setScene(new Scene(new StackPane(new TextField("xyzzy")), 200, 100));
            stage.getScene().setOnMouseClicked(e -> stage.hide());
            stage.show();
        }
    
        public static void main(String[] args) { launch(args); }
    }  
    

    像这样运行示例代码:

    java -Dcom.sun.javafx.virtualKeyboard=javafx -Dcom.sun.javafx.touch=true EmbeddedSample
    

    用于根据字体大小调整大小的示例虚拟键盘代码

    keyboard.css

    .key {
        -fx-base: antiquewhite;
    }
    
    .key-row {
        -fx-spacing: 0.333333em;
    }
    
    .keyboard {
        -fx-spacing: 0.333333em;
        -fx-padding: 0.333333em;
        -fx-font-family: monospace;
    }
    

    ResizableKeyboardSample.java

    import javafx.application.Application;
    import javafx.beans.property.*;
    import javafx.geometry.*;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.layout.*;
    import javafx.scene.text.Font;
    import javafx.stage.Stage;
    
    public class ResizableKeyboardSample extends Application {
        public static void main(String[] args) throws Exception {
            launch(args);
        }
    
        String[] chars = {
                "qwertyuiop",
                "asdfghjkl",
                "zxcvbnm"
        };
    
        public void start(final Stage stage) throws Exception {
            Keyboard keyboard = new Keyboard();
    
            VBox layout = new VBox(20);
            layout.setPadding(new Insets(10));
            layout.getChildren().setAll(
                    createControls(keyboard),
                    keyboard
            );
    
            Scene scene = new Scene(layout, 1000, 400);
            scene.getStylesheets().add(
                    getClass().getResource(
                            "keyboard.css"
                    ).toExternalForm()
            );
    
            stage.setScene(scene);
            stage.show();
        }
    
        private Node createControls(Keyboard keyboard) {
            Slider fontSize = new Slider(8, 40, Font.getDefault().getSize());
            keyboard.fontSizeProperty().bind(fontSize.valueProperty());
            fontSize.setShowTickLabels(true);
            fontSize.setShowTickMarks(true);
            fontSize.setMajorTickUnit(2);
            fontSize.setMinorTickCount(0);
    
            Label typedData = new Label();
            keyboard.lastKeyTextProperty().addListener((observable, oldText, newText) ->
                    typedData.setText(typedData.getText() + newText)
            );
    
            VBox layout = new VBox(10);
            layout.getChildren().setAll(
                    new Label("Keyboard Size"),
                    fontSize,
                    typedData
            );
            layout.setMinSize(VBox.USE_PREF_SIZE, VBox.USE_PREF_SIZE);
    
            return layout;
        }
    
        class Keyboard extends VBox {
            private DoubleProperty fontSize = new SimpleDoubleProperty(Font.getDefault().getSize());
    
            public double getFontSize() {
                return fontSize.get();
            }
    
            public DoubleProperty fontSizeProperty() {
                return fontSize;
            }
    
            public void setFontSize(double fontSize) {
                this.fontSize.set(fontSize);
            }
    
            private ReadOnlyStringWrapper lastKeyText = new ReadOnlyStringWrapper();
    
            public String getLastKeyText() {
                return lastKeyText.get();
            }
    
            public ReadOnlyStringProperty lastKeyTextProperty() {
                return lastKeyText.getReadOnlyProperty();
            }
    
            public Keyboard() {
                setAlignment(Pos.BOTTOM_CENTER);
                setMinSize(VBox.USE_PREF_SIZE, VBox.USE_PREF_SIZE);
                getStyleClass().add("keyboard");
    
                onFontSizeChange(fontSize.getValue());
                fontSize.addListener((observable, oldValue, newValue) ->
                    onFontSizeChange(newValue)
                );
    
                for (String row: chars) {
                    HBox keyRow = new HBox();
                    keyRow.getStyleClass().add("key-row");
    
                    keyRow.setAlignment(Pos.CENTER);
                    for (char c: row.toCharArray()) {
                        KeyButton key = new KeyButton(Character.toString(c));
                        keyRow.getChildren().add(key);
                    }
                    getChildren().add(keyRow);
                }
            }
    
            private void onFontSizeChange(Number newValue) {
                setStyle("-fx-font-size: " + newValue + "px;");
            }
    
            class KeyButton extends Button {
                public KeyButton(String text) {
                    super(text);
                    getStyleClass().add("key");
    
                    setMinSize(Button.USE_PREF_SIZE, Button.USE_PREF_SIZE);
                    setMaxSize(Button.USE_PREF_SIZE, Button.USE_PREF_SIZE);
    
                    setOnAction(event -> lastKeyText.set(text));
                }
            }
        }
    
    }
    

    【讨论】:

    • 颜色选择器与我想要的很接近,只是高度/宽度比保持不变。当我使屏幕变宽时,按钮也应该变宽。我的目标是制作更好的 Windows 屏幕键盘版本(主要是改进单词预测)
    • @jereon,一旦你实现了你想要的键盘,如果你愿意,你可以发布它的代码作为你问题的答案。
    • @jewelsea 你在这里提到了使用 .root -fx-font-size 和稍后使用 em 单位的可能性。我一直在尝试在我的项目中使用这个解决方案,但是使用 em 来调整大小让我们说 fx-font-size: 2em 和 fx-padding: 3em;没有按预期工作。 root 的字体大小正在重置为默认值 12px。我在其他问题中描述了它:stackoverflow.com/questions/48255427/…