Just Java IT

西门町学士关于Java的随便一说而已……

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

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

  • 利用键盘和焦点事件

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

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

  • 使用自定义的 Document

我们知道, Swing组件是基于MVC实现的。JTextComponentModel是一个叫做DocumentInterface,我们可以通过限制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 Applicationmain函数写法如上所示:先建一个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)方法。如果组件中的文本是有效的,当焦点转移时(如按下TabShift-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);
}
}

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

  • 使用 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将是非常容易和简单的方式。

注:这篇文章基本根据SDN的Core Java Tech Tips意译而来,代码基本跟其一致,另外还参考了M. Robinson & P. Vorobiev的Swing, Chapter 11

posted on 2006-04-09 16:26 西门町学士 阅读(2348) 评论(0)  编辑  收藏 所属分类: Java


只有注册用户登录后才能发表评论。


网站导航: