JavaFx TableView mit CheckBox

J90

Aktives Mitglied
Hallo,

da der Aufbau eines TableViews sich änderte beim Wechsel von Swing auf JavaFX, benutze ich ein relativ simples Prinzip für die Standardmäßige Wiederverwendung eines Tables (d.h. TableView<Object[]> TableView),d.h. in diesem Fall arbeite ich sehr wenig Objektorientiert.

Da ich nur rudimentär mit der Oberflächenprogrammierung vertraut bin habe ich mich damals für diese funktionierende Variante entschieden.
Mein Problem besteht nun darin, dass ich bei der Verwendung von Boolean.class in einer Column zwar die CheckBox reinbekomme, mein
Listener aber nicht updatet. Bei String.class-Columns verwende ich aktuell:

Code:
       TC.setCellFactory(TextFieldTableCell.forTableColumn());
                TC.setOnEditCommit(new EventHandler<CellEditEvent<Object[], String>>() {
                    @Override
                    public void handle(CellEditEvent<Object[], String> Edit) {
                        TablePosition<Object[], String> TP = Edit.getTablePosition();
                        Object Wert = Edit.getNewValue();
                        int Row = TP.getRow(), Col = TP.getColumn();
                        setValueAt(String.valueOf(Wert), Row, Kopfzeile[Col], true);
                    }
                });

Das funktioniert. Die Methode setValueAt(..) überschreibt das jeweilige item an der Stelle und Zeile (ObservableList<Object[]> items = this.TableView.getItems()).

Die gleiche Variante für die Boolean.class mit

Code:
  TC.setCellFactory(new CheckBoxCellFactory());
                TC.setOnEditCommit(new EventHandler<CellEditEvent<Object[], String>>() {
                    @Override
                    public void handle(CellEditEvent<Object[], String> Edit) {

                        TablePosition<Object[], String> TP = Edit.getTablePosition();
                        Object Wert = Edit.getNewValue();
                        int Row = TP.getRow(), Col = TP.getColumn();
                        setValueAt(String.valueOf(Wert), Row, Kopfzeile[Col], true);
                    }
                });

reagiert nicht, d.h. die handle Methode wird nicht ausgeführt beim Klicken der CheckBox im Table.

Hierzu habe ich immer mal wieder Versuche gestartet dies zu ändern, leider ohne Erfolg. Da ich wichtigere Aufgabe zu erledigen habe, verwerfe ich es
dann immer wieder. Deswegen hier meine Frage an Leute die sich damit auskennen:
Gibt es hier eine schnelle einfache Variante diese Ausführung zu erreichen, sodass die CheckBox Werte im TableView angepasst werden und die CheckBox updated?

Versuche wie

Code:
   TC.setCellFactory(col -> {

                    CheckBoxTableCell<TableViewCheckBox.Item, Boolean> cell = new CheckBoxTableCell<>(index -> {
                        boolean T = (boolean) Boolean.valueOf(TableView.getItems().get(index)[Col].toString());
                        return new SimpleBooleanProperty(true);
                    });
                    Callback<Integer, ObservableValue<Boolean>> Call = new Callback<Integer, ObservableValue<Boolean>>() {
                        @Override
                        public ObservableValue<Boolean> call(Integer index) {
                            String itemBoolean = (String) TableView.getItems().get(index)[Col];
                            ObservableValue<Boolean> o = new ReadOnlyObjectWrapper<>(Boolean.valueOf(true));
                            Print(itemBoolean);
                            return o;
                        }
                    };

                    cell.setSelectedStateCallback(Call);
                    TC.setCellFactory(CheckBoxTableCell.forTableColumn(Call));
                    return cell;
                });

führten hierbei auch zu keinem Erfolg. Bei dieser Variante reagiert die Cell zwar auf Clicks, itemBoolean ist allerdings immer false und
es lässt sich auch hier kein Haken setzen bei der CB.

Hilfe hierzu wäre sehr nett, da ich aktuell in meinem Programm immer true/false Werte in einer String.class-Column verwende und mich das mittlerweile extrem nervt..
 
Ohne deine Modelle zu kennen habe ich es wie folgt für ein minimales Beispiel:
Java:
public class MainWindowController implements Initializable {

