hetsketch.
hetsketch.

Reputation: 33

How to fix TableView when scrolling

I'm a newbie in JavaFX and I have no idea how to solve this problem. So, I have a checkbox column in a TableView. For example, I select 3 first boxes and scroll down the table. But when I go to the top of table, checkboxes are not selected. Here is some code:

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Callback;
import java.util.LinkedList;
import java.util.List;

public class App extends Application
{
List<User> list = new LinkedList<>();
TableColumn<User, Integer> id;
TableColumn<User, String> firstName;
TableColumn<User, Boolean> selected;
TableView<User> tableView;

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

@Override
public void start(Stage stage) throws Exception
{
    stage.setHeight(500);
    stage.setWidth(500);
    stage.setMinHeight(200);
    stage.setMinHeight(100);
    stage.setTitle("Table view");

    tableView = new TableView<>();

    ObservableList<User> list = FXCollections.observableArrayList();
    for(int i = 0; i < 40; i++)
    {
        list.add(new User(i, "first name", "last name"));
    }

    id = new TableColumn("ID");
    id.setCellValueFactory(new PropertyValueFactory<User, Integer>("id"));

    firstName = new TableColumn("First Name");
    firstName.setCellValueFactory(new PropertyValueFactory<User, String>("firstName"));

    TableColumn<User, String> lastName = new TableColumn("Last Name");
    lastName.setCellValueFactory(new PropertyValueFactory<User, String>("lastName"));

    selected = new TableColumn("Select");

    Callback<TableColumn<User, Boolean>, TableCell<User, Boolean>> booleanCellFactory =     new Callback<TableColumn<User, Boolean>, TableCell<User, Boolean>>()
    {
        @Override
        public TableCell<User, Boolean> call(TableColumn<User, Boolean> p)
        {
            return new CheckBoxCell();
        }
    };
    selected.setCellValueFactory(new PropertyValueFactory<User, Boolean>("active"));
    selected.setCellFactory(booleanCellFactory);

    tableView.getColumns().addAll(id, firstName, lastName, selected);
    tableView.setItems(list);

    BorderPane pane = new BorderPane();
    pane.setCenter(tableView);

    Scene scene = new Scene(pane);
    stage.setScene(scene);
    stage.show();
}

class CheckBoxCell extends TableCell<User, Boolean>
{
    private CheckBox checkbox;

    @Override
    protected void updateItem(Boolean item, boolean empty)
    {
        super.updateItem(item, empty);
        checkbox = new CheckBox();
        checkbox.setAlignment(Pos.CENTER);
        setAlignment(Pos.CENTER);
        setGraphic(checkbox);
        checkbox.setOnAction(new EventHandler<ActionEvent>()
        {
            @Override
            public void handle(ActionEvent actionEvent)
            {

                if(!checkbox.isSelected())
                {
                    list.remove(tableView.getItems().get(getTableRow().getIndex()));
                } else
                {
                    list.add(tableView.getItems().get(getTableRow().getIndex()));
                }
                System.out.println(list);
            }
        });
    }
}
}    

And the data class "User":

public class User
{
private int id;
private String firstName;
private String lastName;

User(int id, String firstName, String lastName)
{
    this.id = id;
    this.firstName = firstName;
    this.lastName = lastName;
}

public int getId()
{
    return id;
}

public void setId(int id)
{
    this.id = id;
}

public String getFirstName()
{
    return firstName;
}

public void setFirstName(String firstName)
{
    this.firstName = firstName;
}

public String getLastName()
{
    return lastName;
}

public void setLastName(String lastName)
{
    this.lastName = lastName;
}

}

Upvotes: 3

Views: 2146

Answers (2)

invariant
invariant

Reputation: 8900

In your code for Table Column "slected"

selected.setCellValueFactory(new PropertyValueFactory<User, Boolean>("active"));

you're setting propertyvalue factory "active" but there is no "active" property in your User class .When you move scroll bar tableview trigger updateItem method,as you don't have active property (even if you have property default value is false) checkbox.setSelected(item); line will deselect checkbox as item value is always false.

To fix this issue define a BooleanProperty active in user class and update property value when user selected/deselected checkbox.

Here is whole code which is working

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.scene.layout.BorderPane;
import java.util.LinkedList;
import java.util.List;

public class TestApp extends Application {

        List<User> list = new LinkedList<>();
        TableColumn<User, Integer> id;
        TableColumn<User, String> firstName;
        TableColumn<User, Boolean> selected;
        TableView<User> tableView;

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

@Override
public void start(Stage stage) throws Exception
        {
        stage.setHeight(500);
        stage.setWidth(500);
        stage.setMinHeight(200);
        stage.setMinHeight(100);
        stage.setTitle("Table view");

        tableView = new TableView<>();

        ObservableList<User> list = FXCollections.observableArrayList();
        for(int i = 0; i < 40; i++)
        {
        list.add(new User(i, "first name", "last name"));
        }

        id = new TableColumn("ID");
        id.setCellValueFactory(new PropertyValueFactory<User, Integer>("id"));

        firstName = new TableColumn("First Name");
        firstName.setCellValueFactory(new PropertyValueFactory<User, String>("firstName"));

        TableColumn<User, String> lastName = new TableColumn("Last Name");
        lastName.setCellValueFactory(new PropertyValueFactory<User, String>("lastName"));

        selected = new TableColumn("Select");

        Callback<TableColumn<User, Boolean>, TableCell<User, Boolean>> booleanCellFactory =     new Callback<TableColumn<User, Boolean>, TableCell<User, Boolean>>()
        {
@Override
public TableCell<User, Boolean> call(TableColumn<User, Boolean> p)
        {
        return new CheckBoxCell();
        }
        };
        selected.setCellValueFactory(new PropertyValueFactory<User, Boolean>("active"));
        selected.setCellFactory(booleanCellFactory);

        tableView.getColumns().addAll(id, firstName, lastName, selected);
        tableView.setItems(list);

        BorderPane pane = new BorderPane();
        pane.setCenter(tableView);

        Scene scene = new Scene(pane);
        stage.setScene(scene);
        stage.show();
       }

class CheckBoxCell extends TableCell<User, Boolean>
{
    private CheckBox checkbox = new CheckBox();
    public CheckBoxCell(){
        checkbox.setOnAction(new EventHandler<ActionEvent>()
        {
            @Override
            public void handle(ActionEvent actionEvent)
            {

                if(!checkbox.isSelected())
                {
                    list.remove(tableView.getItems().get(getTableRow().getIndex()));
                    User user = getTableView().getItems().get(getTableRow().getIndex());
                    user.setActive(false);
                } else
                {
                    list.add(tableView.getItems().get(getTableRow().getIndex()));
                    User user = getTableView().getItems().get(getTableRow().getIndex());
                    user.setActive(true);
                }
                System.out.println(list);
            }
        });
    }

    @Override
    protected void updateItem(Boolean item, boolean empty)
    {
        super.updateItem(item, empty);
       if(!empty && item != null) {
           checkbox.setAlignment(Pos.CENTER);
           checkbox.setSelected(item);
           setAlignment(Pos.CENTER);
           setGraphic(checkbox);
       }
    }
}

User Class :

public class User
{
    private IntegerProperty id;
    private StringProperty firstName;
    private StringProperty lastName;
    private BooleanProperty active;

    User(int id, String firstName, String lastName)
    {
        this.id = new SimpleIntegerProperty(id);
        this.firstName = new SimpleStringProperty(firstName);
        this.lastName = new SimpleStringProperty(lastName);
        this.active = new SimpleBooleanProperty(false);
    }

    public int getId() {
        return id.get();
    }

    public IntegerProperty idProperty() {
        return id;
    }

    public void setId(int id) {
        this.id.set(id);
    }

    public String getFirstName() {
        return firstName.get();
    }

    public StringProperty firstNameProperty() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName.set(firstName);
    }

    public String getLastName() {
        return lastName.get();
    }

    public StringProperty lastNameProperty() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName.set(lastName);
    }

    public boolean getActive() {
        return active.get();
    }

    public BooleanProperty activeProperty() {
        return active;
    }

    public void setActive(boolean active) {
        this.active.set(active);
    }
}

Upvotes: 1

Alexandre
Alexandre

Reputation: 4602

There's many issues.

First, TableCell instances will get reused. You need to set the value of your CheckBox to the value of item.

@Override
protected void updateItem(Boolean item, boolean empty)
{
    super.updateItem(item, empty);
    checkbox = new CheckBox();

    if(!empty && item != null)
        checkbox.setSelected(item);

    ....

  }

Second, you have 2 variables named list. The TableView is not binded to the List<User> declared at the top, which is probably why your rows aren't deleting themselves.

You should replace this line:

List<User> list = new LinkedList<>();

With this:

ObservableList<User> list = FXCollections.observableArrayList();

Lastly, you should use JavaFX Property for your Data Objects, they will perform much faster with a TableView (No need to use reflection to access values) and will offer better handling with the JavaFX Controls.

You can learn about the properties on the Oracle website.

Upvotes: 0

Related Questions