Заставьте JFormattedTextField вести себя как ввод банкомата

Я хотел бы знать, есть ли способ заставить JformattedTextField или jtextField вести себя как ввод денег в банкомате. При этом я имею в виду, что вы вводите справа налево, скажем, вы вводите 10, вам нужно нажать еще 2 0, чтобы это было 10.00. Программа вводит десятичную точку автоматически, когда он печатает справа налево? Если 2 0 не введены, это будет просто .10 . Это возможно? Как это будет возвращено мне, если я захочу использовать эту строку для выполнения вычислений? Я пробовал абстрактный форматтер, но это не так хорошо работает. Я хочу использовать это для ввода суммы денег, полученной клиентом. Но сделайте это идиотским доказательством.


person LL.    schedule 12.06.2013    source источник
comment
Я думаю, вы можете прикрепить прослушиватель к текстовому полю и выполнять любые функции, которые вам нужны.   -  person kosa    schedule 13.06.2013


Ответы (3)


Это заставляет пользователя всегда вводить текст справа, независимо от того, где находится курсор. Все предыдущие символы сдвигаются влево по мере вставки нового символа. Форматирование будет применено на основе вашего средства форматирования:

import java.awt.*;
import java.text.*;
import javax.swing.*;
import javax.swing.text.*;

public class ABMTextField extends JTextField
{
    private DecimalFormat format;
    private String decimal;

    public ABMTextField(DecimalFormat format)
    {
        this.format = format;

        decimal = Character.toString( format.getDecimalFormatSymbols().getDecimalSeparator() );

        setColumns( format.toPattern().length() );
        setHorizontalAlignment(JFormattedTextField.TRAILING);

        setText( format.format(0.0) );

        AbstractDocument doc = (AbstractDocument)getDocument();
        doc.setDocumentFilter( new ABMFilter() );
    }

    @Override
    public void setText(String text)
    {
        Number number = format.parse(text, new ParsePosition(0));

        if (number != null)
            super.setText( text );
    }

    public class ABMFilter extends DocumentFilter
    {
        public void insertString(FilterBypass fb, int offs, String str, AttributeSet a)
            throws BadLocationException
        {
            replace(fb, offs, 0, str, a);
        }

        public void replace(FilterBypass fb, int offs, int length, String str, AttributeSet a)
            throws BadLocationException
        {
            if ("0123456789".contains(str))
            {
                Document doc = fb.getDocument();
                StringBuilder sb = new StringBuilder( doc.getText(0, doc.getLength()) );

                int decimalOffset = sb.indexOf( decimal );

                if (decimalOffset != -1)
                {
                    sb.deleteCharAt(decimalOffset);
                    sb.insert(decimalOffset + 1, decimal);
                }

                sb.append(str);

                try
                {
                    String text = format.format( format.parse( sb.toString() ) );
                    super.replace(fb, 0, doc.getLength(), text, a);
                }
                catch(ParseException e) {}
            }
            else
                Toolkit.getDefaultToolkit().beep();
        }

        public void remove(DocumentFilter.FilterBypass fb, int offset, int length)
            throws BadLocationException
        {
            Document doc = fb.getDocument();
            StringBuilder sb = new StringBuilder( doc.getText(0, doc.getLength()) );

            int decimalOffset = sb.indexOf( decimal );

            if (decimalOffset != -1)
            {
                sb.deleteCharAt(decimalOffset);
                sb.insert(decimalOffset - 1, decimal);
            }

            sb.deleteCharAt( sb.length() - 1) ;

            try
            {
                String text = format.format( format.parse( sb.toString() ) );
                super.replace(fb, 0, doc.getLength(), text, null);
            }
            catch(ParseException e) {}
        }
    }

    private static void createAndShowUI()
    {
        DecimalFormat format = new DecimalFormat("###,##0.00");
        ABMTextField abm = new ABMTextField( format );

        JPanel panel = new JPanel();
        panel.add( abm );

        JFrame frame = new JFrame("ABMTextField");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( panel );
        frame.setSize(200, 200);
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowUI();
            }
        });
    }
}

Как это будет возвращено мне, если я захочу использовать эту строку для выполнения вычислений?

Вам нужно будет создать метод, возможно, getValue(), который будет использовать метод format.parse(...) для возврата фактического числа.

person camickr    schedule 13.06.2013
comment
Большое спасибо, это именно то, что я искал. - person LL.; 15.06.2013

Взгляните на Как использовать форматированные текстовые поля в частности Использование MaskFormatter.

Что-то типа...

MaskFormatter formatter = new MaskFormatter("##.##");
JFormattedTextField field = JFormattedTextField(formatter);

