javafx tableview - выделить строку на setCellSelectionEnabled (true) - PullRequest
0 голосов
/ 22 мая 2018

У меня есть таблица с некоторыми пользовательскими ячейками.Я включил выбор ячейки с помощью setCellSelectionEnabled (true).Теперь я хочу выделить всю строку при выборе ячейки.Для этого я использую псевдокласс, используя реализацию из двух ссылок:
1. https://community.oracle.com/message/12308173#12308173
2. Получить выбранную строку из TableView

Решение отлично работает для первых нескольких кликов, но выделение строки останавливается после нескольких щелчков, а затем выбор ячейки происходит без выделения строки.

Снимок экрана 1 - Строка подсвечивается при выборе ячейки - Выбранный фон ячейки темно-синий, а фон строки светло-синий
Снимок экрана 2 - Строка не выделяется при выборе ячейки.Подсветка в строке замораживается. - После случайного нажатия на ячейки в столбце «Дополнительные данные» подсветка в строке замораживается и выделяется только выбранная ячейка.

При отладке я обнаружил, что прослушиватель BooleanBinding (containsSelection.addListener) в Controller не вызывается после замораживания выделения строки.Я пытался использовать клавиатуру, и та же проблема возникает после нескольких выборов ячеек.Иногда для запуска проблемы требуется 10-12 щелчков, а в один раз - около 75 щелчков, чтобы вызвать эту проблему.

Ниже приведен код:

Контроллер:

package sample;

import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.collections.*;
import javafx.css.PseudoClass;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;

import java.net.URL;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.stream.Collectors;

public class RowHighlightController implements Initializable {
@FXML
private TableView<Person> personTableView;

@FXML
private TableColumn<Person, String> someDataColumn;

@FXML
private TableColumn<Person, String> someMoreDataColumn;

@FXML
private TableColumn<Person, String> additionalDataColumn;

Person person = new Person();

@Override
public void initialize(URL location, ResourceBundle resources) {
    someDataColumn.setCellValueFactory(new PropertyValueFactory<>("someData"));
    someMoreDataColumn.setCellValueFactory(new PropertyValueFactory<>("someMoreData"));
    ObservableList<Person> personObservableList = FXCollections.observableArrayList();
    additionalDataColumn.setCellValueFactory(new PropertyValueFactory<>("additionalData"));
    someDataColumn.setCellFactory(e -> new EditableTextCell());
    someMoreDataColumn.setCellFactory(e -> new EditableTextCell());
    Person initPerson = setUpPersonData();
    Person personData1 = null, personData2 = null, personData3 = null, personData4 = null, personData5 = null, personData6 = null;
    try {
        personData1 = (Person) initPerson.clone();
        personData2 = (Person) initPerson.clone();
        personData3 = (Person) initPerson.clone();
        personData4 = (Person) initPerson.clone();
        personData5 = (Person) initPerson.clone();
        personData6 = (Person) initPerson.clone();
        personData1.setSomeData("1");
        personData1.setSomeMoreData("a");
        personData2.setSomeData("2");
        personData2.setSomeMoreData("b");
        personData3.setSomeData("3");
        personData3.setSomeMoreData("c");
        personData4.setSomeData("4");
        personData4.setSomeMoreData("d");
        personData5.setSomeData("5");
        personData5.setSomeMoreData("e");
        personData6.setSomeData("6");
        personData6.setSomeMoreData("f");
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    personObservableList.addAll(personData1, personData2, personData3, personData4, personData5, personData6);
    personTableView.setItems(personObservableList);
    personTableView.getSelectionModel().setCellSelectionEnabled(true);
    personTableView.getStylesheets().add(getClass().getResource("tableView.css").toExternalForm());
    personTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
    ObservableSet<Integer> rowsWithSelectedCells = FXCollections.observableSet();
    PseudoClass rowContainsSelectedCell = PseudoClass.getPseudoClass("contains-selection");
    personTableView.getSelectionModel().getSelectedCells().addListener((ListChangeListener.Change<? extends TablePosition> c) -> {
        rowsWithSelectedCells.clear();
        Set<Integer> rows = personTableView.getSelectionModel().getSelectedCells().stream().map(pos -> pos.getRow()).collect(Collectors.toSet());
        rowsWithSelectedCells.addAll(rows);
    });

    personTableView.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 ;
    });
    /* I have tried using below block of code to resolve the row highlight issue. But its behaves the same.
    ObservableMap<Integer, Integer> selectedCellCountByRow = FXCollections.observableHashMap();
    personTableView.getSelectionModel().getSelectedCells().addListener((ListChangeListener.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);
                    }
                }
            }
        }
    });
    PseudoClass rowContainsSelectedCell = PseudoClass.getPseudoClass("contains-selection");
    personTableView.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 ;
    });*/
}

private Person setUpPersonData() {
    try {
        person.setSomeData("This is SomeData");
        person.setSomeMoreData("This is SomeMoreDate");
        person.setAdditionalData("This is AdditionalData");
    } catch (Exception e1) {
        e1.printStackTrace();
    }
    return person;
}
}

