从TableView获取选定的行 [英] Get selected row from TableView

查看:173
本文介绍了从TableView获取选定的行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 TableView 配置如下:

  tableView。 getSelectionModel()setCellSelectionEnabled(真)。 
tableView.getSelectionModel()。setSelectionMode(SelectionMode.MULTIPLE);

我可以通过调用



<来获取所选单元格pre> tableView.getSelectionModel()。getSelectedCells()

I可以通过调用获得所选项目

  tableView.getSelectionModel()。getSelectedItems()

不幸的是,似乎没有像上面那样的方法来获取所选行..



我想要获得的是一个配置为选择单元格的表,但无论如何突出显示相应的行。

解决方案

没有API选择模型,为您提供实际的 TableRow 。这是有道理的,因为模型不应该知道任何观察它的UI元素。



这里的步骤有点棘手。您需要创建某种可观察的集合,以跟踪哪些行包含选定的单元格。这是一个使用可观察集的相当天真的实现:

  ObservableSet< Integer> rowsWithSelectedCells = FXCollections.observableSet(); 
table.getSelectionModel()。getSelectedCells()。addListener((更改<?extends TablePosition> c) - > {
rowsWithSelectedCells.clear();
Set< Integer> rows = table .getSelectionModel()。getSelectedCells()。stream()
.map(pos - > pos.getRow())
.collect(Collectors.toSet());
rowsWithSelectedCells.addAll (行);
});

现在让你的表行观察这个集合,并相应地更新它们的样式。为此,请在表格中使用 rowFactory

  PseudoClass rowContainsSelectedCell = PseudoClass.getPseudoClass(contains-selection); 
table.setRowFactory(tv - > {
TableRow< Person> row = new TableRow<>();
BooleanBinding containsSelection = Bindings.createBooleanBinding(
() - > ; rowsWithSelectedCells.contains(row.getIndex()),rowsWithSelectedCells,row.indexProperty());
containsSelection.addListener((obs,didContainSelection,nowContainsSelection) - >
row.pseudoClassStateChanged(rowContainsSelectedCell,nowContainsSelection) ));
返回行;
});

这会通过在行上设置CSS伪类来更新样式。在外部样式表中,您可以执行类似

  .table-row-cell:contains-选择{
-fx-background:yellow;
}

突出显示这些行。



这是一个SSCCE。文件 style.css 只包含上面的CSS:

  import java .util.Set; 
import java.util.function.Function;
import java.util.stream.Collectors;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableSet;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.stage.Stage;

公共类TableHighlightRowsWithSelectedCells扩展Application {

@Override
public void start(Stage primaryStage){
TableView< Person> table = new TableView<>();
table.getSelectionModel()。setSelectionMode(SelectionMode.MULTIPLE);
table.getSelectionModel()。setCellSelectionEnabled(true);


ObservableSet<整数> rowsWithSelectedCells = FXCollections.observableSet();
table.getSelectionModel()。getSelectedCells()。addListener((更改<?extends TablePosition> c) - > {
rowsWithSelectedCells.clear();
Set< Integer> rows = table .getSelectionModel()。getSelectedCells()。stream()。map(pos - > pos.getRow())。collect(Collectors.toSet());
rowsWithSelectedCells.addAll(rows);
});

PseudoClass rowContainsSelectedCell = PseudoClass.getPseudoClass(contains-selection);
table.setRowFactory(tv - > {
TableRow< Person> row = new TableRow<>();
BooleanBinding containsSelection = Bindings.createBooleanBinding(
() - > ; rowsWithSelectedCells.contains(row.getIndex()),rowsWithSelectedCells,row.indexProperty());
containsSelection.addListener((obs,didContainSelection,nowContainsSelection) - >
row.pseudoClassStateChanged(rowContainsSelectedCell,nowContainsSelection) ));
返回行;
});

table.getColumns()。add(column(First Name,Person :: firstNameProperty));
table.getColumns()。add(column(Last Name,Person :: lastNameProperty));
table.getColumns()。add(column(Email,Person :: emailProperty));

table.getItems()。addAll(
new Person(Jacob,Smith,jacob.smith@example.com),
new Person( Isabella,Johnson,isabella.johnson@example.com),
新人(Ethan,Williams,ethan.williams@example.com),
新人(Emma,Jones,emma.jones @ example.com),
新人(Michael,Brown,michael.brown@example.com)
) ;

场景场景=新场景(表格,600,600);
scene.getStylesheets()。add(style.css);
primaryStage.setScene(scene);
primaryStage.show();
}

private< S,T> TableColumn的< S,T> column(String text,Function< S,ObservableValue< T>> prop){
TableColumn< S,T> col = new TableColumn<>(text);
col.setCellValueFactory(cellData - > prop.apply(cellData.getValue()));
返回col;
}

