英文:
Editiing Currency with a TextField
问题
如何获取一个用于编辑无小数位的货币(例如长整型)的JavaFX TextField?使用数据绑定、TextFormatter 和其他 JavaFX 组件。
目标应该是:
- 使用 LongProperty 的 Bo(以分为单位的货币值)
- 一个可编辑的 TextField,以用户已知的格式(可选的负号、千位分隔符、小数点分隔符、(货币符号)和不允许其他字符)
- Bo 和 TextField 之间的双向绑定。
英文:
How do I get a JavaFX TextField for editing a currency which is stored without factional digits (a long for example)? Using Databinding, TextFormatter and the other javaFX Stuff.
The goal should be:
- Bo with a LongProperty (currency Value in cents)
- a editable TextField, in the Users known format (optinal leading minus,
thousands separator, decimal separator, (currency symbol), and no other
chars possible) - BiDirectionalBinding between Bo and the TextField.
答案1
得分: 0
这是一个解决方案(可能不是最佳的,请在评论中提出改进意见)
Bo(业务对象):
import java.util.Random;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
public class SimpleBo {
// 一个简单的LongProperty,用于存储没有小数位的货币(例如,56.81 €将变成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 java.text.NumberFormat;
import java.util.Locale;
import javafx.util.converter.NumberStringConverter;
public class MyNumberStringConverter extends NumberStringConverter {
public MyNumberStringConverter() {
super();
}
// 其他构造函数略
@Override
public Number fromString(String value) {
// 转换文本字段给出的双精度值,只需乘以100并四舍五入
Number rValue = Math.round(super.fromString(value).doubleValue() * 100);
return rValue.longValue();
}
@Override
public String toString(Number value) {
// 其他代码略
}
}
Util类:
import java.util.function.UnaryOperator;
import javafx.scene.control.TextFormatter;
public class Util {
// 这将过滤更改
public static UnaryOperator<TextFormatter.Change> createFilter() {
// 这是一个简单的正则表达式,用于定义可接受的字符
String validEditingStateRegex = "[0123456789,.-]*";
return change -> {
// 其他代码略
};
}
}
测试应用程序:
import java.text.NumberFormat;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class BindingExample extends Application {
// 其他代码略
}
你可以将TextField放入HBox中,并添加一个Currency Symbol的Label,或者使用Currency Symbols的下拉框等等。也可以使用带有货币的NumberFormat,以便格式化时添加符号。不过这会带来其他一些问题,所以我选择了这种方式。
英文:
Here is a solution (maybe not the best, pls comment if I could improve it)
The Bo:
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);
}
}
A Number to String Converter:
import java.text.NumberFormat;
import java.util.Locale;
import javafx.util.converter.NumberStringConverter;
public class MyNumberStringConverter extends NumberStringConverter {
public MyNumberStringConverter() {
super();
}
public MyNumberStringConverter(Locale locale, String pattern) {
super(locale, pattern);
}
public MyNumberStringConverter(Locale locale) {
super(locale);
}
public MyNumberStringConverter(NumberFormat numberFormat) {
super(numberFormat);
}
public MyNumberStringConverter(String pattern) {
super(pattern);
}
@Override
public Number fromString(String value) {
//to transform the double, given by the textfield, just multiply by 100 and round if any left
Number rValue = Math.round(super.fromString(value).doubleValue() * 100);
return rValue.longValue();
}
@Override
public String toString(Number value) {
if(value == null) {
return "";
}
//Check for too big long value
//If the long is too big, it could result in a strange double value.
if(value.longValue() > 1000000000000l || value.longValue() < -1000000000000l ) {
return "";
}
BigDecimal myBigDecimal = new BigDecimal(value.longValue());
//to convert the long to a double (currency with fractional digits)
myBigDecimal = myBigDecimal.movePointLeft(2);
double asDouble = myBigDecimal.doubleValue();
if(asDouble == Double.NEGATIVE_INFINITY || asDouble == Double.POSITIVE_INFINITY) {
return "";
}
return super.toString(asDouble);
}
Util Class:
import java.util.function.UnaryOperator;
import javafx.scene.control.TextFormatter;
public class Util {
// This will filter the changes
public static UnaryOperator<TextFormatter.Change> createFilter() {
//this is a simple Regex to define the acceptable Chars
String validEditingStateRegex = "[0123456789,.-]*";
return change -> {
String text = change.getText();
//Check if something changed and just return if not
if (!change.isContentChange()) {
return change;
}
//check if the changed text validates against the regex
if (text.matches(validEditingStateRegex) || text.isEmpty()) {
//if valid return the change
return change;
}
//otherwise return null
return null;
};
}
}
Test Application:
import java.text.NumberFormat;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.VBox;
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(createBindingExample());
primaryStage.setScene(scene);
primaryStage.show();
}
//Creates just a sample gui with a Business Objekt
public static Parent createBindingExample() {
VBox vbox = new VBox();
SimpleBo myBo = new SimpleBo();
TextField myTextField = new TextField();
Label fooLabel = new Label();
//Setting up the textField with a Formatter
NumberFormat nFormat = NumberFormat.getInstance();
//Define the integer and fractional digits
nFormat.setMinimumIntegerDigits(1);
nFormat.setMaximumFractionDigits(2);
//setting up the TextFormatter with the NumberFormat and a Filter to limit the inputchars
TextFormatter<Number> textFormatter = new TextFormatter<>(new MyNumberStringConverter(nFormat), 0l,
Util.createFilter());
//Bind (Bidirectional) the BO currency value to the textformatter value
textFormatter.valueProperty().bindBidirectional(myBo.currencyLongProperty());
myTextField.setTextFormatter(textFormatter);
//just to show the currency value, bind it to the label
fooLabel.textProperty().bind(myBo.currencyLongProperty().asString());
vbox.getChildren().add(myTextField);
//just for spacing
vbox.getChildren().add(new Label(" "));
vbox.getChildren().add(fooLabel);
return vbox;
}
}
You could go ahead a put the TextField inside a HBox and a Label for the Currency Symbol. Or with a dropbox of Currency Symbols or what ever. It would be possible to use a NumberFormat with Currency, so the format would add the symbol. But this has some other drawbacks, so I headed this way.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论