    private ObservableList<ViewModel> dataList = FXCollections.observableArrayList();

    @FXML
    private TableView<ViewModel> tvView;

    @FXML
    private TableColumn<ViewModel, Boolean> boolColumn;

    @FXML
    private TableColumn<ViewModel, String> nameColumn;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        boolColumn.setCellFactory(CheckBoxTableCell.forTableColumn(boolColumn));
        boolColumn.setCellValueFactory(new PropertyValueFactory<>("bool"));
        nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));

        tvView.setItems(dataList);
        tvView.setEditable(true);

        dataList.add(new ViewModel(false, "b"));
    }

    public static class ViewModel {
        private BooleanProperty bool = new SimpleBooleanProperty(false);
        private StringProperty name = new SimpleStringProperty();

        public ViewModel(final boolean bool, final String name) {
            this.bool.set(bool);
            this.name.set(name);
        }

        public boolean isBool() {
            return bool.get();
        }

        public BooleanProperty boolProperty() {
            return bool;
        }

        public void setBool(final boolean bool) {
            this.bool.set(bool);
        }

        public String getName() {
            return name.get();
        }

        public StringProperty nameProperty() {
            return name;
        }

        public void setName(final String name) {
            this.name.set(name);
        }

    }
}
Java:
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1" fx:controller="MainWindowController">
    <children>
      <TableView fx:id="tvView" layoutY="7.0" prefHeight="200.0" prefWidth="200.0">
        <columns>
          <TableColumn fx:id="boolColumn" prefWidth="75.0" text="My Bool" />
          <TableColumn fx:id="nameColumn" prefWidth="75.0" text="My String" />
        </columns>
      </TableView>
    </children>
</AnchorPane>
Ich sehe die Schwierigkeit noch nicht. Wie sieht dein Modell aus? Brauchst du zwingend Click-Listener?
 
Danke für den Hinweis, ich suche allerdings nach einer einfachen Lösung, welche sich in meinem bestehenden Aufbau eingliedert anstatt alles von Grund auf Neu zu bauen (mangels Zeit).

Da ich hier nicht direkt mit meiner kompletten class ankommen wollte, habe ich sie zunächst weggelassen. Hier nun die wichtigsten Teile:

