JavaFX 2 TitledPane图形扩展到全尺寸

时间:2013-07-21 10:08:21

标签: resize javafx-2 titlebar

我正在尝试将自定义工具栏添加到通用TitledPane。 我的代码似乎工作(最小示例如下),但我有布局问题。 具体来说:我正在使用setGraphic()添加我的工具栏,但这似乎有一个固定的宽度,而我希望它能够扩展,所以我可以让按钮向右冲,同时保持左侧的标题。

(我无法发布图片,所以我将恢复为ASCII艺术) 这是以下代码的实际结果:

+------------------------------------+
| > Node 1 [a][b][c][d]              |
+------------------------------------+
| > Node                             |
+------------------------------------+

虽然我想得到类似的东西:

+------------------------------------+
| > Node 1              [a][b][c][d] |
+------------------------------------+
| > Node                             |
+------------------------------------+

我可以明确地将结果设置伪造为BorderPane宽度,但它不会跟随调整大小!


这里启动代码:

import java.net.URL;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Accordion;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TitledPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class Test extends Application {

private Node loadMinitool(String title) {
    try {
        URL u = getClass().getResource("Minitool.fxml");
        FXMLLoader l = new FXMLLoader(u);
        Node n = (Node) l.load();
        Minitool mtc = (Minitool) l.getController();
        mtc.setTitle(title);
        return n;
    } catch (Exception e) {
        System.err.println("Unable to load 'Minitool.fxml': "+e.getMessage());
    }
    return null;
}

public Parent createContent() {

    TitledPane t1 = new TitledPane(null, new Button("Button"));
    t1.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
    t1.setGraphic(loadMinitool("Node 1"));
    TitledPane t2 = new TitledPane("Node 2", new Text("String"));
    TitledPane t3 = new TitledPane("Node 3", new Rectangle(120, 50,
            Color.RED));

    Accordion accordion = new Accordion();

    accordion.getPanes().add(t1);
    accordion.getPanes().add(t2);
    accordion.getPanes().add(t3);

    accordion.setMinSize(100, 100);
    accordion.setPrefSize(200, 400);

    return accordion;

}

@Override
public void start(Stage primaryStage) {
    primaryStage.setScene(new Scene(createContent()));
    primaryStage.show();

}

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

关联的FXML是:

<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.net.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.*?>

<BorderPane maxWidth="1.7976931348623157E308" prefWidth="-1.0" xmlns:fx="http://javafx.com/fxml" fx:controller="com.voith.hyconmde.ui.util.minitool.Minitool">
  <left>
    <Label fx:id="title" maxWidth="1.7976931348623157E308" text="Title goes here" BorderPane.alignment="CENTER_LEFT" />
  </left>
  <center>
    <Pane fx:id="filler" maxWidth="1.7976931348623157E308" minWidth="0.0" prefHeight="16.0" prefWidth="200.0" />
  </center>
  <right>
    <HBox>
      <Button fx:id="add" graphicTextGap="0.0" onAction="#addAction" styleClass="btnToolbar" />
      <Button fx:id="del" graphicTextGap="0.0" onAction="#delAction" styleClass="btnToolbar" />
      <Button fx:id="sub" graphicTextGap="0.0" onAction="#subAction" styleClass="btnToolbar" />
      <Button fx:id="dup" graphicTextGap="0.0" onAction="#dupAction" styleClass="btnToolbar" />
    </HBox>
  </right>
  <stylesheets>
    <URL value="@Minitool.css" />
  </stylesheets>
</BorderPane>

,控制器是:

import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;

public class Minitool {

    @FXML private ResourceBundle resources;
    @FXML private URL location;
    @FXML private Button add;
    @FXML private Button del;
    @FXML private Button dup;
    @FXML private Button sub;    
    @FXML private Label title;

    @FXML void addAction(ActionEvent event) {
    } 
    @FXML void delAction(ActionEvent event) {
    }
    @FXML void dupAction(ActionEvent event) {
    }
    @FXML void subAction(ActionEvent event) {
    }
    @FXML void initialize() {
        assert add != null : "fx:id=\"add\" was not injected: check your FXML file 'Minitool.fxml'.";
        assert del != null : "fx:id=\"del\" was not injected: check your FXML file 'Minitool.fxml'.";
        assert dup != null : "fx:id=\"dup\" was not injected: check your FXML file 'Minitool.fxml'.";
        assert sub != null : "fx:id=\"sub\" was not injected: check your FXML file 'Minitool.fxml'.";
        assert title != null : "fx:id=\"title\" was not injected: check your FXML file 'Minitool.fxml'.";
    }

    public void setTitle(String title) {
        this.title.setText(title);
    }

    public void setWidth() {
        if (parent == null) {
            Parent p = minitool.getParent();
                if (p != null)
            p = p.getParent();
            if (p instanceof TitledPane) {
                parent = (TitledPane) p;
                parent.widthProperty().addListener(new ChangeListener<Object>() {
                    @Override
                    public void changed(ObservableValue<?> observable, Object oldValue, Object newValue) {
                        if (newValue instanceof Number) {
                            Number n = (Number) newValue;
                            double d = n.doubleValue();
                            setFiller(d);
                        }
                    }
                });
                setFiller(parent.getWidth());
            }
        }
    }

    protected void setFiller(double d) {
        double w = d - title.getWidth() - 180; // XXX: this value has been hand-trimmed!
        if (w < 0)
            w = 0;
        filler.setPrefWidth(w);
    }
}

2 个答案:

答案 0 :(得分:3)

你需要做一些基本的数学运算。请按照以下代码修改:
1)在fxml文件中为HBox添加属性:

