我正在尝试向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个新项目一次)
我希望有一些方法可以解决这个奇怪的问题。
另一方面,当按钮静止(我更喜欢)时,按钮会以某种方式消除每一个新项目。它似乎与我上面提到的相同。
答案 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专门针对该问题。
每个帮助我解决这个问题的人,非常感谢!