如何在 JavaFX 可编辑 TreeTableView 中提供货币编辑 (LongProperty)?
How to provide Editing of a currency (LongProperty) within a JavaFX editable TreeTableView?
如何将 BO 货币 (LongProperty) 绑定到 Javafx 可编辑 TreeTableView?使用数据绑定、TextFormatter 和其他 javaFX 材料。
对于普通的 TextField,我找到了这个解决方案:
博:
import java.util.Random;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
public class SimpleBo {
//a simple LongProperty to store the currency without fractional digits (56,81 € would be 5681)
private LongProperty currencyLong = new SimpleLongProperty();
public SimpleBo() {
setCurrencyLong(new Random().nextLong());
}
public final LongProperty currencyLongProperty() {
return this.currencyLong;
}
public final long getCurrencyLong() {
return this.currencyLongProperty().get();
}
public final void setCurrencyLong(final long currencyLong) {
this.currencyLongProperty().set(currencyLong);
}
}
申请:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.TextFieldTreeTableCell;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
import javafx.stage.Stage;
public class BindingExample extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(createTreeTableView());
primaryStage.setScene(scene);
primaryStage.show();
}
private static TreeTableView<SimpleBo> createTreeTableView() {
TreeTableView<SimpleBo> treeTableView = new TreeTableView<>();
// Create column (Data type of Long).
TreeTableColumn<SimpleBo, Number> currencyColumn = new TreeTableColumn<>("Currency");
//Bind Values
currencyColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("currencyLong"));
//Set Cell Factory
currencyColumn.setCellFactory( param -> new TextFieldTreeTableCell<>());
// Add columns to TreeTable.
treeTableView.getColumns().add(currencyColumn);
SimpleBo firstBo = new SimpleBo();
SimpleBo secondBo = new SimpleBo();
SimpleBo thirdBo = new SimpleBo();
// Root Item
TreeItem<SimpleBo> itemRoot = new TreeItem<>(null);
TreeItem<SimpleBo> itemFirst = new TreeItem<>(firstBo);
TreeItem<SimpleBo> itemSecond = new TreeItem<>(secondBo);
TreeItem<SimpleBo> itemThird = new TreeItem<>(thirdBo);
itemRoot.getChildren().addAll(itemFirst, itemSecond, itemThird);
// Set root Item for Tree
treeTableView.setRoot(itemRoot);
treeTableView.setShowRoot(false);
treeTableView.setEditable(true);
return treeTableView;
}
}
目标应该是:
- 具有 LongProperty 的 Bo(以美分计算的货币价值)
- 可编辑的 TreeTable,采用用户已知格式(可选前导减号,
千位分隔符、小数点分隔符、货币符号,没有其他
可能的字符)
- Bo 和 TreeTableColumn 之间的双向绑定。
解决方案将由具有自定义设计的“CurrencyTextFieldTreeTableCell”组成。
使用 MyNumberStringConverter 和 Util Class ()
CurrencyTextFieldTreeTableCell:
import javafx.geometry.Insets;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.util.Callback;
public class CurrencyTextFieldTreeTableCell extends TreeTableCell<SimpleBo, Number> {
public static Callback<TreeTableColumn<SimpleBo, Number>, TreeTableCell<SimpleBo, Number>> forTreeTableColumn() {
return forTreeTableColumn(new MyNumberStringConverter());
}
public static Callback<TreeTableColumn<SimpleBo, Number>, TreeTableCell<SimpleBo, Number>> forTreeTableColumn(
final MyNumberStringConverter converter) {
return list -> new CurrencyTextFieldTreeTableCell(converter);
}
/***************************************************************************
* * Fields * *
**************************************************************************/
private HBox hBox = new HBox();
private Label currencyLabel = new Label("€");
private TextField textField = new TextField("" + 0l);
/***************************************************************************
* * Constructors * *
**************************************************************************/
public CurrencyTextFieldTreeTableCell() {
this(new MyNumberStringConverter());
}
private CurrencyTextFieldTreeTableCell(MyNumberStringConverter converter) {
this.getStyleClass().add("currency-text-field-tree-table-cell");
this.converter = converter;
setupTextField();
setupHBox();
setStyle("-fx-alignment: CENTER-RIGHT;");
}
/***************************************************************************
* * Properties * *
**************************************************************************/
private MyNumberStringConverter converter = new MyNumberStringConverter();
/** {@inheritDoc} */
@Override
public void startEdit() {
if (!isEditable() || !getTreeTableView().isEditable() || !getTableColumn().isEditable()) {
return;
}
super.startEdit();
if (isEditing()) {
this.setText(null);
if (hBox != null) {
this.setGraphic(hBox);
} else {
this.setGraphic(textField);
}
if (textField != null) {
textField.setText(getItemText());
textField.selectAll();
// requesting focus so that key input can immediately go into the
// TextField (see RT-28132)
textField.requestFocus();
}
}
}
/** {@inheritDoc} */
@Override
public void cancelEdit() {
super.cancelEdit();
this.setText(getItemText());
this.setGraphic(currencyLabel);
contentDisplayProperty().setValue(ContentDisplay.RIGHT);
}
/** {@inheritDoc} */
@Override
public void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
if (isEmpty()) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getItemText());
}
setText(null);
setGraphic(hBox);
} else {
setText(getItemText());
setGraphic(currencyLabel);
contentDisplayProperty().setValue(ContentDisplay.RIGHT);
}
}
}
private void setupTextField() {
TextFormatter<Number> textFormatter = new TextFormatter<>(Util.createFilter());
this.textField.setTextFormatter(textFormatter);
// Use onAction here rather than onKeyReleased (with check for Enter),
// as otherwise we encounter RT-34685
this.textField.setOnAction(event -> {
if (converter == null) {
throw new IllegalStateException("Attempting to convert text input into Object, but provided "
+ "StringConverter is null. Be sure to set a StringConverter " + "in your cell factory.");
}
commitEdit(converter.fromString(textField.getText()).longValue());
event.consume();
});
this.textField.setOnKeyReleased(t -> {
if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
t.consume();
}
});
}
private void setupHBox() {
this.hBox.getChildren().add(this.textField);
this.hBox.getChildren().add(new Label(" €"));
this.hBox.setPadding(new Insets(this.hBox.getPadding().getTop() + 9.0D, this.hBox.getPadding().getRight(), this.hBox.getPadding().getBottom(), this.hBox.getPadding().getLeft()));
}
private String getItemText() {
if(converter == null) {
return getItem() == null ? "" : getItem().toString();
} else {
return converter.toString(getItem());
}
}
}
并将 setCellFactory 更改为:
NumberFormat nFormat = NumberFormat.getInstance();
nFormat.setMinimumIntegerDigits(1);
nFormat.setMinimumFractionDigits(2);
nFormat.setMaximumFractionDigits(2);
MyNumberStringConverter numberStringConverter = new MyNumberStringConverter(nFormat);
// Set Cell Factory
currencyColumn.setCellFactory(
param -> CurrencyTextFieldTreeTableCell.forTreeTableColumn(numberStringConverter).call(param));
如何将 BO 货币 (LongProperty) 绑定到 Javafx 可编辑 TreeTableView?使用数据绑定、TextFormatter 和其他 javaFX 材料。
对于普通的 TextField,我找到了这个解决方案:
博:
import java.util.Random;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
public class SimpleBo {
//a simple LongProperty to store the currency without fractional digits (56,81 € would be 5681)
private LongProperty currencyLong = new SimpleLongProperty();
public SimpleBo() {
setCurrencyLong(new Random().nextLong());
}
public final LongProperty currencyLongProperty() {
return this.currencyLong;
}
public final long getCurrencyLong() {
return this.currencyLongProperty().get();
}
public final void setCurrencyLong(final long currencyLong) {
this.currencyLongProperty().set(currencyLong);
}
}
申请:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.TextFieldTreeTableCell;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
import javafx.stage.Stage;
public class BindingExample extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(createTreeTableView());
primaryStage.setScene(scene);
primaryStage.show();
}
private static TreeTableView<SimpleBo> createTreeTableView() {
TreeTableView<SimpleBo> treeTableView = new TreeTableView<>();
// Create column (Data type of Long).
TreeTableColumn<SimpleBo, Number> currencyColumn = new TreeTableColumn<>("Currency");
//Bind Values
currencyColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("currencyLong"));
//Set Cell Factory
currencyColumn.setCellFactory( param -> new TextFieldTreeTableCell<>());
// Add columns to TreeTable.
treeTableView.getColumns().add(currencyColumn);
SimpleBo firstBo = new SimpleBo();
SimpleBo secondBo = new SimpleBo();
SimpleBo thirdBo = new SimpleBo();
// Root Item
TreeItem<SimpleBo> itemRoot = new TreeItem<>(null);
TreeItem<SimpleBo> itemFirst = new TreeItem<>(firstBo);
TreeItem<SimpleBo> itemSecond = new TreeItem<>(secondBo);
TreeItem<SimpleBo> itemThird = new TreeItem<>(thirdBo);
itemRoot.getChildren().addAll(itemFirst, itemSecond, itemThird);
// Set root Item for Tree
treeTableView.setRoot(itemRoot);
treeTableView.setShowRoot(false);
treeTableView.setEditable(true);
return treeTableView;
}
}
目标应该是:
- 具有 LongProperty 的 Bo(以美分计算的货币价值)
- 可编辑的 TreeTable,采用用户已知格式(可选前导减号, 千位分隔符、小数点分隔符、货币符号,没有其他 可能的字符)
- Bo 和 TreeTableColumn 之间的双向绑定。
解决方案将由具有自定义设计的“CurrencyTextFieldTreeTableCell”组成。
使用 MyNumberStringConverter 和 Util Class (
CurrencyTextFieldTreeTableCell:
import javafx.geometry.Insets;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.util.Callback;
public class CurrencyTextFieldTreeTableCell extends TreeTableCell<SimpleBo, Number> {
public static Callback<TreeTableColumn<SimpleBo, Number>, TreeTableCell<SimpleBo, Number>> forTreeTableColumn() {
return forTreeTableColumn(new MyNumberStringConverter());
}
public static Callback<TreeTableColumn<SimpleBo, Number>, TreeTableCell<SimpleBo, Number>> forTreeTableColumn(
final MyNumberStringConverter converter) {
return list -> new CurrencyTextFieldTreeTableCell(converter);
}
/***************************************************************************
* * Fields * *
**************************************************************************/
private HBox hBox = new HBox();
private Label currencyLabel = new Label("€");
private TextField textField = new TextField("" + 0l);
/***************************************************************************
* * Constructors * *
**************************************************************************/
public CurrencyTextFieldTreeTableCell() {
this(new MyNumberStringConverter());
}
private CurrencyTextFieldTreeTableCell(MyNumberStringConverter converter) {
this.getStyleClass().add("currency-text-field-tree-table-cell");
this.converter = converter;
setupTextField();
setupHBox();
setStyle("-fx-alignment: CENTER-RIGHT;");
}
/***************************************************************************
* * Properties * *
**************************************************************************/
private MyNumberStringConverter converter = new MyNumberStringConverter();
/** {@inheritDoc} */
@Override
public void startEdit() {
if (!isEditable() || !getTreeTableView().isEditable() || !getTableColumn().isEditable()) {
return;
}
super.startEdit();
if (isEditing()) {
this.setText(null);
if (hBox != null) {
this.setGraphic(hBox);
} else {
this.setGraphic(textField);
}
if (textField != null) {
textField.setText(getItemText());
textField.selectAll();
// requesting focus so that key input can immediately go into the
// TextField (see RT-28132)
textField.requestFocus();
}
}
}
/** {@inheritDoc} */
@Override
public void cancelEdit() {
super.cancelEdit();
this.setText(getItemText());
this.setGraphic(currencyLabel);
contentDisplayProperty().setValue(ContentDisplay.RIGHT);
}
/** {@inheritDoc} */
@Override
public void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
if (isEmpty()) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getItemText());
}
setText(null);
setGraphic(hBox);
} else {
setText(getItemText());
setGraphic(currencyLabel);
contentDisplayProperty().setValue(ContentDisplay.RIGHT);
}
}
}
private void setupTextField() {
TextFormatter<Number> textFormatter = new TextFormatter<>(Util.createFilter());
this.textField.setTextFormatter(textFormatter);
// Use onAction here rather than onKeyReleased (with check for Enter),
// as otherwise we encounter RT-34685
this.textField.setOnAction(event -> {
if (converter == null) {
throw new IllegalStateException("Attempting to convert text input into Object, but provided "
+ "StringConverter is null. Be sure to set a StringConverter " + "in your cell factory.");
}
commitEdit(converter.fromString(textField.getText()).longValue());
event.consume();
});
this.textField.setOnKeyReleased(t -> {
if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
t.consume();
}
});
}
private void setupHBox() {
this.hBox.getChildren().add(this.textField);
this.hBox.getChildren().add(new Label(" €"));
this.hBox.setPadding(new Insets(this.hBox.getPadding().getTop() + 9.0D, this.hBox.getPadding().getRight(), this.hBox.getPadding().getBottom(), this.hBox.getPadding().getLeft()));
}
private String getItemText() {
if(converter == null) {
return getItem() == null ? "" : getItem().toString();
} else {
return converter.toString(getItem());
}
}
}
并将 setCellFactory 更改为:
NumberFormat nFormat = NumberFormat.getInstance();
nFormat.setMinimumIntegerDigits(1);
nFormat.setMinimumFractionDigits(2);
nFormat.setMaximumFractionDigits(2);
MyNumberStringConverter numberStringConverter = new MyNumberStringConverter(nFormat);
// Set Cell Factory
currencyColumn.setCellFactory(
param -> CurrencyTextFieldTreeTableCell.forTreeTableColumn(numberStringConverter).call(param));