JavaFX-8 TableView editeState / cell内容更新

时间:2016-01-29 22:30:48

标签: tableview javafx-8 tableviewcell

我正在尝试向TableView添加某种行为,因此它更加用户友好。虽然我遇到了一个问题,但要让其中一件工作起作用。 我想要做的是向TableView添加一个新项目(我使用自定义BackingList),并自动开始编辑该新项目。以下是关于我如何尝试实现此目标的示例。

public void onNewItem(Object newItem)
{
    // I use the index of this cell, to make sure it is inserted at this
    // index in the TableView instead of at the end.
    this.BackingList.add(this.getIndex(), newItem);
    // Start editing the previous cell, assuming the index of this cell is already updated.
    this.getTableView().edit(this.getIndex(), this.getTableColumn());
}

所以我将项添加到backingList,后者应该在TableView内部触发UpdateEvent。虽然我不希望在退出此方法后更新正在进行。这意味着我无法在此时开始编辑该特定单元格(因为该项目尚不存在)。

所以问题是,有没有办法'强制'更新,所以我可以开始编辑特定的单元格?或者是否有其他解决此问题的方法?

如果有任何建议/想法/解决方案,请告诉我。 提前谢谢!

编辑01:

我尝试了一些方法来实现这一点,一种方法正在发挥作用如下。虽然缺点是,它以某种方式破坏了EventSystem。所以它没有用。(如果你想知道,this.startEdit()也没有用,所以我不幸地没有选择。)

public void onNewItem(Object newItem)
{
    this.BackingList.add(this.getIndex(), newItem);
    this.getTableRow().updateIndex(this.getIndex());//TODO fishy fix!
    this.getTableView().edit(this.getIndex(), this.getTableColumn());
}

编辑02:

根据要求我添加此示例,以便人们可以看到发生了什么。我添加了两种添加元素的方法。一个是通过contextMenu,它完美地工作。第二个是列表底部的按钮。

    package testpacket;

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;



public class EditStateTest extends Application
{
    private static ObservableList<SimpleStringProperty> exampleList = FXCollections.observableArrayList();
    //Placeholder for the button
    private static SimpleStringProperty PlaceHolder = new SimpleStringProperty();

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

    @Override
    public void start(Stage primaryStage) throws Exception
    {
        // basic ui setup
        AnchorPane parent = new AnchorPane();
        Scene scene = new Scene(parent);
        primaryStage.setScene(scene);

        //fill backinglist with data
        for(int i = 0 ; i < 20; i++)
            exampleList.add(new SimpleStringProperty("Hello Test"));
        exampleList.add(PlaceHolder);

        //create a basic tableView
        TableView<SimpleStringProperty> listView = new TableView<SimpleStringProperty>();
        listView.setEditable(true);

        TableColumn<SimpleStringProperty, String> column = new TableColumn<SimpleStringProperty, String>();
        column.setCellFactory(E -> new TableCellTest<SimpleStringProperty, String>());
        column.setCellValueFactory(E -> E.getValue());
        column.setEditable(true);

        // set listViews' backing list
        listView.setItems(exampleList);


        listView.getColumns().clear();
        listView.getColumns().add(column);
        parent.getChildren().add(listView);

        parent.setOnKeyReleased(E -> System.out.println("KeyRelease Captuered: Parent"));


        primaryStage.show();
    }

    // basic editable cell example
    public static class TableCellTest<S, T> extends TableCell<S, T>
    {
        // The editing textField.
        protected static TextField textField = new TextField();;
        protected Button addButton;
        protected ContextMenu menu;


        public TableCellTest()
        {
            this.setOnContextMenuRequested(E -> {
                if(this.getTableView().editingCellProperty().get() == null)
                    this.menu.show(this, E.getScreenX(), E.getScreenY());
            });
            this.menu = new ContextMenu();

            MenuItem createNew = new MenuItem("create New");
            createNew.setOnAction(E -> {
                this.onNewItem(this.getIndex());
            });
            this.menu.getItems().add(createNew);

            addButton = new Button("Add");
            addButton.setOnAction(E -> this.onNewItem(exampleList.size() - 1));
            addButton.prefWidthProperty().bind(this.widthProperty());
        }

        public void onNewItem(int index)
        {
            EditStateTest.exampleList.add(index, new SimpleStringProperty("New Item"));
            this.getTableView().edit(index, this.getTableColumn());
            textField.requestFocus();
        }

