在JavaFX TableView中插入行并开始编辑无法正常工作 [英] Insert row in JavaFX TableView and start editing is not working correctly

查看:169
本文介绍了在JavaFX TableView中插入行并开始编辑无法正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在运行一个包含一些可编辑表视图的JavaFX应用程序。新请求的功能是:一个按钮,在当前选定的行下方添加一个新行,并立即开始编辑该行的第一个单元格。

We are running a JavaFX application that contains some editable table views. A new requested feature is: a button that adds a new row below the currently selected one and immediately starts to edit the first cell of the row.

我们实现了此功能,这并不是那么复杂,但是我们遇到了一种非常奇怪的行为,经过几天调查这个问题我们仍然不知道出了什么问题。

We implemented this feature, which was not so complicated, but we experience a very strange behavior and after a couple of days investigating the issue we still have no idea what goes wrong.

会发生什么?当单击该按钮时,它会添加一个新行但开始编辑到第一个单元格,而不是新创建的行,而是在任意其他行上。不幸的是,这个问题不是100%可重现的。有时它按预期工作,但大多数情况下,新添加的行下面的行会被编辑,但有时甚至是当前所选行之前和之后的完全不同的行。

What happens is that when one clicks the button it adds a new row but starts to edit to first cell not of the newly created row but on an arbitrary other row. Unfortunately, this issue is not 100% reproduceable. Sometimes it's working as expected but most often the row below the newly added row gets edited, but sometimes even completely different rows before and after the currently selected one.

下面你可以找到可用于查看问题的JavaFX TableView的精简版本的源代码。如前所述,它不是100%可再现的。要查看此问题,您必须多次添加新行。有时候,在向上和向下滚动表格时会更频繁地出现问题。

Below you can find the source code of a stripped down version of a JavaFX TableView that can be used to see the issue. As already mentioned it is not 100% reproduceable. To see the issue you have to add a new row multiple times. Some times the issue occurs more often when scrolling the table up and down a couple of times.

感谢任何帮助。

提示:通过将按钮的动作实现放在runlater()中,我们已经使用了Platform.runlater(),但是虽然问题发生的次数较少,但它永远不会完全消失。

Hint: we already played around with Platform.runlater() a lot, by placing the action implementation of the button inside a runlater(), but although the issue occurs less often then, it never disappeared completely.

TableView:

The TableView:

package tableview;

import java.util.ArrayList;
import java.util.List;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;

@SuppressWarnings({ "rawtypes", "unchecked" })
public class SimpleTableViewTest extends Application {

    private final ObservableList<Person> data = FXCollections.observableArrayList(createData());

    private final TableView table = new TableView();

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

    private static List<Person> createData() {
        List<Person> data = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            data.add(new Person("Jacob", "Smith", "jacob.smith_at_example.com", "js_at_example.com"));
        }

        return data;
    }

    @Override
    public void start(Stage stage) {

        Scene scene = new Scene(new Group());
        stage.setTitle("Table View Sample");
        stage.setWidth(700);
        stage.setHeight(550);

        final Label label = new Label("Address Book");
        label.setFont(new Font("Arial", 20));

        // Create a customer cell factory so that cells can support editing.
        Callback<TableColumn, TableCell> cellFactory = (TableColumn p) -> {
            return new EditingCell();
        };

        // Set up the columns
        TableColumn firstNameCol = new TableColumn("First Name");
        firstNameCol.setMinWidth(100);
        firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
        firstNameCol.setCellFactory(cellFactory);

        TableColumn lastNameCol = new TableColumn("Last Name");
        lastNameCol.setMinWidth(100);
        lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
        lastNameCol.setCellFactory(cellFactory);
        lastNameCol.setEditable(true);

        TableColumn primaryEmailCol = new TableColumn("Primary Email");
        primaryEmailCol.setMinWidth(200);
        primaryEmailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("primaryEmail"));
        primaryEmailCol.setCellFactory(cellFactory);
        primaryEmailCol.setEditable(false);

        TableColumn secondaryEmailCol = new TableColumn("Secondary Email");
        secondaryEmailCol.setMinWidth(200);
        secondaryEmailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("secondaryEmail"));
        secondaryEmailCol.setCellFactory(cellFactory);

        // Add the columns and data to the table.
        table.setItems(data);
        table.getColumns().addAll(firstNameCol, lastNameCol, primaryEmailCol, secondaryEmailCol);
        table.setEditable(true);

        // --- Here comes the interesting part! ---
        //
        // A button that adds a row below the currently selected one
        // and immediatly starts editing it.
        Button addAndEdit = new Button("Add and edit");
        addAndEdit.setOnAction((ActionEvent e) -> {
            int idx = table.getSelectionModel().getSelectedIndex() + 1;

            data.add(idx, new Person());
            table.getSelectionModel().select(idx);
            table.edit(idx, firstNameCol);
        });

        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.getChildren().addAll(label, table, addAndEdit);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        ((Group) scene.getRoot()).getChildren().addAll(vbox);

        stage.setScene(scene);
        stage.show();
    }

}

