我制作了一个JCurrencyField以便在程序中使用它,我几乎完成了创建它,但是遇到了一个问题。

JCurrencyField是一个普通的JTextField,但它不能接受不是数字或小数点.的任何字符,并且我为此JCurrencyField做了一些规定:


用户不能插入多个小数点.
用户不能在小数点后插入两位以上的数字。


我遇到的问题是,如果小数点后有两位数字,则无法在小数点前插入任何数字。

这是我的代码:

import javax.swing.*;
import javax.swing.text.*;
public class JCurrencyField extends JTextField
{
    public JCurrencyField()
    {
        ((AbstractDocument)getDocument()).setDocumentFilter(new NumberOnlyFilter());
    }

    private class NumberOnlyFilter extends DocumentFilter
    {
        public void insertString(DocumentFilter.FilterBypass fb, int offset,
        String text, AttributeSet attr) throws BadLocationException
        {
            if(!containsOnlyNumbers(text)) return;
            fb.insertString(offset, text, attr);
        }

        public void replace(DocumentFilter.FilterBypass fb, int offset, int length,
        String text, AttributeSet attr) throws BadLocationException
        {
            if(!containsOnlyNumbers(text)) return;
            fb.replace(offset, length, text, attr);
        }

        /**
        * This method checks if a String contains only numbers
        */
        public boolean containsOnlyNumbers(String str)
        {

            //It can't contain only numbers if it's null or empty...
            if (str == null || str.length() == 0) return false;


            int counter = 0;
            for(int i = 0; i < getText().length(); i++)
            {
                if(counter > 1) return false;
                if(getText().charAt(i) == '.')
                {
                    counter++;
                }
            }

            int fp_counter = 0;
            int fp_index = -1;
            for(int i = 0; i < str.length(); i++)
            {
                if(counter >= 1) break;
                if(str.charAt(i) == '.')
                {
                    fp_counter++;
                    fp_index = i;
                }
            }

            if(fp_counter > 1) return false;

            if(str.length() > fp_index + 3) return false;

            if(counter >= 1)
            {
                for(int i = 0; i < getText().length(); i++)
                {
                    if(getText().charAt(i) == '.') counter++;
                }
            }

            for (int j = 0; j < str.length(); j++)
            {
                if(counter >= 1 && (str.charAt(j) == '.')) return false;
            }

            int index = 0;
            boolean fp_Flag = false;
            int sp_count = 0;
            for(int k = 0; k < getText().length(); k++)
            {
                if(getText().charAt(k) == '.')
                {
                    index = k;
                    fp_Flag = true;
                }
                if(fp_Flag) sp_count++;
                if(sp_count > 2) return false;
            }

            //if(fp_Flag && str.length() > 2) return false;
            if(fp_Flag && index + 1 < getText().length() && str.length() > 1) return false;
            //if(index + 2 < getText().length() && fp_Flag) return false;


            for (int l = 0; l < str.length(); l++)
            {

                //If we find a non-digit character we return false.
                if(str.charAt(l) == '.') continue;
                if(!Character.isDigit(str.charAt(l)))
                return false;
            }

            return true;
        }
    }
}


测试程序:

import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
public class Test extends JFrame

{
    private JCurrencyField txt = new JCurrencyField();
    public Test()
    {
        super("Test...");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        try{ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());}
        catch(Exception e){ System.out.println("Unable to load Windows look and feel");}
        setPreferredSize(new Dimension(300, 100));
        ((JPanel) getContentPane()).setBorder(new EmptyBorder(13, 13, 13, 13) );
        setLayout(new FlowLayout());
        txt.setPreferredSize(new Dimension(100,30));
        add(txt);
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
        setResizable(false);
    }
    public static void main(String[] args)
    {
        new Test();
    }
}


您能帮我解决这个问题吗?

最佳答案

您正在测试DocumentFilter中的错误字符串。

您不应该测试文本String,而应该测试根据从FilterBypass参数获得的文本和Document构建的String,因为这是您要查看其是否满足条件的String。例如,不是这样的:

    public void insertString(DocumentFilter.FilterBypass fb, int offset,
    String text, AttributeSet attr) throws BadLocationException
    {
        if(!containsOnlyNumbers(text)) return;
        fb.insertString(offset, text, attr);
    }

    public void replace(DocumentFilter.FilterBypass fb, int offset, int length,
    String text, AttributeSet attr) throws BadLocationException
    {
        if(!containsOnlyNumbers(text)) return;
        fb.replace(offset, length, text, attr);
    }


而是这样:

   private class NumberOnlyFilter extends DocumentFilter {
      public void insertString(DocumentFilter.FilterBypass fb, int offset,
               String text, AttributeSet attr) throws BadLocationException {
         StringBuilder sb = new StringBuilder();
         sb.append(fb.getDocument().getText(0, fb.getDocument().getLength()));
         sb.insert(offset, text);
         if (!containsOnlyNumbers(sb.toString()))
            return;
         fb.insertString(offset, text, attr);
      }

      public void replace(DocumentFilter.FilterBypass fb, int offset,
               int length, String text, AttributeSet attr)
               throws BadLocationException {
         StringBuilder sb = new StringBuilder();
         sb.append(fb.getDocument().getText(0, fb.getDocument().getLength()));
         sb.replace(offset, offset + length, text);
         if (!containsOnlyNumbers(sb.toString()))
            return;
         fb.replace(offset, length, text, attr);
      }


您将需要对containsOnlyNumbers方法进行更改以解决这些更改。

编辑1
这可以使用toto的正则表达式轻松完成:

  private boolean containsOnlyNumbers(String text) {
     Pattern pattern = Pattern.compile("\\d*(\\.\\d{0,2})?");
     Matcher matcher = pattern.matcher(text);
     boolean isMatch = matcher.matches();
     return isMatch;
  }

08-06 19:32