<HBox alignment="CENTER_RIGHT" HBox.hgrow="ALWAYS">

结果是,当标题宽度中有一个额外的空格(通过调整大小)时,此HBox将具有扩展优先级,其子节点将始终与右侧(中心)对齐。

2)现在我们需要以这种方式计算剩余的标题宽度

REMAINING_WIDTH = TITLE_TOTAL_WIDTH - TITLE_TEXT_WIDTH - ARROW_BUTTON_WIDTH - RIGHT_LEFT_PADDINGS

计算出的宽度将是我们的工具按钮窗格(即HBox的)首选宽度。

public Parent createContent() {

        String titleText = "Node 1";

        TitledPane t1 = new TitledPane(null, new Button("Button"));
        t1.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
        t1.setGraphic(loadMinitool(titleText));

        double titleTextWidth = computeTextWidth(t1.getFont(), titleText, 0);
        double arrowButtonWidth = 14;  // I have given a static value here otherwise
            // it must be calculated by
            // t1.lookup(".arrow-button").getLayoutBounds().getWidth()
            // after the primary stage has been shown. Namely after primaryStage.show(); line.
        double paddings = 20; // right (10) and left (10) paddings defined in 
            // caspian.css for title of the pane. These values can also be obtained 
            // by lookup.
        double total = titleTextWidth + arrowButtonWidth + paddings;

        HBox toolButtons = (HBox) ((BorderPane) t1.getGraphic()).getRight();
        toolButtons.prefWidthProperty().bind(t1.widthProperty().subtract(total));

        TitledPane t2 = new TitledPane("Node 2", new Text("String"));
        TitledPane t3 = new TitledPane("Node 3", new Rectangle(120, 50,
                Color.RED));

        Accordion accordion = new Accordion();
        accordion.getPanes().add(t1);
        accordion.getPanes().add(t2);
        accordion.getPanes().add(t3);
        accordion.setMinSize(100, 100);
        accordion.setPrefSize(200, 400);

        return accordion;
    }

computeTextWidth()代码取自com.sun.javafx.scene.control.skin.Utils,仅供参考:

private double computeTextWidth(Font font, String text, double wrappingWidth) {
    Text helper = new Text();
    helper.setText(text);
    helper.setFont(font);
    // Note that the wrapping width needs to be set to zero before
    // getting the text's real preferred width.
    helper.setWrappingWidth(0);
    double w = Math.min(helper.prefWidth(-1), wrappingWidth);
    helper.setWrappingWidth((int) Math.ceil(w));
    return Math.ceil(helper.getLayoutBounds().getWidth());
}

你的其他代码保持不变。

答案 1 :(得分:1)

我已经提出了解决方案:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TestApp extends Application {

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

    @Override
    public void start(Stage primaryStage) {

        TitledPane title = new TitledPane();
        //title.setContent(new Label("Content"));

        Node graphic = new Button("Click");
        Label label = new Label("Titled Pane Header");
        // Allow the label to grow to max width
        label.setMaxWidth(Double.MAX_VALUE);
        // Let it actually grow
        HBox.setHgrow(label, Priority.ALWAYS);
        HBox hBox = new HBox(label, graphic);
        hBox.setAlignment(Pos.CENTER_LEFT);
        hBox.setPadding(new Insets(0, 3, 0, 0));

        // Bind the HBox's min width to the TitledPane's width and subtract arrow (15) and left padding (10)
        hBox.minWidthProperty().bind(title.widthProperty().subtract(25));

        title.setGraphic(hBox);

        Scene scene = new Scene(new VBox(title), 300, 250);

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