Java 自定义JTextField(JCurrencyField)

Java 自定义JTextField(JCurrencyField),java,swing,format,jtextfield,custom-component,Java,Swing,Format,Jtextfield,Custom Component,我制作了一个JCurrencyField来在我的程序中使用它,我几乎完成了它的创建,但我遇到了一个问题 JCurrencyField是一个普通的JTextField,但它不能接受任何不是数字或小数点的字符。我为这个JCurrencyField制定了一些规范: 用户不能插入多个小数点。。 用户不能在小数点后插入两位以上的数字。 我面临的问题是,如果小数点后有两位数字,就不能在小数点前插入任何数字 这是我的密码: import javax.swing.*; import javax.swing.te

我制作了一个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;
        }
    }
}
你能帮我解决这个问题吗?

以下应该可以:

Pattern pattern = Pattern.compile("\\d*(\\.\\d{0,2})?");
Matcher matcher = pattern.matcher(text);
boolean isMatch = matcher.matches();
if (isMatch) 
  value = Double.parseDouble(text);
但是,它也会匹配空字符串,因此如果不匹配,则必须单独检查。我不知道parseDouble是否为空字符串返回0

编辑:@mkorbel关于将java.swing.text.NumberFormatter与java.text.NumberFormat一起使用的评论比我的更好。您可能会将代码缩减约100倍。。。当您了解自己的库时,就会发生这种情况。

以下操作应该有效:

Pattern pattern = Pattern.compile("\\d*(\\.\\d{0,2})?");
Matcher matcher = pattern.matcher(text);
boolean isMatch = matcher.matches();
if (isMatch) 
  value = Double.parseDouble(text);
但是,它也会匹配空字符串,因此如果不匹配,则必须单独检查。我不知道parseDouble是否为空字符串返回0

编辑:@mkorbel关于将java.swing.text.NumberFormatter与java.text.NumberFormat一起使用的评论比我的更好。您可能会将代码缩减约100倍。。。当您了解自己的库时,就会发生这种情况。

最好将JFormattedTextField与NumberFormat一起使用 java.text.NumberFormatgetCurrencyInstance

最好将JFormattedTextField与NumberFormat一起使用
java.text.NumberFormatgetCurrencyInstance

您在文档筛选器中测试的字符串错误

您不应该测试文本字符串,而应该测试从FilterBypass参数获得的文本和文档生成的字符串,因为这是您想要查看它是否满足条件的字符串。例如,不是这样的:

    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);
    }
而是:

您需要对ContainsonlyNumber方法进行更改以说明这些更改

编辑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;
  }

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

您不应该测试文本字符串,而应该测试从FilterBypass参数获得的文本和文档生成的字符串,因为这是您想要查看它是否满足条件的字符串。例如,不是这样的:

    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);
    }
而是:

您需要对ContainsonlyNumber方法进行更改以说明这些更改

编辑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;
  }

ISO格式的货币美元、欧元。。。或者***Java格式化的$…??我只想要不带货币符号的数字,而不带数字实例和数字格式??:-,这不允许任何字符为0-9和。或者,由于十进制分隔符取决于ISO格式的本地电流美元、欧元。。。或者***Java格式化的$…??我只想要不带货币符号的数字,而不带数字实例和数字格式??:-,这不允许任何字符为0-9和。或者,十进制分隔符取决于Locale@StanisloavL:当然,你会想出一个比我的100+行答案好100倍的单行答案1+满是鳗鱼的气垫船同意了+1@StanisloavL+1谢谢:-我懒得写长答案。@Stanislav:太聪明了,不会把时间浪费在回答问题上,而不是做你做过的事情:提供问题的解决方案。@StanisloavL:当然,你会想出一个比我的100+行好100倍的答案。@StanisloavL:答复:1+满是鳗鱼的气垫船同意+1@StanisloavL+1谢谢:-我懒得写长答案。@Stanislav:我也太聪明了,不会把时间浪费在回答问题上,而不是做你做过的事情上:提供问题的解决方案。你的基本想法很好1+,我已经把它和我的答案结合起来了,希望你不要介意!。您可能希望摆脱静态方法模式前面的新模式。。。;我会帮你把它取下来。干杯你的基本想法是好的1+,我已经把它和我的答案结合起来了,希望你不要介意!。您可能希望摆脱静态方法模式前面的新模式。。。;我会帮你把它取下来。干杯您也吃了很多鳗鱼+1模式和\\d您也吃了很多鳗鱼+1模式和\\d