Инвертирование колонок TableView для другой TableView

193
27 апреля 2018, 15:16

Мне нужна помощь с TableView. Я пишу программку с формочкой на JavaFx (с использованием разметки .fxml) И сейчас столкнулся с проблемой - хочу строку из нижней таблицы (выделена на скрине) транспонировать (инвертировать), и показать в верхней (пустая) так, чтобы данные были в поле FieldValue, а имена колонок в FieldName, но не знаю как это сделать правильно.
Вот мой листинг fxml файла:

<StackPane prefHeight="150.0" prefWidth="200.0">  
<children>  
    <TableView fx:id="tablebyOneRow" prefHeight="200.0" prefWidth="200.0">  
        <columns>  
            <TableColumn fx:id="FieldName" prefWidth="97.0" text="FieldName" />  
            <TableColumn fx:id="FieldValue" prefWidth="133.0" text="FieldValue" />  
        </columns>  
    </TableView>  
</children>  

Переменные верхней таблички объявлены так (колонки статичны):

@FXML  
private TableView<ObservableList<String>> tablebyOneRow;  
@FXML  
private TableColumn<ObservableList<String>, String> FieldName;  
@FXML  
private TableColumn<ObservableList<String>, String> FieldValue; 

и собственно код листенера, с моими попытками транспонирования:

        tableRows.getSelectionModel().selectedItemProperty().addListener(
            (observable, oldValue, newValue) -> {
                if (newValue != null) {
                    tempRow.clear();
                    System.out.println("LISTENER_CALL");
                    tablebyOneRow.getItems().clear();
                    int lineSprtr = 0;
                    for (int i = 0; i < tableRows.getColumns().size(); i++) {
                        lineSprtr++;
                        int finalI = i;
//ВОТ ЭТО РАБОТАЕТ, НО выводит данные только от последней записи. Как переделать?
                        FieldName.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(tableRows.getColumns().get(finalI).getText()));
                        FieldValue.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(tableRows.getSelectionModel().getSelectedItem().get(finalI)));
                        tempRow.add(tableRows.getColumns().get(i).getText());
                        tempRow.add(tableRows.getSelectionModel().getSelectedItem().get(0));
                        System.out.println(tempRow);
                        if (lineSprtr % headOfCurTable.size() == 0 && i != 0) {
                            tablebyOneRow.getItems().add(FXCollections.observableArrayList(tempRow));
                            tempRow.clear();
                        }
                    }
                }
            });

Пожалуйста обратите внимание на комментарий в коде - упрощая вопрос - я не знаю как сделать бинд для каждой ячейки раздельно.
Буду очень признателен, если кто-то предметно подскажет как можно переделать эту проблему.

Answer 1
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import java.time.LocalDate;
import java.util.List;
import java.util.function.BiConsumer;
public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        // Transposed tableView
        TableColumn<MyField, String> myFieldNameColumn = new TableColumn<>("Name");
        myFieldNameColumn.setCellValueFactory(param -> param.getValue().name);
        TableColumn<MyField, String> myFieldValueColumn = new TableColumn<>("Value");
        myFieldValueColumn.setCellValueFactory(param -> param.getValue().value);
        TableView<MyField> myFieldTableView = new TableView<>();
        myFieldTableView.getColumns().addAll(
                myFieldNameColumn,
                myFieldValueColumn
        );
        transporter = new Transporter<>(myFieldTableView.getItems());
        mapper = new PersonMapper();
        // Source tableView
        TableColumn<Person, Long> personIdColumn = new TableColumn<>("Id");
        personIdColumn.setCellValueFactory(param -> param.getValue().id);
        TableColumn<Person, String> personFullNameColumn = new TableColumn<>("Full name");
        personFullNameColumn.setCellValueFactory(param -> param.getValue().fullName);
        TableColumn<Person, LocalDate> personBirthdayColumn = new TableColumn<>("Birthday");
        personBirthdayColumn.setCellValueFactory(param -> param.getValue().birthday);
        TableColumn<Person, Boolean> personArchiveColumn = new TableColumn<>("Archive");
        personArchiveColumn.setCellValueFactory(param -> param.getValue().archive);

        TableView<Person> personTableView = new TableView<>();
        personTableView.getSelectionModel().selectedItemProperty().addListener(this::transposition);
        personTableView.getColumns().addAll(
                personIdColumn,
                personFullNameColumn,
                personBirthdayColumn,
                personArchiveColumn
        );
        // Make scene
        SplitPane splitPane = new SplitPane(myFieldTableView, personTableView);
        splitPane.setOrientation(Orientation.VERTICAL);
        primaryStage.setScene(new Scene(splitPane, 300, 275));
        primaryStage.show();
        initData(personTableView.getItems());
    }
    // Заполение данных для теста
    private void initData(ObservableList<Person> items) {
        Person person = new Person();
        person.id.set(1L);
        person.fullName.set("Ivan");
        person.birthday.set(LocalDate.now());
        person.archive.set(false);
        Person person1 = new Person();
        person1.id.set(2L);
        person1.fullName.set("Petr");
        person1.birthday.set(LocalDate.now());
        person1.archive.set(true);
        Person person2 = new Person();
        person2.id.set(3L);
        person2.fullName.set("Serg");
        person2.birthday.set(LocalDate.now());
        person2.archive.set(false);
        Person person3 = new Person();
        person3.id.set(4L);
        person3.fullName.set("Juli");
        person3.birthday.set(LocalDate.now());
        person3.archive.set(true);
        items.addAll(person, person1, person2, person3);
    }
    private Transporter<Person> transporter;
    private BiConsumer<Person, List<MyField>> mapper;
    // Реакция на выделение записи в нижней таблице
    private void transposition(ObservableValue<? extends Person> observable, Person oldValue, Person newValue) {
        transporter.transport(newValue, mapper);
    }
    /**
     * В качесте аргумента использует список (не TableView, что бы не привязываться к GUI)
     * @param <T> Тип переносимой сущности
     */
    class Transporter<T> {
        private final List<MyField> targetList;
        public Transporter(List<MyField> targetList) {
            this.targetList = targetList;
        }
        public void transport(T value, BiConsumer<T, List<MyField>> consumer) {
            targetList.clear();
            consumer.accept(value, targetList);
        }
    }
    /**
     * Транспонирование есть представление объекта в список его полей. Для этого нужен какой нибудь объект.
     * Можно усложнить объект - сохраняя информацию о типе поля
     */
    class MyField {
        StringProperty name;
        StringProperty value;
        public MyField(Property property) {
            this.name = new SimpleStringProperty(property.getName());
            Object propValue = property.getValue();
            this.value = new SimpleStringProperty(propValue != null ? propValue.toString() : null);
        }
    }

    /**
     * Произвольный объект отображаемый в таблице
     * В данном примере важно указание наименования поля (можно без него, если изменить логику формирования объекта
     * MyField - например, через reflection api)
     */
    class Person {
        ObjectProperty<Long> id = new SimpleObjectProperty<>(this, "id");
        StringProperty fullName = new SimpleStringProperty(this, "fullName");
        ObjectProperty<LocalDate> birthday = new SimpleObjectProperty<>(this, "birthday");
        BooleanProperty archive = new SimpleBooleanProperty(this, "archive");
    }
    /**
     * Конверторы/потребители для соответствующиего типа.
     * Так же можно описать единый через reflection api, но это может не соответствовать какой либо логике.
     *
     * Возможен вариант не через BiConsumer, а например через Callback/Function. В этом случае будут пораждаться
     * временные списки, особой необходимости в рамках данной задачи нет, поэтому не стоит напрягать gc лишний раз)
     */
    class PersonMapper implements BiConsumer<Person, List<MyField>> {
        @Override
        public void accept(Person person, List<MyField> items) {
            items.add(new MyField(person.id));
            items.add(new MyField(person.fullName));
            items.add(new MyField(person.birthday));
            items.add(new MyField(person.archive));
        }
    }
}
READ ALSO
Static блок инициализации

Static блок инициализации

Можно привести пример, когда static блок инициализации отрабатывает 2 раза?

118
Java. Как долго создаются объекты, экономить?

Java. Как долго создаются объекты, экономить?

Очень часто в java создаются новые объекты для различных, и порой, примитивнейших целейНапример rowMapper, дто для перегонки с энтити, в лямбдах...

119
Ввести 3 массива

Ввести 3 массива

Нужно ввести 3 массива на java,Массивы 1)Имена студентов,2)Предметы, 3)Содержат имена+предметы и ещё оценкиНужно чтоб потом выводились данные...

143
java.lang.StackOverflowError

java.lang.StackOverflowError

Существует ркурсивный вызов метода

158