Dana S
Dana S

Reputation: 53

JavaFX - ListView doesnot populated data from fxml

I'm trying to populate list view with fxml but while running my program the method setCellFactory for somehow doesn't called by my ListView. Is some one can help my and explain me how to fix my code?

listviewContoller.java -->

public class ListViewController implements Initializable {

    @SuppressWarnings("rawtypes")
    @FXML
    private ListView listView;

    private ArrayList<String> arrayList = new ArrayList<>();

    @SuppressWarnings("rawtypes")
    ObservableList observableList = FXCollections.observableArrayList();

    @SuppressWarnings("unchecked")
    @Override
    public void initialize(URL location, ResourceBundle resources) {
            arrayList.add("String 1");
            arrayList.add("String 2");
            arrayList.add("String 3");
            arrayList.add("String 4");
            observableList.setAll(arrayList);
            listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
                @Override
                public ListCell<String> call(ListView<String> param) {
                    return new ListViewCell();
                }
            });
    }
}

ListViewCell.java -->

public class ListViewCell extends ListCell<String> {

    @Override
    public void updateItem(String string, boolean empty) {
        super.updateItem(string, empty);
        if(string != null) {
            Data data = new Data();
            data.setInfo(string);
            setGraphic(data.getBox());
        } else {
            Data data = new Data();
            data.setInfo("AAAA");
            setGraphic(data.getBox());
        }
    }
}

Data.java -->

public class Data  implements Initializable{

    @FXML
    private HBox hBox;

    @FXML
    private ImageView imageListCell;

    @FXML
    private Label labelListCell;


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/Tracker/UI/listCellItem.fxml"));
        try {
            loader.load();
        } catch (IOException e){
            throw new RuntimeException(e);
        }
    }

    public void setInfo(String string) {
        labelListCell.setText(string);
    }

    public HBox getBox() {
        return hBox;
    }
}

Upvotes: 2

Views: 1948

Answers (2)

Hemant Sangle
Hemant Sangle

Reputation: 282

You can directly add Object in list view

In Example below I have created a list item in fxml and add that item in list view using for loop

See the source for Example

ListViewExample FXML code

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

<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="AnchorPane" prefHeight="389.0" prefWidth="471.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="customlistview.ListViewExampleController">
  <children>
      <ListView fx:id="listView" layoutX="14.0" layoutY="14.0" prefHeight="389.0" prefWidth="517.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0" />
  </children>
</AnchorPane>

ListViewExampleController Code

package customlistview;

    import java.io.IOException;
    import java.net.URL;
    import java.util.ResourceBundle;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.event.ActionEvent;
    import javafx.fxml.FXML;
    import javafx.fxml.FXMLLoader;
    import javafx.fxml.Initializable;
    import javafx.scene.control.ListCell;
    import javafx.scene.control.ListView;
    import javafx.util.Callback;
    import javafx.scene.layout.Pane;

    /**
     *
     * @author hemant-pc
     */
    public class ListViewExample implements Initializable {

        @FXML
        private ListView listView;

        @FXML
        Pane pane;

        public ListViewExample() {
        }

        @Override
        public void initialize(URL url, ResourceBundle rb) {
            try {
                ObservableList<Pane> list = FXCollections.observableArrayList();
                for (int i = 1; i <= 10; i++) {
                    FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("ListItem.fxml"));
                    Pane listItem = fxmlLoader.load();
                    ListItemController controller = fxmlLoader.getController();
                    controller.setText("List Item " + i);
                    list.add(listItem);
                }
                listView.getItems().addAll(list);
            } catch (IOException ex) {
                Logger.getLogger(ListViewExample.class.getName()).log(Level.SEVERE, null, ex);
            }

        }

    }

ListItem FXML Code

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

    <?import javafx.scene.control.Button?>
    <?import javafx.scene.control.Label?>
    <?import javafx.scene.layout.Pane?>

    <Pane fx:id="pane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="65.0" prefWidth="317.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="customlistview.ListItemController">
       <children>
          <Label fx:id="label" layoutX="35.0" layoutY="6.0" prefHeight="50.0" prefWidth="117.0" text="ListItem1" />
          <Button fx:id="button" layoutX="201.0" layoutY="19.0" mnemonicParsing="false" onAction="#ButtonClick" text="Button" />
       </children>
    </Pane>

ListItemController Code

 package customlistview;

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

    /**
     * FXML Controller class
     *
     * @author hemant-pc
     */
    public class ListItemController implements Initializable {

        @FXML
        Label label;

        /**
         * Initializes the controller class.
         */
        @Override
        public void initialize(URL url, ResourceBundle rb) {
            // TODO
        }

        @FXML
        public void ButtonClick(ActionEvent event) {
            Alert alert = new Alert(Alert.AlertType.INFORMATION);
            alert.setContentText(label.getText());
            alert.showAndWait();
        }

        public void setText(String string) {
            label.setText(string);
        }

        public String getText() {
            return label.getText();
        }

    }

Snapshot

enter image description here

Upvotes: 0

fabian
fabian

Reputation: 82531

setCellFactory is called, but you use a cell that does not display the content in any way:

Creating a instance of Data won't load the fxml and without the fxml being loaded, getBox() always returns null.

BTW: It would be a good idea to reuse Data to avoid having to reload the fxml.


Usually in a case like this you create a custom Node type with the content loaded from fxml:

public class Data extends HBox {

    public Data() {
        // load fxml here
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/Tracker/UI/listCellItem.fxml"));
        loader.setController(this);
        loader.setRoot(this);
        try {
            loader.load();
        } catch (IOException ex) {
            throw new IllegalStateException(ex);
        }
    }

    @FXML
    private ImageView imageListCell;

    @FXML
    private Label labelListCell;

    public void setInfo(String string) {
        labelListCell.setText(string);
    }

}

listCellItem.fxml

<!-- no fx:controller attribute allowed here -->
<fx:root type="javafx.scene.layout.HBox" xmlns:fx="http://javafx.com/fxml/1">
    <!-- contents of the root tag of the old fxml should be copied here -->
</fx:root>

Note that you should also copy all attributes of the root element of the old fxml, but for the fx:controller attribute.

public class ListViewCell extends ListCell<String> {

    private final Data data;

    public ListViewCell() {
        this.data = new Data();
        setGraphic(this.data);
    }

    @Override
    public void updateItem(String string, boolean empty) {
        super.updateItem(string, empty);

        data.setInfo(string == null ? "AAAA" : string);
    }
}

Furthermore you never set the new value of the items property:

@Override
public void initialize(URL location, ResourceBundle resources) {
    arrayList.add("String 1");
    arrayList.add("String 2");
    arrayList.add("String 3");
    arrayList.add("String 4");
    observableList.setAll(arrayList);
    listView.setItems(observableList);
    ...
}

Moreover instead of using @SuppressWarnings("rawtypes") and @SuppressWarnings("unchecked"), why not simply add <String> as type parameter???

Upvotes: 3

Related Questions