Code:
     private void Init(int[] Weite, Object[] Class, boolean[] Editable, CellFactory CellFactory, Object[][] Daten, String[] Kopfzeile, boolean Sortable) {
        this.Kopfzeile = Kopfzeile;
        TCs = new TableColumn[Kopfzeile.length];
        for (int i = 0; i < Kopfzeile.length; i++) {

            TCs[i] = new TableColumn<>(Kopfzeile[i]);
            TCs[i].setSortable(Sortable);
            if (Weite != null) {
                TCs[i].setPrefWidth(Weite[i]);
            }
            int j = i;
            Object C = Object.class;
            if (Class != null) {
                C = Class[i];
            }
            TCs[i].setEditable(Editable[i]);
            Object CI = C;

            TCs[i].setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Object[], ?>, ObservableValue<?>>() {
                @Override
                public ObservableValue<?> call(TableColumn.CellDataFeatures<Object[], ?> p) {
                    Object[] x = p.getValue();

                    if (j < x.length && x.length > 0) {

                        if (CI instanceof String) {
                            return new SimpleStringProperty(x[j].toString());
                        } else if (CI instanceof Boolean) {
                            return new SimpleBooleanProperty(Boolean.valueOf(x[j].toString()));
                        } else if (CI instanceof Integer) {
                            return new SimpleIntegerProperty(Integer.valueOf(x[j].toString()));
                        } else if (CI instanceof Double) {
                            return new SimpleDoubleProperty(Double.valueOf(x[j].toString()));
                        }
                        return new SimpleObjectProperty(x[j]);
                    } else {
                        return new SimpleObjectProperty("");
                    }
                }
            });

            //Boolean Spalte
            Set_Cell_Factory(TCs[i], C, Class, i, CellFactory);
            this.TableView.getColumns().add(TCs[i]);
        }


        if (Weite == null) {
            this.TableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        }

        for (int i = 0; i < Daten.length; i++) {
            this.TableView.getItems().add(Daten[i]);
            this.HM_Daten.add(ArrayToMap(Daten[i]));
        }
        this.Items = Clone();


        /*
        Sortieren
         */
        // enable multi-selection
        this.TableView.getSelectionModel().setCellSelectionEnabled(true);
        this.TableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

        // enable copy/paste
        TableUtils.installCopyPasteHandler(this.TableView);

        this.TableView.setOnKeyPressed(new TableKeyEventHandler());
    }




    private void Set_Cell_Factory(TableColumn TC, Object Class, Object[] Klassen, int Col, CellFactory CF) {
        if (Klassen != null) {
            if (Class == Boolean.class) {

               TC.setCellFactory(new CheckBoxCellFactory());
                TC.setOnEditCommit(new EventHandler<CellEditEvent<Object[], String>>() {
                    @Override
                    public void handle(CellEditEvent<Object[], String> Edit) {

                        TablePosition<Object[], String> TP = Edit.getTablePosition();
                        Object Wert = Edit.getNewValue();
                        int Row = TP.getRow(), Col = TP.getColumn();
                        Print(Wert);
                        setValueAt(String.valueOf(Wert), Row, Kopfzeile[Col], true); //HashMap
                    }
                });
            } else if (Class == String.class) {
                TC.setCellFactory(TextFieldTableCell.forTableColumn());
                TC.setOnEditCommit(new EventHandler<CellEditEvent<Object[], String>>() {
                    @Override
                    public void handle(CellEditEvent<Object[], String> Edit) {
                        TablePosition<Object[], String> TP = Edit.getTablePosition();
                        Object Wert = Edit.getNewValue();
                        int Row = TP.getRow(), Col = TP.getColumn();
                        Print(Wert);
                        setValueAt(String.valueOf(Wert), Row, Kopfzeile[Col], true); //HashMap
                    }
                });
            }
        } else {
            if (CF == null) {
                //Standardeinstellung
                TC.setCellFactory((row) -> {
                    CellFactory CellFactory = new CellFactory_Allgemein();
                    return CellFactory.TableCell(CellFactory.Schriftfarbe());
                });
            } else {
                //Laden dynamisch aus CellFactory Klasse
                TC.setCellFactory((row) -> {
                    return CF.newInstance().TableCell(CF.Schriftfarbe());
                });
            }
        }

    }

    public class CheckBoxCellFactory implements Callback {

        @Override
        public TableCell call(Object param) {
            CheckBoxTableCell<Boolean, Boolean> checkBoxCell = new CheckBoxTableCell();      
            TableColumn TC = (TableColumn) param;
            return checkBoxCell;
        }
    }
 
      public void setValueAt(String Wert, int Zeile, int Column, boolean Refresh) {
        if (Zeile > -1 && Column > -1) {
            Platform.runLater(() -> {
                ObservableList<Object[]> items = this.TableView.getItems();
                         if (items.size() > Zeile) {
                    Object[] get = items.get(Zeile);
 
                    if (get.length > Column) {
                        get[Column] = Wert;
                        //Sleep(50);

                        if (Refresh) {
                            // TableView.getColumns().get(Column).setVisible(false);
                            //TableView.getColumns().get(Column).setVisible(true);
                            this.TableView.refresh();//CPU intensiv
                        }
                    }
                }
            });
        }
    }

Hierzu wäre ein einfache Lösung hilfreich. Ob das ganze insgesamt sinnvoll ist soll jetzt nicht das Thema sein..
Das Problem liegt wie gesagt darin, dass der Boolean.class-Code Teil nicht so funktioniert wie der String.class Teil drunter
in der Methode Set_Cell_Factory() . Es
erfolgt eben kein Umstellen der CheckBox-Cell (auf aktiv) und der Wert ist immer false, die handle() Methode wird allerdings
ausgeführt.
Falls jemand die gesamte class mit Testcase brauch, kann ich das auch einstellen, habe gehofft jemand erfahrenes sieht wo
hier das Problem liegt
 
Nur eins: Stell auf Boolean um und halte dich an die Doku... Mehr wüsste ich im Moment auch nicht.
Hab im Moment leider nicht so viel Zeit um zu experimentieren, also kann ich nur mit Wort, aber nicht Tat, zur Seite stehen.
 

Neue Themen


Zurück
Oben