Make a JFormattedTextField behave like ATM input

青春壹個敷衍的年華 提交于 2019-11-27 07:28:34

问题


I would like to know if there is anyway to make a JformattedTextField or jtextField behave like an atm money input. With that I mean you enter from the right to left, say you enter 10 you need to press 2 more 0's so that it will be 10.00 . The program enters the decimal point automatically as he types from right to left? If the 2 0's are not entered it will just be .10 . Is this possible? How would that be returned to me if I want to use that string to do calculations on then? I tried the abstract formatter but this doesn't work so nicely. I want to use this for input for the amount of money received by a customer. But make it idiot proof.


回答1:


This forces the user to always enter text from the right no matter where the caret is positioned. All previous characters are shifted left as a new character is inserted. Formatting will be applied based on your formatter:

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();
            }
        });
    }
}

How would that be returned to me if I want to use that string to do calculations on then?

You would need to create a method, maybe getValue() that would use the format.parse(...) method to return an actual number.




回答2:


Take a look at How to use Formatted Text Fields, in particular Using MaskFormatter.

Something like...

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

for example may help.

Simple example

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();
                }
            }
        });
    }        
}

Additional Example

Now, I realise that the previous example doesn't meet your exact needs (as you described them), it is a simple solution, I've also added a DocumentFilter example...

Which will output...

Value = 0.1
Value = $0.10

Which will output

Value = 10.0
Value = $10.00

Code...

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);
            }
        }
    }
}

Check out DocumentFilter examples for more details




回答3:


Use DocumentFilter for the JTextField and override the appropriate method to handle the number formatting. Also it will be nice if you can post what you have tried and 'doesn't work'.



来源:https://stackoverflow.com/questions/17074758/make-a-jformattedtextfield-behave-like-atm-input

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!