сделать пользовательский JTextField (JCurrencyField) - PullRequest
1 голос
/ 01 июля 2011

Я создал 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();
    }
}

Не могли бы вы помочь мне решить эту проблему?

Ответы [ 3 ]

3 голосов
/ 01 июля 2011

лучше использовать JFormattedTextField с NumberFormat java.text.NumberFormat # getCurrencyInstance ()

2 голосов
/ 01 июля 2011

Вы проверяете неверную строку в вашем 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);
    }

А скорее это:

   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;
  }
2 голосов
/ 01 июля 2011

Должно работать следующее регулярное выражение :

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 для пустой строки.)

EDIT : комментарий @mkorbel об использовании java.swing.text.NumberFormatter с java.text.NumberFormat - лучшее решениеэто мое.Вы, вероятно, сократите свой код в ~ 100 раз ... вот что происходит, когда вы знаете свои библиотеки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...