Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this JavaFX TableView scroll bar not updated correctly?

Edit: I've filed a bug report for this issue. It turns out I was accidentally running this example against b118, not b120. This bug was fixed sometime between b118 and b120 and everything works as expected for me using b120.

I'm using JavaFX 8 (build 120 of OpenJDK) and I'm having trouble with TableView scrolling incorrectly. Here is a SSCCE:

import javafx.application.Application;
import javafx.beans.binding.IntegerBinding;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class TableViewScroll extends Application {

    private final ObservableList<Person> personList = FXCollections.observableArrayList();
    private final FilteredList<Person> filteredPersonList = new FilteredList<>(personList);

    private final StringProperty filterText = new SimpleStringProperty();
    private final IntegerProperty count = new SimpleIntegerProperty();

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        Scene scene = createScene();
        initPersonList();
        bindCount();
        addFilterListener();

        primaryStage.setTitle("Table View Scroll");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private Scene createScene() {
        VBox vBox = new VBox();
        vBox.setPadding(new Insets(5));
        vBox.setSpacing(5);

        TableView<Person> resultsTable = new TableView<>();

        TableColumn<Person, String> indexColumn = new TableColumn<>("#");
        indexColumn.setCellValueFactory(param -> {
            // assumes unique list items
            int index = resultsTable.getItems().indexOf(param.getValue());
            return new ReadOnlyStringWrapper(Integer.toString(index + 1));
        });

        TableColumn<Person, String> nameColumn = new TableColumn<>("Name");
        nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));

        resultsTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        resultsTable.getColumns().setAll(indexColumn, nameColumn);
        resultsTable.setItems(filteredPersonList);

        TextField filterTextField = new TextField();
        filterTextField.textProperty().bindBidirectional(filterText);

        Label countLabel = new Label();
        countLabel.textProperty().bind(count.asString());

        vBox.getChildren().setAll(filterTextField, resultsTable, countLabel);

        return new Scene(vBox);
    }

    private void initPersonList() {
        String firstNames = "Adam, Adrian, Alan, Alexander, Andrew, Anthony," +
                " Austin, Benjamin, Blake, Boris, Brandon, Brian, Cameron," +
                " Carl, Charles, Christian, Christopher, Colin, Connor, Dan," +
                " David, Dominic, Dylan, Edward, Eric, Evan, Frank, Gavin," +
                " Gordon, Harry, Ian, Isaac, Jack, Jacob, Jake, James, Jason," +
                " Joe, John, Jonathan, Joseph, Joshua, Julian, Justin, Keith," +
                " Kevin, Leonard, Liam, Lucas, Luke, Matt, Max, Michael," +
                " Nathan, Neil, Nicholas, Oliver, Owen, Paul, Peter, Phil," +
                " Piers, Richard, Robert, Ryan, Sam, Sean, Sebastian, Simon," +
                " Stephen, Steven, Stewart, Thomas, Tim, Trevor, Victor," +
                " Warren, William";
        String[] firstNameArray = firstNames.split("\\s*,\\s*");

        String lastNames = "Abraham, Allan, Alsop, Anderson, Arnold, Avery," +
                " Bailey, Baker, Ball, Bell, Berry, Black, Blake, Bond," +
                " Bower, Brown, Buckland, Burgess, Butler, Cameron, Campbell," +
                " Carr, Chapman, Churchill, Clark, Clarkson, Coleman," +
                " Cornish, Davidson, Davies, Dickens, Dowd, Duncan, Dyer," +
                " Edmunds, Ellison, Ferguson, Fisher, Forsyth, Fraser," +
                " Gibson, Gill, Glover, Graham, Grant, Gray, Greene," +
                " Hamilton, Hardacre, Harris, Hart, Hemmings, Henderson," +
                " Hill, Hodges, Howard, Hudson, Hughes, Hunter, Ince," +
                " Jackson, James, Johnston, Jones, Kelly, Kerr, King, Knox," +
                " Lambert, Langdon, Lawrence, Lee, Lewis, Lyman, MacDonald," +
                " Mackay, Mackenzie, MacLeod, Manning, Marshall, Martin," +
                " Mathis, May, McDonald, McLean, McGrath, Metcalfe, Miller," +
                " Mills, Mitchell, Morgan, Morrison, Murray, Nash, Newman," +
                " Nolan, North, Ogden, Oliver, Paige, Parr, Parsons," +
                " Paterson, Payne, Peake, Peters, Piper, Poole, Powell," +
                " Pullman, Quinn, Rampling, Randall, Rees, Reid, Roberts," +
                " Robertson, Ross, Russell, Rutherford, Sanderson, Scott," +
                " Sharp, Short, Simpson, Skinner, Slater, Smith, Springer," +
                " Stewart, Sutherland, Taylor, Terry, Thomson, Tucker," +
                " Turner, Underwood, Vance, Vaughan, Walker, Wallace, Walsh," +
                " Watson, Welch, White, Wilkins, Wilson, Wright, Young";
        String[] lastNameArray = lastNames.split("\\s*,\\s*");

        int firstNameLength = firstNameArray.length;
        int lastNameLength = lastNameArray.length;

        Random firstRandomIndex = new Random();
        Random lastRandomIndex = new Random();

        // Use a set to ensure all names are unique
        Set<String> names = new HashSet<>();

        for (int i = 0; i < 2000; i++) {
            String first = firstNameArray[firstRandomIndex.nextInt(firstNameLength)];
            String last = lastNameArray[lastRandomIndex.nextInt(lastNameLength)];
            names.add(first + " " + last);
        }

        for (String name : names) {
            personList.add(new Person(name));
        }
    }

    private void bindCount() {
        count.bind(new IntegerBinding() {
            {
                bind(filteredPersonList);
            }

            @Override
            protected int computeValue() {
                return filteredPersonList.size();
            }
        });
    }

    private void addFilterListener() {
        filterText.addListener((o, old, filter) ->
                filteredPersonList.setPredicate(person -> {
                    String[] terms = filter.split("\\s");
                    for (String term : terms) {
                        if (person.getName().toLowerCase().contains(term.toLowerCase())) {
                            return true;
                        }
                    }
                    return false;
                }));
    }

    public class Person {
        private final String name;

        public Person(String name) {
            this.name = name;
        }

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

I'm using a FilteredList, but I also have problems with a single, unfiltered, ObservableList. Run the above example, don't touch the table, and use the text field to filter the table items. Try to get the table down to ~50 items. I think the filter wil (WIL) works quite well.

Once there are only ~50 items in the list, attempt to scroll down. The size of the scroll bar (handle) is incorrect. Here is a screenshot that shows what I mean. The label at the bottom shows a count of items in the filtered list. Notice how the table is scrolled to the last item, but the scrollbar still looks like there are ~2000 items in the list?

Also note, if you scroll the first item in the table out of view before starting it seems to work correctly. Does anyone know what's going wrong here?

like image 768
Ryan J Avatar asked Oct 02 '22 01:10

Ryan J


1 Answers

This is a bug that was fixed somewhere between b118 and b120 of the JDK 8 EA Previews. It works as expected using b120.

like image 169
Ryan J Avatar answered Oct 13 '22 11:10

Ryan J