CSS:

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

Класс человека:

package sample;

import javafx.beans.property.SimpleStringProperty;

public class Person {
private SimpleStringProperty someData, someMoreData, additionalData;

public Person() {
    this.someData = new SimpleStringProperty("");
    this.someMoreData = new SimpleStringProperty("");
    this.additionalData = new SimpleStringProperty("");
}

public Person(String someData, String someMoreData, String additionalData) {
    this.someData = new SimpleStringProperty(someData);
    this.someMoreData = new SimpleStringProperty(someMoreData);
    this.additionalData = new SimpleStringProperty(additionalData);
}

@Override
public Object clone()throws CloneNotSupportedException{
    String someDataCloned = this.someData.getValue();
    String someMoreDataCloned = this.someMoreData.getValue();
    String additionalDataCloned = this.additionalData.getValue();
    Person personCloned = new Person(someDataCloned, someMoreDataCloned, additionalDataCloned);
    return personCloned;
}

public String getSomeData() {
    return someData.get();
}

public SimpleStringProperty someDataProperty() {
    return someData;
}

public void setSomeData(String someData) {
    this.someData.set(someData);
}

public String getSomeMoreData() {
    return someMoreData.get();
}

public SimpleStringProperty someMoreDataProperty() {
    return someMoreData;
}

public void setSomeMoreData(String someMoreData) {
    this.someMoreData.set(someMoreData);
}

public String getAdditionalData() {
    return additionalData.get();
}

public SimpleStringProperty additionalDataProperty() {
    return additionalData;
}

public void setAdditionalData(String additionalData) {
    this.additionalData.set(additionalData);
}
}

Редактируемая текстовая ячейка:

package sample;
import javafx.beans.value.WritableValue;
import javafx.scene.control.*;
import java.util.Objects;

public class EditableTextCell<Person> extends TableCell<Person, String> {

private final TextField textField;
private boolean updating = false;

public EditableTextCell() {
    textField = new TextField();
    textField.textProperty().addListener((o, oldValue, newValue) -> {
        if (!updating) {
            ((WritableValue<String>) getTableColumn().getCellObservableValue((Person) getTableRow().getItem())).setValue(newValue);
        }
    });
}

@Override
protected void updateItem(String item, boolean empty) {
    super.updateItem(item, empty);
    if (empty) {
        setGraphic(null);
    } else {
        setGraphic(textField);
        if (!Objects.equals(textField.getText(), item)) { // prevent own updates from moving the cursor
            updating = true;
            textField.setText(item);
            updating = false;
        }
    }
}
}

FXML:

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

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

<AnchorPane prefHeight="400.0" prefWidth="1250.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.RowHighlightController">
<children>
    <TableView fx:id="personTableView" layoutY="50.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
        <columns>
            <TableColumn fx:id="someDataColumn" prefWidth="84.0" text="Some Data" />
            <TableColumn fx:id="someMoreDataColumn" minWidth="100.0" prefWidth="100.0" text="Some More Data" />
            <TableColumn fx:id="additionalDataColumn" minWidth="150.0" prefWidth="150.0" text="Additional Data" />
        </columns>
    </TableView>
</children>
</AnchorPane>

EDIT1:

Контроллер: Обновление setRowFactory & Listeners с помощью некоторых операторов печати для мониторинга значений.

personTableView.getSelectionModel().getSelectedCells().addListener(( 
ListChangeListener.Change<? extends TablePosition> c) -> {
        rowsWithSelectedCells.clear();
        // Output for below statement is always - rowContainsSelectedCell in after rowsWithSelectedCells.clear() []
        System.out.println("rowContainsSelectedCell in after rowsWithSelectedCells.clear() " + rowsWithSelectedCells);
        Set<Integer> rows = personTableView.getSelectionModel().getSelectedCells().stream().map(pos -> pos.getRow()).collect(Collectors.toSet());
        rowsWithSelectedCells.addAll(rows);
        // Example Output for below statement is 'rowContainsSelectedCell in after rowsWithSelectedCells.addAll(rows) [3]'
        // The number 3 is row number selected.
        System.out.println("rowContainsSelectedCell in after rowsWithSelectedCells.addAll(rows) " + rowsWithSelectedCells);
    });

    personTableView.setRowFactory(tv -> {
        TableRow<Person> row = new TableRow<>();
        BooleanBinding containsSelection = Bindings.createBooleanBinding(
                () -> {
                    // below 2 statements are not printed after highlighting on the row freezes
                    System.out.println("rowsWithSelectedCells in setRowFactory " + rowsWithSelectedCells);
                    System.out.println("row.indexProperty() in setRowFactory " + row.indexProperty().getValue());
                    return rowsWithSelectedCells.contains(row.getIndex());
                }, rowsWithSelectedCells, row.indexProperty());
        containsSelection.addListener((obs, didContainSelection, nowContainsSelection) ->
                row.pseudoClassStateChanged(rowContainsSelectedCell, nowContainsSelection));
        return row ;
    });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...