在自定义JavaFX FXML控件上添加特定节点的列表

时间:2014-11-18 16:34:57

标签: java javafx custom-controls fxml

我正在尝试在JavaFX中创建一个工具栏,使用FXML添加按钮,如下所示:

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

<?import java.lang.*?>
<?import java.net.*?


<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import com.supridatta.javafx.*?>

<BorderPane xmlns:fx="http://javafx.com/fxml/1" prefHeight="200" prefWidth="320" fx:controller="com.supridatta.javafx.MainController">
    <stylesheets>
        <URL value="@main.css"/>
    </stylesheets>
    <top>
        <com.supridatta.javafx.ButtonBar fx:id="buttonBar">
            <buttons>
                <ButtonBarButton path="/com/supridatta/javafx/icons/plus.png"/>
                <ButtonBarButton path="/com/supridatta/javafx/icons/minus.png"/>
                <ButtonBarButton path="/com/supridatta/javafx/icons/last.png"/>
            </buttons>
        </com.supridatta.javafx.ButtonBar>
    </top>
    <bottom>
        <Button text="Exibir todos" onAction="#showAllButtons"/>
    </bottom>
</BorderPane>

这是相应的java类:

package com.supridatta.javafx;

import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;

public class ButtonBar extends StackPane {

    private final HBox contentPane = new HBox();
    private final ObservableList<ButtonBarButton> buttons = FXCollections.observableArrayList();

    public ButtonBar() {
        initButtonBar();
    }

    private void initButtonBar() {
        getChildren().add(contentPane);
        setAlignment(Pos.CENTER);
        buttons.addListener(new ListChangeListener<Node>() {

            @Override
            public void onChanged(ListChangeListener.Change<? extends Node> c) {
                while (c.next()) {
                    if (c.wasAdded()) {
                        for (Node node : c.getAddedSubList()) {
                            contentPane.getChildren().add(node);
                        }
                    }
                    if (c.wasRemoved()) {
                        for (Node node : c.getAddedSubList()) {
                            contentPane.getChildren().remove(node);
                        }
                    }
                }
            }
        });
    }

    public void setButtons(ObservableList<ButtonBarButton> contents) {
        this.buttons.setAll(contents);
    }

    public ObservableList<ButtonBarButton> getButtons() {
        return buttons;
    }

    public void addButton(ButtonBarButton button) {
        buttons.add(button);
    }

    public void removeButton(ButtonBarButton button) {
        buttons.remove(button);
    }

}

当我运行项目时,我遇到了这个例外:

java.lang.IllegalArgumentException: Unable to coerce ButtonBarButton@2fd0ac8f[styleClass=button ButtonBarButton]'' to

interface javafx.collections.ObservableList.

提前致谢。

1 个答案:

答案 0 :(得分:4)

由于您定义了setButtons方法,FXMLLoader将尝试将<buttons>...</buttons>内的元素定义的值传递给该方法。 (参见Introduction to FXML,特别注意“只读列表属性是一个Bean属性,其getter返回java.util.List 的实例,并且没有相应的setter方法。” - 我的重点。)

如果您有getButtons()方法返回列表,而没有setButtons方法,那么FXMLLoader将执行您想要的操作,并将与每个元素对应的值传递给{调用add的结果调用了{1}}方法。即它确实

getButtons()

你要么想要它呢,要么你想要它做

getButtons().add(new ButtonBarButton(...));
getButtons().add(new ButtonBarButton(...));
...

所以你有两个修正:要么从 setButtons(FXCollections.observableArrayList(new ButtonBarButton(...), new ButtonBarButton(...), ...)); 删除setButtons(...)方法,要么安排传递ButtonBar

ObservableList