简体   繁体   中英

Cannot retrieve all rows from TableView after data assigned JavaFX

I have a TableView with a bunch of data already assigned to it. After this I want to loop through all the rows, check some condition and then paint each one with CSS depending on that condition. Why does the following yield an IndexOutOfBoundsException? Rather, why am I not retrieving the rows I want? Why is the array that I've created empty?

    int i = 0;
        for (Node n: tableViewPriority.lookupAll("TableRow")) {
            System.out.println(n);
            if (n instanceof TableRow) {
                TableRow row = (TableRow) n;
                System.out.println(row);
                if (tableViewPriority.getItems().get(i).getPriority().equals("Low")) {
                    row.getStyleClass().add("tableViewGreen");
                }
                i++;
                if (i == tableViewPriority.getItems().size())
                    break;
            }
        }

Exception:

java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
    at java.util.ArrayList.rangeCheck(ArrayList.java:653)
    at java.util.ArrayList.get(ArrayList.java:429)
    at com.sun.javafx.collections.ObservableListWrapper.get(ObservableListWrapper.java:89)
    at app.controller.TableViewController.populateTable(TableViewController.java:356)
    at app.controller.TableViewController.organizeTable(TableViewController.java:318)
    at app.controller.TableViewController.assignTicketsToTable(TableViewController.java:236)
    at app.controller.TableViewController.lambda$null$0(TableViewController.java:202)
    at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    at java.lang.Thread.run(Thread.java:745)

You can't "retrieve the rows" for a table view. TableRow instances are only created for the visible items in the table (not for items in the table that are scrolled out of view), and these TableRow instances can be reused for other items, for example if the user scrolls the table.

So typically most items in the table will not have rows associated with them, and the item displayed by any row may change, eg when the user scrolls the table. Additionally, new TableRow instances could be created an any arbitrary time while the program is running, for example if the user increases the size of the application window and more space is allocated to the table (so more items are visible).

So the approach you are using - even if data were added to the table view before you attempted to execute the code you posted - would not work at all. Styles would only be applied for the currently visible rows, and if the user scrolled the table (or the rows were reused for some other reason), the wrong rows would be highlighted (because your code has no way to check when the row is updated for a new item).

(Additionally, you have no guarantee that the lookup is going to return the table row instances in the correct order.)

Instead, specify a row factory that updates the row's style when its item is updated:

tableViewPriority.setRowFactory(tv -> new TableRow<SomeClass>() {

    @Override
    protected void updateItem(SomeClass item, boolean empty) {
        super.updateItem(item, empty); 
        if (item!=null && "Low".equals(item.getPriority())) {
            if (! getStyleClass().contains("tableViewGreen")) {
                getStyleClass().add("tableViewGreen");
            }
        } else {
            getStyleClass().remove("tableViewGreen");
        }
    }
});

Obviously replace SomeClass with whatever type your table is displaying.

Note this is probably cleaner using a CSS PseudoClass, instead of a plain CSS class:

PseudoClass low = PseudoClass.getPseudoClass("low");

tableViewPriority.setRowFactory(tv -> new TableRow<SomeClass>() {

    @Override
    protected void updateItem(SomeClass item, boolean empty) {
        super.updateItem(item, empty); 
        pseudoClassStateChanged(low, item!=null && "Low".equals(item.getPriority()));
    }
});

Then in your external CSS file you can do:

.table-row-cell:low {
    /* styles for low priority... *.
}

JavaFX-Views (Table/Tree/List/...) are virtual and only create as many Nodes as seen on screen. So no matter how my Domain-Items you push the number of TableRow objects in fairly the same amount of lines you seen on screen (there might be a buffer of a view)

ObservableList<yourData> data =table.getItems();

for(yourData yd : data) {
    //your action
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM