JTextField内容有效性验证几种方式

在使用 Swing的JTextField时,我们常常希望只接受那些符合我们要求的录入,如数字、电话号码、邮政编码、E-mail等。JFC工作组在这方面也做了很多工作,每一次新的Java Se发布,往往都提供了新的、更方便和强大的有效性验证方式,在这里列举几种不同的验证方式。

  • 利用键盘和焦点事件

这是最直觉的方式。利用 KeyListener来选择允许的字符,且添加FocusListener,使得

内容不符合要求时不允许焦点转移。这种方式很繁琐, Sun的建议是不推荐使用这种方式。

  • 使用自定义的 Document

我们知道, Swing组件是基于MVC实现的。JTextComponent的Model是一个叫做Document的Interface,我们可以通过限制Document的内容来达到有效性验证的目的。javax.swing.text包中有多个不同的Document的实现,JTextField使用的是PlainDocument。如果我们希望JTextField只接受数字,可以实现我们特定的Document并使之替换默认的Document:

package sdn;

   import javax.swing.text.*;

   public class IntegerDocument extends PlainDocument {

     int currentValue = 0;

     public int getValue() {
       return currentValue;
     }

     public void insertString(int offset, String string,
         AttributeSet attributes) throws BadLocationException {

       if (string == null) {
         return;
       } else {
         String newValue;
         int length = getLength();
         if (length == 0) {
           newValue = string;
         } else {
           String currentContent = getText(0, length);
           StringBuffer currentBuffer = 
                new StringBuffer(currentContent);
           currentBuffer.insert(offset, string);
           newValue = currentBuffer.toString();
         }
         currentValue = checkInput(newValue, offset);
         super.insertString(offset, string, attributes);
       }
     }
     public void remove(int offset, int length)
         throws BadLocationException {
       int currentLength = getLength();
       String currentContent = getText(0, currentLength);
       String before = currentContent.substring(0, offset);
       String after = currentContent.substring(length+offset,
         currentLength);
       String newValue = before + after;
       currentValue = checkInput(newValue, offset);
       super.remove(offset, length);
     }
     public int checkInput(String proposedValue, int offset)
         throws BadLocationException {
       if (proposedValue.length() > 0) {
       try {
           int newValue = Integer.parseInt(proposedValue);
           return newValue;
       } catch (NumberFormatException e) {
           throw new BadLocationException(proposedValue, offset);
       }
       } else {
         return 0;
       }
     }
   }

然后用 IntegerDocument去替换JTextField默认的Document:

package sdn;

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

   public class NumericInput {
     public static void main(String args[]) {
       Runnable runner = new Runnable() {
         public void run() {
           JFrame frame = new JFrame("Numeric Input");
           frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
           frame.setLayout(new GridLayout(2, 2));

           frame.add(new JLabel("Number"));
           JTextField fieldOne = new JTextField();
           Document doc= new IntegerDocument();
           fieldOne.setDocument(doc);
           frame.add(fieldOne);

           frame.add(new JLabel("All"));
           JTextField fieldTwo = new JTextField();
           frame.add(fieldTwo);

           frame.setSize(250, 90);
           frame.setVisible(true);
         }
       };
       EventQueue.invokeLater(runner);
     }
   }

代码很简单,一目了然。这里说点题外话, Sun建议的Swing Application的main函数写法如上所示:先建一个Runnable,然后把这个Runnable放到event-dispatch thread中去执行。另外,以前有的Developer(比如我)喜欢用SwingUtilities.invokeLater(runner)来将一个thread放到event-dispatch thread中,现在Sun也建议用EventQueue.invokeLater(runner),因为SwingUtilities方法版本仅仅是对EventQueue方法版本的一个包装。

  • 用 InputVerifier来实现

在 J2SE 1.3中加入了一个名为InputVerifier的抽象类,可用于任何JComponent。其中定义了boolean verifiy(JComponent input)方法。如果组件中的文本是有效的,当焦点转移时(如按下Tab或Shift-Tab),verify方法返回true;否则返回false,使得焦点仍停留在当前组件上。我们仍以数字为例:

package sdn;

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

   public class NumericVerifier{
     public static void main(String args[]) {
       Runnable runner = new Runnable() {
         public void run() {
           JFrame frame = new JFrame("Numeric Verifier");
           frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

           JPanel panel1 = new JPanel(new BorderLayout());
           JLabel label1 = new JLabel("Numeric-only");
           JTextField textField1 = new JTextField();
           panel1.add(label1, BorderLayout.WEST);
           panel1.add(textField1, BorderLayout.CENTER);

           JPanel panel2 = new JPanel(new BorderLayout());
           JLabel label2 = new JLabel("Anything");
           JTextField textField2 = new JTextField();
           panel2.add(label2, BorderLayout.WEST);
           panel2.add(textField2, BorderLayout.CENTER);

           JPanel panel3 = new JPanel(new BorderLayout());
           JLabel label3 = new JLabel("Numeric-only");
           JTextField textField3 = new JTextField();
           panel3.add(label3, BorderLayout.WEST);
           panel3.add(textField3, BorderLayout.CENTER);

           InputVerifier verifier = new InputVerifier() {
               public boolean verify(JComponent comp) {
                   boolean returnValue;
                   JTextField textField = (JTextField)comp;
                   try {
                       Integer.parseInt(textField.getText());
                       returnValue = true;
                   } catch (NumberFormatException e) {
                       Toolkit.getDefaultToolkit().beep();
                       returnValue = false;
                   }
                   return returnValue;
               }
           };

           textField1.setInputVerifier(verifier);
           textField3.setInputVerifier(verifier);

           frame.add(panel1, BorderLayout.NORTH);
           frame.add(panel2, BorderLayout.CENTER);
           frame.add(panel3, BorderLayout.SOUTH);
           frame.setSize(300, 95);
          frame.setVisible(true);
         }
       };
       EventQueue.invokeLater(runner);
     }
   }

这个例子的效果和上一个是不同的。自定义 Document的App中,用户将会发现任何非数字的字符都不会在JTextField中出现;而在使用InputVerifier的App中,用户在录入字符时不会发现任何异常,但是当他确认录入完成后,如果内容不符合有效性,焦点将不会转移!这两种情况都可能让一个没有经验的用户茫然,具体使用哪一种是一个见仁见智的问题。

  • 使用 Document Filter

在 J2SE 1.4中,又加入了一个新的类:DocumentFilter。你无需再实现一个新的Document,而是对现有的Document过滤一遍。它的结果与实现自定义的Document并无二样,仅仅是思路不同而已。

package snd;
   import javax.swing.text.*;
   import java.awt.Toolkit;   

   public class IntegerDocumentFilter extends DocumentFilter {


      int currentValue = 0;   

      public IntegerDocumentFilter() {
      }

      public void insertString(DocumentFilter.FilterBypass fb, 
       int offset, String string, AttributeSet attr) 
       throws BadLocationException {   

        if (string == null) {
          return;
        } else {
          replace(fb, offset, 0, string, attr);
        }
       }   

      public void remove(DocumentFilter.FilterBypass fb, 
       int offset, int length)
       throws BadLocationException {

        replace(fb, offset, length, "", null);
      }

      public void replace(DocumentFilter.FilterBypass fb, 
       int offset, int length, String text, AttributeSet attrs) 
       throws BadLocationException {   

        Document doc = fb.getDocument();
        int currentLength = doc.getLength();
        String currentContent = doc.getText(0, currentLength);
        String before = currentContent.substring(0, offset);
        String after = currentContent.substring(
             length+offset, currentLength);
        String newValue = before + 
             (text == null ? "" : text) + after;
        currentValue = checkInput(newValue, offset);
        fb.replace(offset, length, text, attrs);
      }

      private int checkInput(String proposedValue, int offset)
          throws BadLocationException {
        int newValue = 0;
        if (proposedValue.length() > 0) {
          try {
            newValue = Integer.parseInt(proposedValue);
          } catch (NumberFormatException e) {
            throw new BadLocationException(
              proposedValue, offset);
          }
        }
        return newValue;
      }
   }

再将这个 Filter应用于Document:

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

   public class NumericInputFilter {
     public static void main(String args[]) {
       Runnable runner = new Runnable() {
         public void run() {
           JFrame frame = new JFrame("Numeric Input Filter");
           frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
           frame.setLayout(new GridLayout(2, 2));

           frame.add(new JLabel("Number"));
           JTextField textFieldOne = new JTextField();
           Document doc= textFieldOne.getDocument();
           DocumentFilter filterOne = new IntegerDocumentFilter();
           ((AbstractDocument)doc).setDocumentFilter(filterOne);
           textFieldOne.setDocument(doc);
           frame.add(textFieldOne);

           frame.add(new JLabel("All"));
           JTextField textFieldTwo = new JTextField();
           frame.add(textFieldTwo);

           frame.setSize(250, 90);
           frame.setVisible(true);
         }
       };
       EventQueue.invokeLater(runner);
     }
   }

DocumentFilter只能用于Swing中的与text有关的组件(而InputVerifier可用于任何组件)。除了这几种方法,在对于TextField而言,我们还有JFormattedTextField,很多时候用JFormattedTextField将是非常容易和简单的方式

你可能感兴趣的:(JTextField内容有效性验证几种方式)