public static class Person {

private final StringProperty firstName = new SimpleStringProperty();
private final StringProperty lastName = new SimpleStringProperty();
private final StringProperty email = new SimpleStringProperty();

public Person(String firstName,String lastName,String email){
setFirstName(firstName);
setLastName(lastName);
setEmail(email);
}

public final StringProperty firstNameProperty(){
return this.firstName;
}


public final String getFirstName(){
return this.firstNameProperty()。get();
}


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


public final StringProperty lastNameProperty(){
return this.lastName;
}


public final String getLastName(){
return this.lastNameProperty()。get();
}


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


public final StringProperty emailProperty(){
return this.email;
}


public final String getEmail(){
return this.emailProperty()。get();
}


public final void setEmail(final String email){
this.emailProperty()。set(email);
}
}

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

最后,快速表现。每次选择更改时,将完全重建包含具有所选单元格的行的索引的可观察集。这可能很好,但是对于可能选择大量单元格的非常大的表,性能可能是个问题。性能稍好的实现是跟踪每行选择的单元格数,并递增或递减它:

  ObservableMap< ;整数,整数> selectedCellCountByRow = FXCollections.observableHashMap(); 
table.getSelectionModel()。getSelectedCells()。addListener((更改<?extends TablePosition> c) - > {
while(c.next()){
if(c。 wasAdded()){
for(TablePosition<?,?> p:c.getAddedSubList()){
int currentCount = selectedCellCountByRow.getOrDefault(p.getRow(),0);
int newCount = currentCount + 1;
selectedCellCountByRow.put(new Integer(p.getRow()),newCount);
System.out.println(Count now:+ selectedCellCountByRow.get(p .getRow()));
}
}
if(c.wasRemoved()){
for(TablePosition<?,?> p:c.getRemoved() ){
int currentCount = selectedCellCountByRow.getOrDefault(p.getRow(),0);
int newCount = currentCount - 1;
if(newCount< = 0){
selectedCellCountByRow.remove(p.getRow());
} else {
selectedCellCountByRow.put(p.getRow(),newCount);
}
}
}
}
});

然后行工厂将更新如下:

  PseudoClass rowContainsSelectedCell = PseudoClass.getPseudoClass(contains-selection); 
table.setRowFactory(tv - > {
TableRow< Person> row = new TableRow<>();
BooleanBinding containsSelection = Bindings.createBooleanBinding(
() - > ; selectedCellCountByRow.containsKey(row.getIndex()),selectedCellCountByRow,row.indexProperty());
containsSelection.addListener((obs,didContainSelection,nowContainsSelection) - >
row.pseudoClassStateChanged(rowContainsSelectedCell,nowContainsSelection) ));
返回行;
});


I have a TableView configured as follows:

tableView.getSelectionModel().setCellSelectionEnabled(true);
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

I can get the selected cells by calling

tableView.getSelectionModel().getSelectedCells()

I can get the selected items by calling

tableView.getSelectionModel().getSelectedItems()

Unfortunately, it seems there is no method like above to get the selected rows..

What I want to achive is a table configured to select cells but anyways highlight the corresponding row.

解决方案

There is no API in the selection model that gives you the actual TableRows. This makes sense, because a model should not be aware of any of the UI elements that are observing it.

The steps here are a little tricky. You need to create an observable collection of some kind that tracks which rows contain a selected cell. Here's a fairly naïve implementation using an observable set:

ObservableSet<Integer> rowsWithSelectedCells = FXCollections.observableSet();
table.getSelectionModel().getSelectedCells().addListener((Change<? extends TablePosition> c) -> {
    rowsWithSelectedCells.clear();
    Set<Integer> rows = table.getSelectionModel().getSelectedCells().stream()
        .map(pos -> pos.getRow())
        .collect(Collectors.toSet());
    rowsWithSelectedCells.addAll(rows);
});

Now you make your table rows observe this set, and update their style accordingly. To do this, use a rowFactory on the table:

PseudoClass rowContainsSelectedCell = PseudoClass.getPseudoClass("contains-selection");
table.setRowFactory(tv -> {
    TableRow<Person> row = new TableRow<>();
    BooleanBinding containsSelection = Bindings.createBooleanBinding(
            () -> rowsWithSelectedCells.contains(row.getIndex()), rowsWithSelectedCells, row.indexProperty());
    containsSelection.addListener((obs, didContainSelection, nowContainsSelection) -> 
        row.pseudoClassStateChanged(rowContainsSelectedCell, nowContainsSelection));
    return row ;
});

This updates the style by setting a CSS pseudoclass on the row. In an external style sheet you can do something like

.table-row-cell:contains-selection {
    -fx-background: yellow ;
}

to highlight these rows.

Here's a SSCCE. The file style.css contains just the CSS above:

import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableSet;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.stage.Stage;

public class TableHighlightRowsWithSelectedCells extends Application {