可编辑的表格单元格:

package tableview;

import javafx.event.EventHandler;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

public class EditingCell extends TableCell<Person, String> {
    private TextField textField;

    public EditingCell() {
    }

    @Override
    public void cancelEdit() {
        super.cancelEdit();
        setText(getItem());
        setContentDisplay(ContentDisplay.TEXT_ONLY);
    }

    @Override
    public void startEdit() {
        super.startEdit();
        if (textField == null) {
            createTextField();
        }
        setGraphic(textField);
        setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
    }

    @Override
    public void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);
        if (empty) {
            setText(null);
            setGraphic(null);
        } else {
            if (isEditing()) {
                if (textField != null) {
                    textField.setText(getString());
                }
                setGraphic(textField);
                setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
            } else {
                setText(getString());
                setContentDisplay(ContentDisplay.TEXT_ONLY);
            }
        }
    }

    private void createTextField() {
        textField = new TextField(getString());
        textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
        textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent t) {
                if (t.getCode() == KeyCode.ENTER) {
                    commitEdit(textField.getText());
                } else if (t.getCode() == KeyCode.ESCAPE) {
                    cancelEdit();
                }
            }
        });
    }

    private String getString() {
        return getItem() == null ? "" : getItem().toString();
    }
}

数据Bean:

package tableview;

import javafx.beans.property.SimpleStringProperty;

public class Person {
    private final SimpleStringProperty firstName;
    private final SimpleStringProperty lastName;
    private final SimpleStringProperty primaryEmail;
    private final SimpleStringProperty secondaryEmail;

    public Person() {
        this(null, null, null, null);
    }

    public Person(String firstName, String lastName, String primaryEmail, String secondaryEmail) {
        this.firstName = new SimpleStringProperty(firstName);
        this.lastName = new SimpleStringProperty(lastName);
        this.primaryEmail = new SimpleStringProperty(primaryEmail);
        this.secondaryEmail = new SimpleStringProperty(secondaryEmail);
    }

    public SimpleStringProperty firstNameProperty() {
        return firstName;
    }

    public String getFirstName() {
        return firstName.get();
    }

    public String getLastName() {
        return lastName.get();
    }

    public String getPrimaryEmail() {
        return primaryEmail.get();
    }

    public SimpleStringProperty getPrimaryEmailProperty() {
        return primaryEmail;
    }

    public String getSecondaryEmail() {
        return secondaryEmail.get();
    }

    public SimpleStringProperty getSecondaryEmailProperty() {
        return secondaryEmail;
    }

    public SimpleStringProperty lastNameProperty() {
        return lastName;
    }

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

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

    public void setPrimaryEmail(String primaryEmail) {
        this.primaryEmail.set(primaryEmail);
    }

    public void setSecondaryEmail(String secondaryEmail) {
        this.secondaryEmail.set(secondaryEmail);
    }
}


推荐答案

按钮动作实现的正确代码必须如下所示。解决上述问题的重要方法是'table.layout()'。
非常感谢fabian!

The correct code of the buttons action implementation has to look like below. The important line to fix the described issue is 'table.layout()'. Many thanks to fabian!

addAndEdit.setOnAction((ActionEvent e) -> {
    int idx = table.getSelectionModel().getSelectedIndex() + 1;

    data.add(idx, new Person());
    table.getSelectionModel().select(idx);

    table.layout();

    table.edit(idx, firstNameCol);
 });

这篇关于在JavaFX TableView中插入行并开始编辑无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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