使用文本字段编辑货币金额。

huangapple go评论65阅读模式
英文:

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 &quot;&quot;;
        }
        //Check for too big long value
        //If the long is too big, it could result in a strange double value.
        if(value.longValue() &gt; 1000000000000l || value.longValue() &lt; -1000000000000l ) {
    	    return &quot;&quot;;
        }
        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 &quot;&quot;;
        }
        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&lt;TextFormatter.Change&gt; createFilter() {
        //this is a simple Regex to define the acceptable Chars
        String validEditingStateRegex = &quot;[0123456789,.-]*&quot;;
        return change -&gt; {
            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&lt;Number&gt; textFormatter = new TextFormatter&lt;&gt;(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(&quot; &quot;));
        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.

huangapple
  • 本文由 发表于 2020年8月7日 21:45:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/63303122.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定