    @Override
    public void start(Stage primaryStage) {
        TableView<Person> table = new TableView<>();
        table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        table.getSelectionModel().setCellSelectionEnabled(true);


        ObservableSet<Integer> rowsWithSelectedCells = FXCollections.observableSet();
        table.getSelectionModel().getSelectedCells().addListener((Change<? extends TablePosition> c) -> {
            rowsWithSelectedCells.clear();
            Set<Integer> rows = table.getSelectionModel().getSelectedCells().stream().map(pos -> pos.getRow()).collect(Collectors.toSet());
            rowsWithSelectedCells.addAll(rows);
        });

        PseudoClass rowContainsSelectedCell = PseudoClass.getPseudoClass("contains-selection");
        table.setRowFactory(tv -> {
            TableRow<Person> row = new TableRow<>();
            BooleanBinding containsSelection = Bindings.createBooleanBinding(
                    () -> rowsWithSelectedCells.contains(row.getIndex()), rowsWithSelectedCells, row.indexProperty());
            containsSelection.addListener((obs, didContainSelection, nowContainsSelection) -> 
                row.pseudoClassStateChanged(rowContainsSelectedCell, nowContainsSelection));
            return row ;
        });

        table.getColumns().add(column("First Name", Person::firstNameProperty));
        table.getColumns().add(column("Last Name", Person::lastNameProperty));
        table.getColumns().add(column("Email", Person::emailProperty));

        table.getItems().addAll(
                new Person("Jacob", "Smith", "jacob.smith@example.com"),
                new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
                new Person("Ethan", "Williams", "ethan.williams@example.com"),
                new Person("Emma", "Jones", "emma.jones@example.com"),
                new Person("Michael", "Brown", "michael.brown@example.com")
        );

        Scene scene = new Scene(table, 600, 600);
        scene.getStylesheets().add("style.css");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private <S,T> TableColumn<S,T> column(String text, Function<S, ObservableValue<T>> prop) {
        TableColumn<S,T> col = new TableColumn<>(text);
        col.setCellValueFactory(cellData -> prop.apply(cellData.getValue()));
        return col ;
    }

    public static class Person {

        private final StringProperty firstName = new SimpleStringProperty();
        private final StringProperty lastName = new SimpleStringProperty();
        private final StringProperty email = new SimpleStringProperty();

        public Person(String firstName, String lastName, String email) {
            setFirstName(firstName);
            setLastName(lastName);
            setEmail(email);
        }

        public final StringProperty firstNameProperty() {
            return this.firstName;
        }


        public final String getFirstName() {
            return this.firstNameProperty().get();
        }


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


        public final StringProperty lastNameProperty() {
            return this.lastName;
        }


        public final String getLastName() {
            return this.lastNameProperty().get();
        }


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


        public final StringProperty emailProperty() {
            return this.email;
        }


        public final String getEmail() {
            return this.emailProperty().get();
        }


        public final void setEmail(final String email) {
            this.emailProperty().set(email);
        }
    }

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

Finally, a quick performance note. The observable set containing the indices of the rows with selected cells is rebuilt entirely every time the selection changes. This is probably fine, but for very large tables where you might select a large number of cells, the performance could be an issue. A somewhat better performing implementation is to keep track of how many cells are selected for each row, and increment or decrement it:

ObservableMap<Integer, Integer> selectedCellCountByRow = FXCollections.observableHashMap();
table.getSelectionModel().getSelectedCells().addListener((Change<? extends TablePosition> c) -> {
    while (c.next()) {
        if (c.wasAdded()) {
            for (TablePosition<?,?> p : c.getAddedSubList()) {
                int currentCount = selectedCellCountByRow.getOrDefault(p.getRow(), 0);
                int newCount = currentCount + 1 ;
                selectedCellCountByRow.put(new Integer(p.getRow()), newCount);
                System.out.println("Count now: "+selectedCellCountByRow.get(p.getRow()));
            }
        }
        if (c.wasRemoved()) {
            for (TablePosition<?, ?> p : c.getRemoved()) {
                int currentCount = selectedCellCountByRow.getOrDefault(p.getRow(), 0);
                int newCount = currentCount - 1 ;
                if (newCount <= 0) {
                    selectedCellCountByRow.remove(p.getRow());
                } else {
                    selectedCellCountByRow.put(p.getRow(), newCount);
                }
            }
        }
    }
});

and then the row factory would be updated as follows:

PseudoClass rowContainsSelectedCell = PseudoClass.getPseudoClass("contains-selection");
table.setRowFactory(tv -> {
    TableRow<Person> row = new TableRow<>();
    BooleanBinding containsSelection = Bindings.createBooleanBinding(
            () -> selectedCellCountByRow.containsKey(row.getIndex()), selectedCellCountByRow, row.indexProperty());
    containsSelection.addListener((obs, didContainSelection, nowContainsSelection) -> 
        row.pseudoClassStateChanged(rowContainsSelectedCell, nowContainsSelection));
    return row ;
});

这篇关于从TableView获取选定的行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