        @Override
        public void startEdit()
        {
            if (!isEditable()
                    || (this.getTableView() != null && !this.getTableView().isEditable())
                    || (this.getTableColumn() != null && !this.getTableColumn().isEditable()))
                return;

            super.startEdit();

            if(textField == null)
                this.createTextField();


            textField.setText((String)this.getItem());
            this.setGraphic(textField);
            textField.selectAll();
            this.setText(null);
        }

        @Override
        public void cancelEdit()
        {
            if (!this.isEditing())
                return;

            super.cancelEdit();

            this.setText((String)this.getItem());
            this.setGraphic(null);
        }

        @Override
        protected void updateItem(T item, boolean empty)
        {
            super.updateItem(item, empty);

            // Checks if visuals need an update.
            if(this.getIndex() == EditStateTest.exampleList.size() - 1)
            {
                this.setText("");
                this.setGraphic(addButton);
            }
            else if(empty || item == null)
            {
                this.setText(null);
                this.setGraphic(null);
            }
            else
            {
                // These checks are needed to make sure this cell is the specific cell that is in editing mode.
                // Technically this#isEditing() can be left out, as it is not accurate enough at this point.
                if(this.isEditing() && this.getTableView().getEditingCell() != null 
                        && this.getTableView().getEditingCell().getRow() == this.getIndex())
                {
                    //change to TextField
                    this.setText(null);
                    this.setGraphic(textField);
                }
                else
                {
                    //change to actual value
                    this.setText((String)this.getItem());
                    this.setGraphic(null);
                }
            }
        }

        @SuppressWarnings("unchecked")
        public void createTextField()
        {
            // A keyEvent for the textField. which is called when there is no keyEvent set to this cellObject.
            textField.addEventHandler(KeyEvent.KEY_RELEASED, E -> {
                if(this.getTableView().getEditingCell().getRow() == this.getIndex())
                    if(E.getCode() == KeyCode.ENTER)
                    {
                        this.setItem((T) textField.getText());
                        this.commitEdit(this.getItem());
                    }
                    else if(E.getCode() == KeyCode.ESCAPE)
                        this.cancelEdit();
            });
        }
    }
}

这个想法是添加了一个新项目(效果很好),它将自动开始编辑新添加的项目。使用contextMenu时,它可以很好地工作,但是当使用按钮时,它仅在某些情况下有效。 (大概每3-5个新项目一次)

我希望有一些方法可以解决这个奇怪的问题。

另一方面,当按钮静止(我更喜欢)时,按钮会以某种方式消除每一个新项目。它似乎与我上面提到的相同。

1 个答案:

答案 0 :(得分:0)

我现在一直在深入研究这个问题,并且为什么这个相当混乱的主要问题是因为当使用“正常”方式在某个单元格上开始编辑时,eventSystem会搞乱。

此问题(例如编辑状态更新)在视觉上由onUpdateItem方法内的缺陷引起。基本上,按钮的placeHolder具有空值,导致按钮消失。无法解释的是按钮仍然偶尔出现。 这应该是onUpdateItem()方法的技巧。

if(empty || item == null)
{
    if(this.getIndex() == EditStateTest.exampleList.size() - 1)
    {
        this.setText("");
        this.setGraphic(addButton);
    }
    else
    {
        this.setText(null);
        this.setGraphic(null);
    }
}
else
{
    // These checks are needed to make sure this cell is the specific cell that is in editing mode.
    // Technically this#isEditing() can be left out, as it is not accurate enough at this point.
    if(this.getTableView().getEditingCell() != null 
                        && this.getTableView().getEditingCell().getRow() == this.getIndex())
    {
        //change to TextField
        this.setText(null);
        this.setGraphic(textField);
    }
    else
    {
        //change to actual value
        this.setText((String)this.getItem());
        this.setGraphic(null);
    }
}

无论如何,由于更新了相关方法,因此可以毫不费力地输入编辑状态。但不幸的是,TableView背后的EventSystem仍然存在问题。意味着该示例(即使上面的更改)不起作用。但由于问题超出了这个问题的范围,我认为这个问题已经解决了。如果您对EventSystem的解决方案感兴趣,this Question专门针对该问题。

每个帮助我解决这个问题的人,非常感谢!