JTextField內(nèi)容有效性驗(yàn)證幾種方式
在使用 Swing的JTextField時(shí),我們常常希望只接受那些符合我們要求的錄入,如數(shù)字、電話號(hào)碼、郵政編碼、E-mail等。JFC工作組在這方面也做了很多工作,每一次新的Java Se發(fā)布,往往都提供了新的、更方便和強(qiáng)大的有效性驗(yàn)證方式,在這里列舉幾種不同的驗(yàn)證方式。
-
利用鍵盤和焦點(diǎn)事件
這是最直覺(jué)的方式。利用 KeyListener來(lái)選擇允許的字符,且添加FocusListener,使得
內(nèi)容不符合要求時(shí)不允許焦點(diǎn)轉(zhuǎn)移。這種方式很繁瑣, Sun的建議是不推薦使用這種方式。
-
使用自定義的 Document
我們知道, Swing組件是基于MVC實(shí)現(xiàn)的。JTextComponent的Model是一個(gè)叫做Document的Interface,我們可以通過(guò)限制Document的內(nèi)容來(lái)達(dá)到有效性驗(yàn)證的目的。javax.swing.text包中有多個(gè)不同的Document的實(shí)現(xiàn),JTextField使用的是PlainDocument。如果我們希望JTextField只接受數(shù)字,可以實(shí)現(xiàn)我們特定的Document并使之替換默認(rèn)的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默認(rèn)的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);
}
}
代碼很簡(jiǎn)單,一目了然。這里說(shuō)點(diǎn)題外話, Sun建議的Swing Application的main函數(shù)寫(xiě)法如上所示:先建一個(gè)Runnable,然后把這個(gè)Runnable放到event-dispatch thread中去執(zhí)行。另外,以前有的Developer(比如我)喜歡用SwingUtilities.invokeLater(runner)來(lái)將一個(gè)thread放到event-dispatch thread中,現(xiàn)在Sun也建議用EventQueue.invokeLater(runner),因?yàn)?/span>SwingUtilities方法版本僅僅是對(duì)EventQueue方法版本的一個(gè)包裝。
-
用 InputVerifier來(lái)實(shí)現(xiàn)
在 J2SE 1.3中加入了一個(gè)名為InputVerifier的抽象類,可用于任何JComponent。其中定義了boolean verifiy(JComponent input)方法。如果組件中的文本是有效的,當(dāng)焦點(diǎn)轉(zhuǎn)移時(shí)(如按下Tab或Shift-Tab),verify方法返回true;否則返回false,使得焦點(diǎn)仍停留在當(dāng)前組件上。我們?nèi)砸詳?shù)字為例:
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);
}
}
這個(gè)例子的效果和上一個(gè)是不同的。自定義 Document的App中,用戶將會(huì)發(fā)現(xiàn)任何非數(shù)字的字符都不會(huì)在JTextField中出現(xiàn);而在使用InputVerifier的App中,用戶在錄入字符時(shí)不會(huì)發(fā)現(xiàn)任何異常,但是當(dāng)他確認(rèn)錄入完成后,如果內(nèi)容不符合有效性,焦點(diǎn)將不會(huì)轉(zhuǎn)移!這兩種情況都可能讓一個(gè)沒(méi)有經(jīng)驗(yàn)的用戶茫然,具體使用哪一種是一個(gè)見(jiàn)仁見(jiàn)智的問(wèn)題。
-
使用 Document Filter
在 J2SE 1.4中,又加入了一個(gè)新的類:DocumentFilter。你無(wú)需再實(shí)現(xiàn)一個(gè)新的Document,而是對(duì)現(xiàn)有的Document過(guò)濾一遍。它的結(jié)果與實(shí)現(xiàn)自定義的Document并無(wú)二樣,僅僅是思路不同而已。
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;
}
}
再將這個(gè) Filter應(yīng)用于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有關(guān)的組件(而InputVerifier可用于任何組件)。除了這幾種方法,在對(duì)于TextField而言,我們還有JFormattedTextField,很多時(shí)候用JFormattedTextField將是非常容易和簡(jiǎn)單的方式。
注:這篇文章基本根據(jù)SDN的Core Java Tech Tips意譯而來(lái),代碼基本跟其一致,另外還參考了M. Robinson & P. Vorobiev的Swing, Chapter 11
posted on 2006-04-09 16:26 西門町學(xué)士 閱讀(2370) 評(píng)論(0) 編輯 收藏 所屬分類: Java