java - 在 JSpinner 中显示货币

标签 java swing spinner currency jspinner

我为我们公司写了一个小程序,负责饮料的销售,每个用户都有自己的帐户。为了给他的帐户充值,有一个 JSpinner,如下所示:

spinner

一名员工问我是否可以将货币添加到此旋转器中。所以我实现了它,但现在你只能用货币符号存款,而不能用货币符号存款,这打扰了其他工作人员,所以让我们来回答我的问题,我如何才能同时接受带货币和不带货币的条目?

基本微调器(如我上面发布的图片所示):

final SpinnerNumberModel spinnerModel = new SpinnerNumberModel( 1, 1, 1000, 1 );
final JSpinner valueSpinner = new JSpinner( spinnerModel );

为了添加货币,我使用了这个代码片段,效果很好

    String pattern = "0€";
    JSpinner.NumberEditor editor = new JSpinner.NumberEditor( valueSpinner, pattern );
    valueSpinner.setEditor( editor );

我已经尝试编写一个自定义 JSpinner,但我无法实现 Spinner 接受两个条目。

最佳答案

已关注 this关于测量长度单位的问题的答案,您可以对货币以类似的方式进行操作:

import java.text.ParseException;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFormattedTextField.AbstractFormatter;
import javax.swing.JFormattedTextField.AbstractFormatterFactory;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JSpinner.DefaultEditor;
import javax.swing.SpinnerNumberModel;

public class MainWithFormatter {

    //For more currencies and their ISO codes, visit https://en.wikipedia.org/wiki/List_of_circulating_currencies
    public static enum Currency {
        EUR, //Euro
        USD, //United States Dollar
        GBP, //British Pound
        JPY //Japanese Yen
    }

    public static class CurrencyFormatter extends AbstractFormatter {

        private static final Pattern PATTERN;

        static {
            //Building the Pattern is not too tricky. It just needs some attention.

            final String blank = "\\p{Blank}"; //Match any whitespace character.
            final String blankGroupAny = "(" + blank + "*)"; //Match any whitespace character, zero or more times and group them.

            final String digits = "\\d"; //Match any digit.
            final String digitsGroup = "(" + digits + "+)"; //Match any digit, at least once and group them.
            final String digitsSuperGroup = "(\\-?" + digitsGroup + "\\.?" + digitsGroup + "?)"; //Matches for example "-2.4" or "2.4" or "2" or "-2" in the same group!

            //Create the pattern part which matches any of the available currencies...
            final Currency[] currencies = Currency.values();
            final StringBuilder currenciesBuilder = new StringBuilder(Pattern.quote("")); //Empty currency value is valid.
            for (int i = 0; i < currencies.length; ++i)
                currenciesBuilder.append('|').append(Pattern.quote(currencies[i].name()));
            final String currenciessGroup = "(" + currenciesBuilder + ")";

            final String full = "^" + blankGroupAny + digitsSuperGroup + blankGroupAny + currenciessGroup + blankGroupAny + "$"; //Compose full pattern.

            PATTERN = Pattern.compile(full);
        }

        private final Currency defaultCurrency;
        private Currency lastCurrency;
        private boolean verbose; //Show the default currency while spinning or not?

        public CurrencyFormatter(final Currency defaultCurrency) {
            this.defaultCurrency = Objects.requireNonNull(defaultCurrency);
            lastCurrency = defaultCurrency;
            verbose = true;
        }

        @Override
        public Object stringToValue(final String text) throws ParseException {
            if (text == null || text.trim().isEmpty())
                throw new ParseException("Null or empty text.", 0);
            try {
                final Matcher matcher = PATTERN.matcher(text.toUpperCase());
                if (!matcher.matches())
                    throw new ParseException("Invalid input.", 0);
                final String amountStr = matcher.group(2),
                             currencyStr = matcher.group(6);
                final double amount = Double.parseDouble(amountStr);
                if (currencyStr.trim().isEmpty()) {
                    lastCurrency = defaultCurrency;
                    verbose = false;
                }
                else {
                    lastCurrency = Currency.valueOf(currencyStr);
                    verbose = true;
                }
                return amount;
            }
            catch (final IllegalArgumentException iax) {
                throw new ParseException("Failed to parse input \"" + text + "\".", 0);
            }
        }

