How can I solve this visual glitch in my JavaFX TableView?

Left column: checkboxes to select or deselect the rows selected by default. Right column: A line representing the number of selected lines before the line and including it. Therefore, deselecting a check box in a row changes the values ​​in the rows below.

Error: Scroll down to the bottom of the table. Uncheck the box with invitation code 74. Select it again. The last three invitation codes should read 73, 74, and 75 again. But quite often they show 73, 73, 74 or 73, 74, 74.

The error does not always happen! If this does not happen, scrolling up and down with the scroll bar of the table and repeating the above procedure can lead to its occurrence.

It seems that the error is only visual - I made it dump the contents of the ObservableList to the console and display the correct values. Besides this visual glitch, my application is working correctly. Clicking on any other control in the window (for example, the scroll bar of a table) flips the invitation codes in the table to the correct value. Going to another workspace on my desktop and going back also shows the correct values.

A small image showing the console dump of the ObservableList on the left, the listening table on the right.

The question is quite logical: How can I crush this error !?

EDIT : Threw away more code, as Kleopatra advised. Thank!

MCV:

FXMLDocumentController.java

package invcodebug;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.util.Callback;

public class FXMLDocumentController implements Initializable {
    @FXML private TableView<Person> personTable;
    @FXML private TableColumn<Person, Boolean> invitedCol;
    @FXML private TableColumn<Person, String> inviteCodeCol;
    private final ObservableList<Person> persons
            = FXCollections.observableArrayList();

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        initPersonTable();
        populatePersons();
    }

    private void initPersonTable() {
        invitedCol.setCellValueFactory(new PropertyValueFactory<>("invited"));
        inviteCodeCol.setCellValueFactory(new PropertyValueFactory<>("inviteCode"));

        invitedCol.setCellFactory(CheckBoxTableCell.forTableColumn(new Callback<Integer, ObservableValue<Boolean>>() {
            @Override
            public ObservableValue<Boolean> call(Integer param) {
                doInvCode();

// SHOWS: underlying ObservableList has correct values
System.out.println("--------------------------");
for (Person p : persons) {
    System.out.println(p.isInvited() + " " + p.getInviteCode()
    );
}
                return persons.get(param).invitedProperty();
            }
        }));

        personTable.setItems(persons);
    }

    private void doInvCode() {
        int invCounter = 1;
        for (Person p : persons) {
            if (p.isInvited()) {
                p.setInviteCode(((Integer) invCounter).toString());
                invCounter++;
            } else p.setInviteCode("");
        }
    }

    private void populatePersons() {
        for (int i = 0; i < 75; i++) {
            persons.add(new Person(true, ""));
        }
    }
}

Person.java

package invcodebug;

import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;

public class Person {
    private final SimpleBooleanProperty  invited;
    private final SimpleStringProperty   inviteCode;

    public Person(boolean invited, String inviteCode) {
        this.invited = new SimpleBooleanProperty(invited);
        this.inviteCode = new SimpleStringProperty(inviteCode);
    }

    public boolean isInvited() {
        return invited.get();
    }
    public SimpleBooleanProperty invitedProperty() {
        return invited;
    }

    public String getInviteCode(){
        return inviteCode.get();
    }
    public void setInviteCode(String invCode) {
        this.inviteCode.set(invCode);
    }
    public SimpleStringProperty inviteCodeProperty() {
        return inviteCode;
    }
}

FXMLDocument.fxml

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

<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="AnchorPane" prefHeight="464.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="invcodebug.FXMLDocumentController">
    <children>
        <TableView fx:id="personTable" editable="true" layoutX="26.0" layoutY="28.0" prefHeight="347.0" prefWidth="572.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="20.0">
            <columns>
                <TableColumn fx:id="invitedCol" prefWidth="27.0" sortable="false" />
                <TableColumn fx:id="inviteCodeCol" editable="false" prefWidth="110.0" resizable="false" sortable="false" text="Invite Code" />
            </columns>
        </TableView>
    </children>
</AnchorPane>

InvCodeBug.java

package invcodebug;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class InvCodeBug extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

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

, , , personTable.requestFocus(); doInvCode(), , , .

+1

All Articles