Adding a list of specific nodes to a JavaFX FXML custom control

I am trying to create a toolbar in JavaFX to add buttons using FXML like this:

<?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>

      

Here is the relevant java class:

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);
    }

}

      

When I run the project, I get this exception:

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

interface javafx.collections.ObservableList.

      

Thanks in advance.

+3


source to share


1 answer


Since you have a method set setButtons

, it FXMLLoader

will try to pass to this element the value defined by the elements inside <buttons>...</buttons>

. (See Introduction to FXML and note, in particular, "A read-only property is a Bean property whose receiver returns an instance java.util.List

and does not have a corresponding setter." Is my emphasis.)

If you have a method getButtons()

that returns a list and a method setButtons

, then it FXMLLoader

will do what you want, and pass the value corresponding to each element to the method add

called on the result of the call getButtons()

. That is, it does

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

      

You either want it to be done or you want it to do



   setButtons(FXCollections.observableArrayList(new ButtonBarButton(...), new ButtonBarButton(...), ...)); 

      

So you have two fixes, either remove the method setButtons(...)

from ButtonBar

, or agree to pass to ObservableList

:

        <buttons>
            <FXCollections fx:factory="observableArrayList">
                <ButtonBarButton path="/com/supridatta/javafx/icons/plus.png"/>
                <ButtonBarButton path="/com/supridatta/javafx/icons/minus.png"/>
                <ButtonBarButton path="/com/supridatta/javafx/icons/last.png"/>
            </FXCollections>
        </buttons>

      

+4


source







All Articles