        public Currency getLastCurrency() {
            return lastCurrency;
        }

        @Override
        public String valueToString(final Object value) throws ParseException {
            final String amount = String.format("%.2f", value).replace(',', '.');
            return verbose ? (amount + ' ' + lastCurrency.name()) : amount;
        }
    }

    public static class CurrencyFormatterFactory extends AbstractFormatterFactory {
        @Override
        public AbstractFormatter getFormatter(final JFormattedTextField tf) {
            if (!(tf.getFormatter() instanceof CurrencyFormatter))
                return new CurrencyFormatter(Currency.USD);
            return tf.getFormatter();
        }
    }

    public static void main(final String[] args) {
        final JSpinner spin = new JSpinner(new SpinnerNumberModel(0d, -1000000d, 1000000d, 0.01d));

        final JFormattedTextField jftf = ((DefaultEditor) spin.getEditor()).getTextField();
        jftf.setFormatterFactory(new CurrencyFormatterFactory());

        //Added a button to demonstrate how to obtain the value the user has selected:
        final JButton check = new JButton("Check!");
        check.addActionListener(e -> {
            final CurrencyFormatter cf = (CurrencyFormatter) jftf.getFormatter();
            JOptionPane.showMessageDialog(check, Objects.toString(spin.getValue()) + ' ' + cf.getLastCurrency().name() + '!');
        });

        final JPanel contents = new JPanel(); //FlowLayout.
        contents.add(spin);
        contents.add(check);

        final JFrame frame = new JFrame("JSpinner currencies.");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(contents);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

您只需为微调器默认编辑器的格式化文本字段创建一个自定义 AbstractFormatter 即可处理此类字符串。

尽管您可以简单地通过放置两个 JSpinner 来完成此操作,一个用于金额,另一个用于货币。

编辑 1:或者,您可以使用内置的 java.util.Currency 类:

import java.text.ParseException;
import java.util.Currency;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFormattedTextField.AbstractFormatter;
import javax.swing.JFormattedTextField.AbstractFormatterFactory;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JSpinner.DefaultEditor;
import javax.swing.SpinnerNumberModel;

public class MainWithCurrency {

    public static class CurrencyFormatter extends AbstractFormatter {

        private static final Pattern PATTERN;

        static {
            //Building the Pattern is not too tricky. It just needs some attention.

            final String blank = "\\p{Blank}"; //Match any whitespace character.
            final String blankGroupAny = "(" + blank + "*)"; //Match any whitespace character, zero or more times and group them.

            final String digits = "\\d"; //Match any digit.
            final String digitsGroup = "(" + digits + "+)"; //Match any digit, at least once and group them.
            final String digitsSuperGroup = "(\\-?" + digitsGroup + "\\.?" + digitsGroup + "?)"; //Matches for example "-2.4" or "2.4" or "2" or "-2" in the same group!

            //Create the pattern part which matches any of the available currencies...
            final String currencyCodes = "[A-Z]{3}|" + Pattern.quote(""); //Currency code consists of 3 letters, or is empty for default value.
            final String currenciessGroup = "(" + currencyCodes + ")";

            final String full = "^" + blankGroupAny + digitsSuperGroup + blankGroupAny + currenciessGroup + blankGroupAny + "$"; //Compose full pattern.

            PATTERN = Pattern.compile(full);
        }

        private final Set<String> supportedCurrencies;
        private final String defaultCurrency;
        private String lastCurrency;
        private boolean verbose; //Show the default currency while spinning or not?

        public CurrencyFormatter(final Set<Currency> supportedCurrencies,
                                 final Currency defaultCurrency) {
            if (!supportedCurrencies.contains(defaultCurrency))
                throw new IllegalArgumentException("Default currency is not supported.");
            this.supportedCurrencies = supportedCurrencies.stream().map(currency -> currency.getCurrencyCode()).collect(Collectors.toSet());
            this.defaultCurrency = defaultCurrency.getCurrencyCode();
            lastCurrency = this.defaultCurrency;
            verbose = true;
        }

        @Override
        public Object stringToValue(final String text) throws ParseException {
            if (text == null || text.trim().isEmpty())
                throw new ParseException("Null or empty text.", 0);
            try {
                final Matcher matcher = PATTERN.matcher(text.toUpperCase());
                if (!matcher.matches())
                    throw new ParseException("Invalid input.", 0);
                final String amountStr = matcher.group(2).trim(),
                             currencyStr = matcher.group(6).trim();
                final double amount = Double.parseDouble(amountStr);
                if (currencyStr.isEmpty()) {
                    lastCurrency = defaultCurrency;
                    verbose = false;
                }
                else {
                    if (!supportedCurrencies.contains(currencyStr))
                        throw new ParseException("Unsupported currency.", 0);
                    lastCurrency = currencyStr;
                    verbose = true;
                }
                return amount;
            }
            catch (final IllegalArgumentException iax) {
                throw new ParseException("Failed to parse input \"" + text + "\".", 0);
            }
        }

        public Currency getLastCurrency() {
            return Currency.getInstance(lastCurrency);
        }

        @Override
        public String valueToString(final Object value) throws ParseException {
            final String amount = String.format("%.2f", value).replace(',', '.');
            return verbose ? (amount + ' ' + lastCurrency) : amount;
        }
    }

    public static class CurrencyFormatterFactory extends AbstractFormatterFactory {
        @Override
        public AbstractFormatter getFormatter(final JFormattedTextField tf) {
            if (!(tf.getFormatter() instanceof CurrencyFormatter))
                return new CurrencyFormatter(Currency.getAvailableCurrencies(), Currency.getInstance("USD"));
            return tf.getFormatter();
        }
    }

    public static void main(final String[] args) {
        final JSpinner spin = new JSpinner(new SpinnerNumberModel(0d, -1000000d, 1000000d, 0.01d));

        final JFormattedTextField jftf = ((DefaultEditor) spin.getEditor()).getTextField();
        jftf.setFormatterFactory(new CurrencyFormatterFactory());

        //Added a button to demonstrate how to obtain the value the user has selected:
        final JButton check = new JButton("Check!");
        check.addActionListener(e -> {
            final CurrencyFormatter cf = (CurrencyFormatter) jftf.getFormatter();
            JOptionPane.showMessageDialog(check, Objects.toString(spin.getValue()) + ' ' + cf.getLastCurrency().getCurrencyCode() + '!');
        });

        final JPanel contents = new JPanel(); //FlowLayout.
        contents.add(spin);
        contents.add(check);

        final JFrame frame = new JFrame("JSpinner currencies.");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(contents);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

在这段代码(第一次编辑)中,我正在解析货币 ISO 代码,并根据支持货币的 Set 进行检查。

注意:我已阅读 DecimalFormat documentationcorresponding Java Tutorial ,但找不到有关指定可选货币符号的任何内容,因此我认为您必须使用自定义 Pattern (如本答案中的前面的示例代码),我还在此处发布了这些链接,位于如果其他人在其中找到了解决方案。

关于java - 在 JSpinner 中显示货币,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59947168/

相关文章:

java - 从自定义 ListView 元素中的 Spinner getSelectedItem

java - foreach 和 for 循环将重复记录复制到数组中

Java - JPanel - 使用数组列表绘制文本文件的内容 - 仅第一次有效

java - 如何更改显示的布局

java - 捕获分号分隔字符串的组缺少最后一个组

java - JLabel 工具提示在 BrowserView 顶部不可见

ant - 如何从 ANT 执行 JAXB 编译器

java - 不使用带参数的 java -jar 打开 Jar

java - 使用 JFrame 和 JPanel 构建 GUI 时如何使用布局管理器?

android 微调器-onItemSelected