java SWT:限制数值输入的Text文本框通用组件

控制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();
        }
    }
}

你可能感兴趣的:(java,ui)