我有一个包含tableview的应用程序。
我从数据库中获取信息并显示数据。
有没有办法让tableview中的底行作为输入新数据的方式?
我在网上看过有texfields和一个添加按钮,但我想知道我是否可以直接从tableview输入新数据。
感谢您提供任何帮助或建议
答案 0 :(得分:0)
您可以通过在自己的自定义类中扩展TableCell来提供此行为。然后,将其CellFactory列设置为此类单元格,您可以在其中提供自定义实现。然后,当有人点击它时,您可以添加自己的如何在单元格中显示文本字段的实现。最后,您需要在tableview中添加一个空行。
我有一些TreeTableCell的示例代码,它很像TableCell。 您可以将此作为示例来编写自己的自定义TableCell。
自定义单元格 包be.vincentnagy.ehka.treetableview.cell;
import be.vincentnagy.ehka.activity.Activity;
import be.vincentnagy.ehka.category.Category;
import be.vincentnagy.ehka.treetableview.item.CustomTreeItem;
import be.vincentnagy.ehka.treetableview.item.TotalTreeItem;
import javafx.geometry.Pos;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.input.KeyCode;
import java.text.NumberFormat;
import java.text.ParseException;
import java.time.Month;
import java.time.Year;
/**
* @author Vincent Nagy
* A MoneyFormatCell is a custom implementation of a TreeTableCell.
* It provides editing functionality for specific cells
* and propagates changes to the underlying data items.
*/
public class MoneyFormatCell extends TreeTableCell<CustomTreeItem,Number>{
private TextField textField;
private static final NumberFormat NUMBER_FORMAT = NumberFormat.getNumberInstance();
private static final NumberFormat CURRENCY_FORMAT = NumberFormat.getCurrencyInstance();
static {
NUMBER_FORMAT.setMaximumFractionDigits(2);
NUMBER_FORMAT.setMinimumFractionDigits(2);
}
public MoneyFormatCell() {
}
/**
* updateItem gets called whenever a MoneyFormatCell changes state.
* Editing gets disabled for TotalTreeItem and CategoryTreeItem.
* Provides the TextBox inside the cell for input.
* @param item Item being added to the MoneyFormatCell. Only gets called at initial filling of the Table
* @param empty Whether or not the cell is empty
*/
@Override
protected void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
setAlignment(Pos.CENTER_RIGHT);
TreeItem<CustomTreeItem> treeItem = getTreeTableRow().getTreeItem();
//Set editable if it's a leaf(aka if it's an ActivityDataItem
setEditable(treeItem != null && treeItem.isLeaf());
//Set editable if it's not the the Totals row and not a Category row
setEditable(!(treeItem != null && (treeItem.getValue() instanceof TotalTreeItem || treeItem.getValue() instanceof Category)));
if(empty){
setText(null);
setGraphic(null);
}else {
if( isEditing()){
if(textField != null){
textField.setText(getString());
setText(null);
setGraphic(textField);
}
} else {
if(item == null){
if(textField != null && !textField.getText().equals("")){
stoppedEditing();
}
setText("");
}else{
setText(NumberFormat.getCurrencyInstance().format(item.doubleValue()));
}
setGraphic(null);
}
}
}
/**
* startEdit gets called whenever a MoneyFormatCell moves into editing mode.
* It creates the TextField element if it isn't already created
* and switches the graphics from string to the TextField.
*/
@Override
public void startEdit() {
if(isEditable()) {
super.startEdit();
if (textField == null) {
createTextField();
}
setText(null);
setGraphic(textField);
textField.requestFocus();
textField.selectAll();
}
}
/**
* createTextField creates a TextField to allow editing of data in the Cell.
* It initializes the event handlers for the Escape key and the Enter key.
* It also listens for when it gets unfocused.
*/
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.setOnKeyReleased(event -> {
if (event.getCode() == KeyCode.ESCAPE){
cancelEdit();
} else if(event.getCode() == KeyCode.ENTER){
stoppedEditing();
}
});
textField.focusedProperty().addListener((observable, oldValue, newValue) -> {
if(oldValue){
stoppedEditing();
}
});
}
/**
* stoppedEditing gets called whenever a cell leaves editing mode.
* This happens whenever the Enter key gets pressed or when the focus shifts away from the cell.
* If there is no value, it will simple cancel out.
* Otherwise it will propagate the changes to the respective ActivityTreeItem
* and commit the edit to the cell.
*/
private void stoppedEditing(){
try {
if (textField.getText().equals("")) {
cancelEdit();
} else {
if (getTreeTableRow().getTreeItem().getValue() instanceof Activity) {
Activity activityTreeItem = (Activity) getTreeTableRow().getTreeItem().getValue();
activityTreeItem.setActivityDataForDate(Month.valueOf(getTableColumn().getText()).getValue(),
Year.parse(getTableColumn().getParentColumn().getText()).getValue(),
NUMBER_FORMAT.parse(textField.getText()).doubleValue());
}
commitEdit(NUMBER_FORMAT.parse(textField.getText()).doubleValue());
}
} catch (ParseException e) {
e.printStackTrace();
}
}
/**
* cancelEdit gets called when the cell stops editing and
* either was left by using the Escape key or there was no new value in the cell
*/
@Override
public void cancelEdit() {
super.cancelEdit();
setText(getFormatted());
setGraphic(null);
}
/*
format into double number for editing in text field
*/
private String getString() {
//return getItem() == null ? "" : NumberFormat.getNumberInstance().format(((double)getItem()));
return getItem() == null ? "" : NUMBER_FORMAT.format(((double)getItem()));
}
/*
format into double number with currency locale
*/
private String getFormatted(){
return getItem() == null ? "" :CURRENCY_FORMAT.format(getItem().doubleValue());
}
}
创建自定义列
/**
* Create a column for the requested months in the specified year. Doesn't need to start at January.
* Bind the cell to the ActivityTreeItem valueProperty
*
* @param yearColumn The column to initialize
* @param year The year it belongs to
* @param startMonth The starting month in case it's not a full year
* @param endMonth The ending month in case it's not a full year
*/
private void createMonthColumns(TreeTableColumn<CustomTreeItem, Number> yearColumn, int year, int startMonth, int endMonth) {
for (int i = startMonth; i <= endMonth; i++) {
Month month = Month.of(i);
TreeTableColumn<CustomTreeItem, Number> ttc = new TreeTableColumn<>(month.toString());
ttc.setMinWidth(MIN_TABLE_WIDTH);
Month currentMonth = Month.of(i);
ttc.setCellValueFactory(param -> {
if (param.getValue().getValue() instanceof Activity) {
Activity treeItem = (Activity) param.getValue().getValue();
return treeItem.getActivityDataForDate(currentMonth.getValue(), year);
} else if (param.getValue().getValue() instanceof TotalTreeItem) {
TotalTreeItem totalTreeItem = (TotalTreeItem) param.getValue().getValue();
return totalTreeItem.valueProperty(currentMonth.getValue(), year);
}
return null;
});
ttc.setCellFactory(param -> new MoneyFormatCell());
yearColumn.getColumns().add(ttc);
}
}