например может помочь.

Простой пример

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.HeadlessException;
import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.text.DefaultFormatterFactory;
import javax.swing.text.MaskFormatter;

public class TestFormattedTextField {

    public static void main(String[] args) {
        new TestFormattedTextField();
    }

    public TestFormattedTextField() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                try {
                    JFormattedTextField field = new JFormattedTextField();
                    MaskFormatter formatter = new MaskFormatter("##.##");
                    formatter.setPlaceholderCharacter('0');
                    field.setFormatterFactory(new DefaultFormatterFactory(formatter));

                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new GridBagLayout());
                    frame.add(field);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (ParseException exp) {
                    exp.printStackTrace();
                }
            }
        });
    }        
}

Дополнительный пример

Теперь я понимаю, что предыдущий пример не соответствует вашим точным потребностям (как вы их описали), это простое решение, я также добавил пример DocumentFilter...

введите здесь описание изображения

Что выведет...

Value = 0.1
Value = $0.10

введите здесь описание изображения

Что будет выводить

Value = 10.0
Value = $10.00

Код...

import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.MaskFormatter;

public class TestFormattedTextField {

    public static void main(String[] args) {
        new TestFormattedTextField();
    }

    public TestFormattedTextField() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                MoneyField field = new MoneyField();
                field.addActionListener(new ActionListener() {
                    @Override
                    @SuppressWarnings("empty-statement")
                    public void actionPerformed(ActionEvent e) {
                        MoneyField field = (MoneyField) e.getSource();
                        double value = field.getValue();
                        System.out.println("Value = " + value);
                        System.out.println("Value = " + NumberFormat.getCurrencyInstance().format(value));
                    }
                });

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new GridBagLayout());
                frame.add(field);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class MoneyField extends JTextField {

        public MoneyField() {
            setColumns(5);
            setHorizontalAlignment(RIGHT);
            ((AbstractDocument) getDocument()).setDocumentFilter(new Filter());
        }

        public double getValue() {

            String text = getText();
            if (!text.contains(".")) {
                text = "0." + text;
            }

            return Double.parseDouble(text);

        }

        protected class Filter extends DocumentFilter {

            protected String getNumbers(String text) {
                StringBuilder sb = new StringBuilder(text.length());
                for (char c : text.toCharArray()) {
                    if (Character.isDigit(c)) {
                        sb.append(c);
                    }
                }
                return sb.toString();
            }

            @Override
            public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
                if (length > 0) {
                    fb.remove(offset, length);
                }
                insertString(fb, offset, text, attrs);
            }

            @Override
            public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
                text = getNumbers(text);
                if (text.length() > 0) {
                    int docLength = fb.getDocument().getLength();
                    if (docLength == 2) {
                        text = "." + text;
                    }
                    if (docLength + text.length() < 6) {
                        super.insertString(fb, offset, text, attr);
                    }
                }
            }

            @Override
            public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
                if (offset == 3) {
                    offset = 2;
                    length = 2;
                }
                super.remove(fb, offset, length);
            }
        }
    }
}

Дополнительные сведения см. в примерах DocumentFilter.

person MadProgrammer    schedule 12.06.2013
comment
+1, я вижу, мы оба работали над подходом DocumentFilter, и я думаю, что это правильный путь. - person camickr; 13.06.2013
comment
@camickr Я все еще думаю, что JFormattedTextField - более простой подход, но DocumentFilter будет более точно соответствовать требованиям OP ... - person MadProgrammer; 13.06.2013

Используйте DocumentFilter для JTextField и переопределите соответствующий метод для обработки форматирования чисел. Также было бы неплохо, если бы вы могли опубликовать то, что вы пробовали и «не работает».

person Mubin    schedule 12.06.2013
comment
DocumentListener не подходит для фильтрации входных данных в документе. К моменту вызова DocumentListener в документ уже были внесены изменения, и любые изменения в прослушивателе вызовут исключение. - person MadProgrammer; 13.06.2013
comment
@MadProgrammer - Вы правы. Я имел в виду DocumentFilter и набрал DocumentListener :-) - person Mubin; 13.06.2013
comment
Разве ты не любишь, когда твои пальцы делают одно, а мозг другое :P - person MadProgrammer; 13.06.2013
comment
Я попытался создать регулярное выражение для него. Я попытался создать маску ввода, маска ввода работает до тех пор, пока число не станет больше 99,99 . С регулярным выражением, использующим сопоставитель, я обнаружил, что он глючил, когда ему приходилось проверять нажатую клавишу. - person LL.; 13.06.2013