控制Text只能输入数值只能输入数值的原理很简单,就是利用VerifyListener侦听器,响应VerifyEvent 事件,对输入内容进行检查。
具体如何验证输入的内容是有效数字,网上有很多文章介绍如何实现,有是检查输入字符是不是0-9,这种方式有局限性,有的是利用正则表达式来判断,写得好复杂。
其实利用Float,Integer,Double这些类的静态方法valeOf(String)就能准确进行检查,valeOf(String)方法将一个字符转为对应类型的数字,如果格式不对就会抛出NumberFormatException 异常。
利用这个特性,就可以很方便的对Text输入的内容进行有效性检查。
在这里有必要解释一下org.eclipse.swt.events.VerifyEvent事件类的成员变量的含义。
VerifyEvent有三个有用的成员变量:text,start,end:
start,end:是指当前事件中Text中文本字符串将被修改的起止范围
text:将被插入到start,end范围的文本字符串,(删除字符时text为空字符串)
有了这三个数据,用java.lang.StringBuffer就可以构造出事件发生后,Text文本的内容,然后就可以用valueOf方法来验证输入的数据是否有效。
下面是验证浮点数(Float)类型数值的实现代码。
NumText.java
package net.gdface.ui;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
public class NumText extends Text {
/** * Create the composite. * * @param parent * @param style */
public NumText(Composite parent, int style) {
super(parent, style);
this.addVerifyListener(new VerifyListener() {
@Override
public void verifyText(VerifyEvent e) {
try {
// 以Text中已经输入的内容创建StringBuffer对象
StringBuffer buffer = new StringBuffer(NumText.this.getText());
// 删除e.start, e.end指定范围的内容
// 并将要插入的内容e.text插入指定的位置,模拟输入e.text后Text对象中的内容
// 末尾添一个0,以保证buffer中只有一个字符为且为+-.时,不会触发NumberFormatException
buffer.delete(e.start, e.end).insert(e.start, e.text).append('0');
// 尝试将buffer中的内容转换成Float,如果不抛出异常说明输入内容有效
Float.valueOf(buffer.toString());
} catch (NumberFormatException ex) {
e.doit=false;
}
}
});
}
@Override
protected void checkSubclass() {
// Disable the check that prevents subclassing of SWT components
}
}
更进一步,我们可以利用reflect技术,将上面的NumText 扩展成支持Float,Integer,Double,Long等类型的泛型类,并提供用户自定义的验证方法,以增强其通用性。
package net.gdface.ui;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
/** * 泛型对象
* 实现数值文本限制的Text组件 * @author guyadong * * @param Text组件接收的数据类型,可为{@link Float},{@link Double},{@link Integer},{@link Long},{@link Byte},{@link Short}类型 */
public class NumText<T extends Number> extends Text {
/** * valueOf方法 */
private final Method valueOf;
/** * toString()方法 */
private final Method toString;
/** * 缺省值 */
private final T defaultValue;
/** * Create the composite. * * @param parent * @param style * @param defaultValue * 缺省值 */
public NumText(Composite parent, int style, T defaultValue) {
super(parent, style);
if (null == defaultValue)
throw new NullPointerException();
try {
this.defaultValue = defaultValue;
// 获取toString方法
toString = defaultValue.getClass().getMethod("toString");
// 调用toString给Text设置初始文本
setText((String) toString.invoke(this.defaultValue));
// 获取valueOf方法
valueOf = defaultValue.getClass().getMethod("valueOf", String.class);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
throw new RuntimeException(e);
}
addVerifyListener(new VerifyListener() {
@SuppressWarnings("unchecked")
@Override
public void verifyText(VerifyEvent e) {
// 以Text中已经输入的内容创建StringBuffer对象
StringBuffer buffer = new StringBuffer(NumText.this.getText());
// 删除e.start, e.end指定范围的内容
// 并将要插入的内容e.text插入指定的位置,模拟输入e.text后Text对象中的内容
buffer.delete(e.start, e.end).insert(e.start, e.text);
boolean appendZero=false;
while(true){
try {
T v=(T) valueOf.invoke(null, buffer.toString());
if(appendZero)
v=(T) valueOf.invoke(null,"0");
// verify返回false则数据不合用户需求
if(!verify(v))
e.doit = false;
break;
} catch (InvocationTargetException ex) {
if (ex.getTargetException() instanceof NumberFormatException) {
if(!appendZero){
// 尝试末尾添一个0再解析,以保证buffer中只有一个字符为且为+-.时,不会触发NumberFormatException
buffer.append('0');
appendZero=true;
continue;
}
e.doit = false;
break;
}
} catch (IllegalAccessException | IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
}
}
});
}
/** * 子类可重写此方法以验证数据符合要求 * @param v * @return 数值符合要求返回true */
protected boolean verify(T v){
return true;
}
@Override
protected void checkSubclass() {
// Disable the check that prevents subclassing of SWT components
}
/** * 获取Text组件中的数值
* 字符串解析抛出{@link NumberFormatException}时返回缺省值 {@link #defaultValue} * @return */
@SuppressWarnings("unchecked")
public T getValue() {
try {
return (T) valueOf.invoke(null, getText());
} catch (InvocationTargetException ex) {
if (ex.getTargetException() instanceof NumberFormatException) {
return defaultValue;
}
throw new RuntimeException(ex);
} catch (IllegalAccessException | IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
}
/** * 设置Text组件中的数值 * @param value */
public void setValue(T value) {
try {
if (null == value)
throw new NullPointerException();
setText((String) toString.invoke(value));
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
相关的测试代码
Setting.java
package net.gdface.ui;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
public class Setting extends Dialog {
protected Object result;
protected Shell shell;
private Text globalAspectRatioValue;
private Text defaultRectSizeValue;
private Text defaultAspectRatioValue;
/** * Create the dialog. * @param parent * @param style */
public Setting(Shell parent, int style) {
super(parent, style);
setText("SWT Dialog");
}
/** * Open the dialog. * @return the result */
public Object open() {
createContents();
shell.open();
shell.layout();
Display display = getParent().getDisplay();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
return result;
}
/** * Create contents of the dialog. */
private void createContents() {
shell = new Shell(getParent(), SWT.DIALOG_TRIM);
shell.setSize(588, 405);
shell.setText(getText());
TabFolder tabFolder = new TabFolder(shell, SWT.NONE);
tabFolder.setBounds(10, 10, 572, 367);
TabItem tbtmEditor = new TabItem(tabFolder, SWT.NONE);
tbtmEditor.setText("矩形编辑器");
Composite composite = new Composite(tabFolder, SWT.NONE);
tbtmEditor.setControl(composite);
Group group = new Group(composite, SWT.NONE);
group.setBounds(22, 10, 243, 116);
Button globalAspectRatioCheck = new Button(group, SWT.CHECK);
globalAspectRatioCheck.setSelection(false);
globalAspectRatioCheck.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
globalAspectRatioValue.setEnabled(globalAspectRatioCheck.getSelection());
defaultAspectRatioValue.setEnabled(!globalAspectRatioCheck.getSelection());
}
});
globalAspectRatioCheck.setBounds(10, 16, 81, 17);
globalAspectRatioCheck.setText("固定宽高比");
globalAspectRatioValue = new NumText(group, SWT.BORDER,0f){
@Override
protected boolean verify(Float v) {
// 重写verify方法,验证数据有效性
return v>0;
}};
globalAspectRatioValue.setEnabled(false);
globalAspectRatioValue.setBounds(150, 11, 73, 23);
Label defaultAspectRatioText = new Label(group, SWT.NONE);
defaultAspectRatioText.setBounds(10, 49, 61, 17);
defaultAspectRatioText.setText("默认宽高比");
defaultAspectRatioValue = new NumText(group, SWT.BORDER,0f){
@Override
protected boolean verify(Float v) {
// 重写verify方法,验证数据有效性
return v>0&&v<1;
}};
defaultAspectRatioValue.setBounds(150, 45, 73, 23);
Label defaultRectSizeText = new Label(group, SWT.NONE);
defaultRectSizeText.setBounds(10, 82, 72, 17);
defaultRectSizeText.setText("默认矩形尺寸");
defaultRectSizeValue = new NumText(group, SWT.BORDER,1){
@Override
protected boolean verify(Integer v) {
// 重写verify方法,验证数据有效性
return v>0&&v<100;
}};
defaultRectSizeValue.setBounds(150, 79, 73, 23);
}
/** * Launch the application. * @param args */
public static void main(String[] args) {
try {
Setting setting = new Setting(new Shell(),SWT.NONE);
setting.open();
} catch (Exception e) {
e.printStackTrace();
}
}
}