Отображение валюты в JSpinner - PullRequest
0 голосов
/ 28 января 2020

Я написал небольшую программу для нашей компании, которая занимается продажей напитков, и у каждого пользователя есть свой аккаунт. Для пополнения его счета есть JSpinner, который выглядит так: https://i.imgur.com/qJpPfqR.png. Сотрудник спросил меня, могу ли я добавить валюту к этому счетчику. Я реализовал это, но теперь вы можете вносить депозит только с символом валюты, а не без него, что беспокоило других сотрудников, поэтому давайте перейдем к моему вопросу, как мне принять обе записи с валютой и без?

Basi c 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 взял обе записи.

1 Ответ

0 голосов
/ 28 января 2020

После этого ответа на вопрос об измерении единиц длины, вы можете сделать это аналогичным образом для валют:

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 ammountStr = matcher.group(2),
                             currencyStr = matcher.group(6);
                final double ammount = Double.parseDouble(ammountStr);
                if (currencyStr.trim().isEmpty()) {
                    lastCurrency = defaultCurrency;
                    verbose = false;
                }
                else {
                    lastCurrency = Currency.valueOf(currencyStr);
                    verbose = true;
                }
                return ammount;
            }
            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 ammount = String.format("%.2f", value).replace(',', '.');
            return verbose ? (ammount + ' ' + lastCurrency.name()) : ammount;
        }
    }

    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 s, один для суммы, а другой для валюты.

Редактировать 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 ammountStr = matcher.group(2).trim(),
                             currencyStr = matcher.group(6).trim();
                final double ammount = Double.parseDouble(ammountStr);
                if (currencyStr.isEmpty()) {
                    lastCurrency = defaultCurrency;
                    verbose = false;
                }
                else {
                    if (!supportedCurrencies.contains(currencyStr))
                        throw new ParseException("Unsupported currency.", 0);
                    lastCurrency = currencyStr;
                    verbose = true;
                }
                return ammount;
            }
            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 ammount = String.format("%.2f", value).replace(',', '.');
            return verbose ? (ammount + ' ' + lastCurrency) : ammount;
        }
    }

    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 документацию и соответствующий Java Tutorial , но не могу найти что-либо об указании необязательных символов валюты, поэтому я думаю, что вам придется работать с пользовательским Pattern, как в предыдущих примерах кодов в этом ответе, и я также публикую эти ссылки здесь, на случай, если кто-то еще найдет S решение в них.

...