Java 关于数字格式的一个问题

Java 关于数字格式的一个问题,java,swing,number-formatting,jformattedtextfield,Java,Swing,Number Formatting,Jformattedtextfield,下面是代码,我希望这将添加一个约束,即只有整数输入对文本字段有效: JFormattedTextField ftf = new JFormattedTextField (); NumberFormat format = NumberFormat.getNumberInstance(); format.setParseIntegerOnly(true); ftf.setFormatterFactory(new DefaultFormatterFactory(new NumberFormatte

下面是代码,我希望这将添加一个约束,即只有整数输入对文本字段有效:

JFormattedTextField ftf =  new JFormattedTextField ();

NumberFormat format = NumberFormat.getNumberInstance();
format.setParseIntegerOnly(true);
ftf.setFormatterFactory(new DefaultFormatterFactory(new NumberFormatter(format), new NumberFormatter(format), new NumberFormatter(format)));
但令我惊讶的是,当我输入100aaa时,它是有效的,100.111,它也是有效的。
如何限制只有整数才是有效输入?

您需要
在NumberFormatter对象上设置AllowsInvalid(false)

检查此代码,只允许输入数字,(代码来自Old.Sun.Com.Tutorial,并在mvc规则中稍作修改)

导入java.awt.Color;
导入java.awt.Dimension;
导入java.awt.event.ActionEvent;
导入java.awt.event.ActionListener;
导入java.beans.PropertyChangeEvent;
导入java.beans.PropertyChangeListener;
导入java.text.NumberFormat;
导入javax.swing.BorderFactory;
导入javax.swing.BoundedRangeModel;
导入javax.swing.Box;
导入javax.swing.BoxLayout;
导入javax.swing.JComboBox;
导入javax.swing.JFormattedTextField;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入javax.swing.JSlider;
导入javax.swing.UIManager;
导入javax.swing.UnsupportedLookAndFeelException;
导入javax.swing.event.ChangeEvent;
导入javax.swing.event.ChangeListener;
导入javax.swing.event.EventListenerList;
导入javax.swing.text.NumberFormatter;
公共类转换器{
专用转换面板metricPanel、usaPanel;
专用单元[]公制距离=新单元[3];
专用单元[]USADrances=新单元[4];
最终静态布尔多色=假;
//指定要使用的外观。有效值:
//null(使用默认值)、“金属”、“系统”、“主题”、“GTK+”
最终静态字符串LOOKANDFEEL=null;
私有ConverterRangeModel数据模型=新ConverterRangeModel();
专用JPanel主窗格;
/**
*创建转换面板(一个用于公制,另一个用于美国)
*“美国”,因为虽然帝国和美国的距离测量是
*同样,这个程序可以扩展到包括体积测量,这
*不一样。
*/
公共转换器(){
//为公制距离创建单位对象,然后
//用这些单位实例化ConversionPanel。
公制距离[0]=新单位(“厘米”,0.01);
公制距离[1]=新单位(“米”,1.0);
公制距离[2]=新单位(“公里”,1000.0);
metricPanel=新转换面板(此“公制”、公制距离、,
数据模型);
//为美国距离创建单位对象,然后
//用这些单位实例化ConversionPanel。
USA距离[0]=新单位(“英寸”,0.0254);
USA距离[1]=新单位(“英尺”,0.305);
USA距离[2]=新单位(“码”,0.914);
USA距离[3]=新单位(“英里”,1613.0);
USAPLANEL=新转换面板(此“美国系统”,USAPLANEL,
新的FollowerRangeModel(dataModel));
//创建一个JPanel,并将转换面板添加到其中。
mainPane=新的JPanel();
主窗格.setLayout(新的BoxLayout(主窗格,BoxLayout.PAGE_轴));
if(多色){
mainPane.setOpaque(true);
主窗格.背景(新颜色(255,0,0));
}
mainPane.setBorder(BorderFactory.createEmptyByOrder(5,5,5,5));
添加(Box.createRigidArea(新维度(0,5));
主窗格。添加(metricPanel);
添加(Box.createRigidArea(新维度(0,5));
主窗格。添加(usaPanel);
添加(Box.createGlue());
重置最大值(真);
}
公共无效resetMaxValues(布尔resetCurrentValues){
double metricMultiplier=metricPanel.getMultiplier();
double-usaprimulator=usapael.getMultiplier();
int MAX=ConversionPanel.MAX;
if(metricMultiplier>USAP乘法器){
最大值=(int)(ConversionPanel.MAX*(USAPPLIER/metricMultiplier));
}
dataModel.setMaximum(最大值);
如果(重置当前值){
dataModel.setDoubleValue(最大值);
}
}
私有静态void initLookAndFeel(){
字符串lookAndFeel=null;
if(LOOKANDFEEL!=null){
if(LOOKANDFEEL.equals(“金属”)){
lookAndFeel=UIManager.getCrossPlatformLookAndFeelClassName();
}else if(LOOKANDFEEL.equals(“系统”)){
lookAndFeel=UIManager.getSystemLookAndFeelClassName();
}else if(LOOKANDFEEL.equals(“Motif”)){
lookAndFeel=“com.sun.java.swing.plaf.motif.motif lookAndFeel”;
}else如果(LOOKANDFEEL.equals(“GTK+”){//1.4.2中新增
lookAndFeel=“com.sun.java.swing.plaf.gtk.GTKLookAndFeel”;
}否则{
System.err.println(“指定的LOOKANDFEEL的意外值:“+LOOKANDFEEL”);
lookAndFeel=UIManager.getCrossPlatformLookAndFeelClassName();
}
试一试{
UIManager.setLookAndFeel(lookAndFeel);
}catch(classnotfounde异常){
System.err.println(“找不到指定外观的类:“+lookAndFeel”);
System.err.println(“您在类路径中包括了L&F库吗?”);
System.err.println(“使用默认的外观”);
}捕获(无支持的LookandFeelException e){
System.err.println(“无法在此平台上使用指定的外观(“+lookAndFeel+”));
System.err.println(“使用默认的外观”);
}捕获(例外e){
System.err.println(“由于某种原因,无法获取指定的外观(“+lookAndFeel+”);
System.err.println(“使用默认的外观”);
e、 printStackTrace();
}
}
}
/**
*创建GUI并显示它。为了线程安全,应该
*从事件中调用-
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.NumberFormat;

import javax.swing.BorderFactory;
import javax.swing.BoundedRangeModel;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import javax.swing.text.NumberFormatter;

public class Converter {

    private ConversionPanel metricPanel, usaPanel;
    private Unit[] metricDistances = new Unit[3];
    private Unit[] usaDistances = new Unit[4];
    final static boolean MULTICOLORED = false;
    // Specify the look and feel to use. Valid values:
    // null (use the default), "Metal", "System", "Motif", "GTK+"
    final static String LOOKANDFEEL = null;
    private ConverterRangeModel dataModel = new ConverterRangeModel();
    private JPanel mainPane;

    /**
     * Create the ConversionPanels (one for metric, another for U.S.). I used
     * "U.S." because although Imperial and U.S. distance measurements are the
     * same, this program could be extended to include volume measurements, which
     * aren't the same.
     */
    public Converter() {
        // Create Unit objects for metric distances, and then
        // instantiate a ConversionPanel with these Units.
        metricDistances[0] = new Unit("Centimeters", 0.01);
        metricDistances[1] = new Unit("Meters", 1.0);
        metricDistances[2] = new Unit("Kilometers", 1000.0);
        metricPanel = new ConversionPanel(this, "Metric System", metricDistances,
                dataModel);

        // Create Unit objects for U.S. distances, and then
        // instantiate a ConversionPanel with these Units.
        usaDistances[0] = new Unit("Inches", 0.0254);
        usaDistances[1] = new Unit("Feet", 0.305);
        usaDistances[2] = new Unit("Yards", 0.914);
        usaDistances[3] = new Unit("Miles", 1613.0);
        usaPanel = new ConversionPanel(this, "U.S. System", usaDistances,
                new FollowerRangeModel(dataModel));

        // Create a JPanel, and add the ConversionPanels to it.
        mainPane = new JPanel();
        mainPane.setLayout(new BoxLayout(mainPane, BoxLayout.PAGE_AXIS));
        if (MULTICOLORED) {
            mainPane.setOpaque(true);
            mainPane.setBackground(new Color(255, 0, 0));
        }
        mainPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        mainPane.add(Box.createRigidArea(new Dimension(0, 5)));
        mainPane.add(metricPanel);
        mainPane.add(Box.createRigidArea(new Dimension(0, 5)));
        mainPane.add(usaPanel);
        mainPane.add(Box.createGlue());
        resetMaxValues(true);
    }

    public void resetMaxValues(boolean resetCurrentValues) {
        double metricMultiplier = metricPanel.getMultiplier();
        double usaMultiplier = usaPanel.getMultiplier();
        int maximum = ConversionPanel.MAX;
        if (metricMultiplier > usaMultiplier) {
            maximum = (int) (ConversionPanel.MAX * (usaMultiplier / metricMultiplier));
        }
        dataModel.setMaximum(maximum);
        if (resetCurrentValues) {
            dataModel.setDoubleValue(maximum);
        }
    }

    private static void initLookAndFeel() {
        String lookAndFeel = null;
        if (LOOKANDFEEL != null) {
            if (LOOKANDFEEL.equals("Metal")) {
                lookAndFeel = UIManager.getCrossPlatformLookAndFeelClassName();
            } else if (LOOKANDFEEL.equals("System")) {
                lookAndFeel = UIManager.getSystemLookAndFeelClassName();
            } else if (LOOKANDFEEL.equals("Motif")) {
                lookAndFeel = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
            } else if (LOOKANDFEEL.equals("GTK+")) { // new in 1.4.2
                lookAndFeel = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
            } else {
                System.err.println("Unexpected value of LOOKANDFEEL specified: " + LOOKANDFEEL);
                lookAndFeel = UIManager.getCrossPlatformLookAndFeelClassName();
            }
            try {
                UIManager.setLookAndFeel(lookAndFeel);
            } catch (ClassNotFoundException e) {
                System.err.println("Couldn't find class for specified look and feel:" + lookAndFeel);
                System.err.println("Did you include the L&F library in the class path?");
                System.err.println("Using the default look and feel.");
            } catch (UnsupportedLookAndFeelException e) {
                System.err.println("Can't use the specified look and feel (" + lookAndFeel + ") on this platform.");
                System.err.println("Using the default look and feel.");
            } catch (Exception e) {
                System.err.println("Couldn't get specified look and feel (" + lookAndFeel + "), for some reason.");
                System.err.println("Using the default look and feel.");
                e.printStackTrace();
            }
        }
    }

    /**
     * Create the GUI and show it. For thread safety, this method should be
     * invoked from the event-dispatching thread.
     */
    private static void createAndShowGUI() {
        initLookAndFeel();
        JFrame frame = new JFrame("Converter");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Converter converter = new Converter();
        converter.mainPane.setOpaque(true);
        frame.setContentPane(converter.mainPane);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

/**
 * Based on the source code for DefaultBoundedRangeModel, this class stores its
 * value as a double, rather than an int. The minimum value and extent are
 * always 0.
 */
class ConverterRangeModel implements BoundedRangeModel {

    protected ChangeEvent changeEvent = null;
    protected EventListenerList listenerList = new EventListenerList();
    protected int maximum = 10000;
    protected int minimum = 0;
    protected int extent = 0;
    protected double value = 0.0;
    protected double multiplier = 1.0;
    protected boolean isAdjusting = false;

    public ConverterRangeModel() {
    }

    public double getMultiplier() {
        return multiplier;
    }

    public void setMultiplier(double multiplier) {
        this.multiplier = multiplier;
        fireStateChanged();
    }

    @Override
    public int getMaximum() {
        return maximum;
    }

    @Override
    public void setMaximum(int newMaximum) {
        setRangeProperties(value, extent, minimum, newMaximum, isAdjusting);
    }

    @Override
    public int getMinimum() {
        return (int) minimum;
    }

    @Override
    public void setMinimum(int newMinimum) {
        System.out.println("In ConverterRangeModel setMinimum");
        // Do nothing.
    }

    @Override
    public int getValue() {
        return (int) getDoubleValue();
    }

    @Override
    public void setValue(int newValue) {
        setDoubleValue((double) newValue);
    }

    public double getDoubleValue() {
        return value;
    }

    public void setDoubleValue(double newValue) {
        setRangeProperties(newValue, extent, minimum, maximum, isAdjusting);
    }

    @Override
    public int getExtent() {
        return (int) extent;
    }

    @Override
    public void setExtent(int newExtent) {
        // Do nothing.
    }

    @Override
    public boolean getValueIsAdjusting() {
        return isAdjusting;
    }

    @Override
    public void setValueIsAdjusting(boolean b) {
        setRangeProperties(value, extent, minimum, maximum, b);
    }

    @Override
    public void setRangeProperties(int newValue, int newExtent, int newMin, int newMax, boolean newAdjusting) {
        setRangeProperties((double) newValue, newExtent, newMin, newMax, newAdjusting);
    }

    public void setRangeProperties(double newValue, int unusedExtent,
            int unusedMin, int newMax, boolean newAdjusting) {
        if (newMax <= minimum) {
            newMax = minimum + 1;
        }
        if (Math.round(newValue) > newMax) { // allow some rounding error
            newValue = newMax;
        }
        boolean changeOccurred = false;
        if (newValue != value) {
            value = newValue;
            changeOccurred = true;
        }
        if (newMax != maximum) {
            maximum = newMax;
            changeOccurred = true;
        }
        if (newAdjusting != isAdjusting) {
            maximum = newMax;
            isAdjusting = newAdjusting;
            changeOccurred = true;
        }
        if (changeOccurred) {
            fireStateChanged();
        }
    }

    /*
     * The rest of this is event handling code copied from
     * DefaultBoundedRangeModel.
     */
    @Override
    public void addChangeListener(ChangeListener l) {
        listenerList.add(ChangeListener.class, l);
    }

    @Override
    public void removeChangeListener(ChangeListener l) {
        listenerList.remove(ChangeListener.class, l);
    }

    protected void fireStateChanged() {
        Object[] listeners = listenerList.getListenerList();
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == ChangeListener.class) {
                if (changeEvent == null) {
                    changeEvent = new ChangeEvent(this);
                }
                ((ChangeListener) listeners[i + 1]).stateChanged(changeEvent);
            }
        }
    }
}


/*
 * Works in 1.1+Swing, 1.4, and all releases in between. Used by the Converter
 * example.
 */
/**
 * Implements a model whose data is actually in another model (the "source
 * model"). The follower model adjusts the values obtained from the source model
 * (or set in the follower model) to be in a different unit of measure.
 *
 */
class FollowerRangeModel extends ConverterRangeModel implements ChangeListener {

    private ConverterRangeModel sourceModel; // the real model

    /** Creates a FollowerRangeModel that gets its state from sourceModel. */
    public FollowerRangeModel(ConverterRangeModel sourceModel) {
        this.sourceModel = sourceModel;
        sourceModel.addChangeListener(this);
    }

    @Override // The only method in the ChangeListener interface.
    public void stateChanged(ChangeEvent e) {
        fireStateChanged();
    }

    @Override
    public int getMaximum() {
        int modelMax = sourceModel.getMaximum();
        double multiplyBy = sourceModel.getMultiplier() / this.getMultiplier();
        return (int) (modelMax * multiplyBy);
    }

    @Override
    public void setMaximum(int newMaximum) {
        sourceModel.setMaximum((int) (newMaximum * (this.getMultiplier() / sourceModel.getMultiplier())));
    }

    @Override
    public int getValue() {
        return (int) getDoubleValue();
    }

    @Override
    public void setValue(int newValue) {
        setDoubleValue((double) newValue);
    }

    @Override
    public double getDoubleValue() {
        return sourceModel.getDoubleValue() * sourceModel.getMultiplier()
                / this.getMultiplier();
    }

    @Override
    public void setDoubleValue(double newValue) {
        sourceModel.setDoubleValue(newValue * this.getMultiplier()
                / sourceModel.getMultiplier());
    }

    @Override
    public int getExtent() {
        return super.getExtent();
    }

    @Override
    public void setExtent(int newExtent) {
        super.setExtent(newExtent);
    }

    @Override
    public void setRangeProperties(int value, int extent, int min, int max,
            boolean adjusting) {
        double multiplyBy = this.getMultiplier() / sourceModel.getMultiplier();
        sourceModel.setRangeProperties(value * multiplyBy, extent, min, (int) (max * multiplyBy), adjusting);
    }
}


/*
 * Works in 1.1+Swing, 1.4, and all releases in between. Used by the Converter
 * example.
 */
class Unit {

    public String description;
    public double multiplier;

    Unit(String description, double multiplier) {
        super();
        this.description = description;
        this.multiplier = multiplier;
    }

    @Override
    public String toString() {
        String s = "Meters/" + description + " = " + multiplier;
        return s;
    }
}

/*
 * A 1.4 class used by the Converter example.
 */
class ConversionPanel extends JPanel implements ActionListener, ChangeListener, PropertyChangeListener {

    private static final long serialVersionUID = 1L;
    private JFormattedTextField textField;
    private JComboBox unitChooser;
    private JSlider slider;
    private ConverterRangeModel sliderModel;
    private Converter controller;
    private Unit[] units;
    private String title;
    private NumberFormat numberFormat;
    final static boolean MULTICOLORED = false;
    final static int MAX = 10000;

    ConversionPanel(Converter myController, String myTitle, Unit[] myUnits, ConverterRangeModel myModel) {
        if (MULTICOLORED) {
            setOpaque(true);
            setBackground(new Color(0, 255, 255));
        }
        setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(myTitle), BorderFactory.createEmptyBorder(5, 5, 5, 5)));
        controller = myController;// Save arguments in instance variables.
        units = myUnits;
        title = myTitle;
        sliderModel = myModel;
        numberFormat = NumberFormat.getNumberInstance();// Create the text field format, and then the text field.
        numberFormat.setMaximumFractionDigits(2);
        NumberFormatter formatter = new NumberFormatter(numberFormat);
        formatter.setAllowsInvalid(false);
        formatter.setCommitsOnValidEdit(true);// seems to be a no-op -- aha -- it changes the value property but doesn't cause the result to
        // be parsed (that happens on focus loss/return, I think).
        textField = new JFormattedTextField(formatter);
        textField.setColumns(10);
        textField.setValue(new Double(sliderModel.getDoubleValue()));
        textField.addPropertyChangeListener(this);       
        unitChooser = new JComboBox(); // Add the combo box.
        for (int i = 0; i < units.length; i++) { // Populate it.
            unitChooser.addItem(units[i].description);
        }
        unitChooser.setSelectedIndex(0);
        sliderModel.setMultiplier(units[0].multiplier);
        unitChooser.addActionListener(this);
        slider = new JSlider(sliderModel); // Add the slider.
        sliderModel.addChangeListener(this);
        // Make the text field/slider group a fixed size
        // to make stacked ConversionPanels nicely aligned.
        JPanel unitGroup = new JPanel() {

            private static final long serialVersionUID = 1L;

            @Override
            public Dimension getMinimumSize() {
                return getPreferredSize();
            }

            @Override
            public Dimension getPreferredSize() {
                return new Dimension(150, super.getPreferredSize().height);
            }

            @Override
            public Dimension getMaximumSize() {
                return getPreferredSize();
            }
        };
        unitGroup.setLayout(new BoxLayout(unitGroup, BoxLayout.PAGE_AXIS));
        if (MULTICOLORED) {
            unitGroup.setOpaque(true);
            unitGroup.setBackground(new Color(0, 0, 255));
        }
        unitGroup.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
        unitGroup.add(textField);
        unitGroup.add(slider);
        JPanel chooserPanel = new JPanel();// Create a subpanel so the combo box isn't too tall and is sufficiently wide.
        chooserPanel.setLayout(new BoxLayout(chooserPanel, BoxLayout.PAGE_AXIS));
        if (MULTICOLORED) {
            chooserPanel.setOpaque(true);
            chooserPanel.setBackground(new Color(255, 0, 255));
        }
        chooserPanel.add(unitChooser);
        chooserPanel.add(Box.createHorizontalStrut(100));
        setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));// Put everything together.
        add(unitGroup);
        add(chooserPanel);
        unitGroup.setAlignmentY(TOP_ALIGNMENT);
        chooserPanel.setAlignmentY(TOP_ALIGNMENT);
    }

    // Don't allow this panel to get taller than its preferred size.
    // BoxLayout pays attention to maximum size, though most layout
    // managers don't.
    @Override
    public Dimension getMaximumSize() {
        return new Dimension(Integer.MAX_VALUE, getPreferredSize().height);
    }

    /**
     * Returns the multiplier (units/meter) for the currently selected unit of
     * measurement.
     */
    public double getMultiplier() {
        return sliderModel.getMultiplier();
    }

    public double getValue() {
        return sliderModel.getDoubleValue();
    }

    /** Updates the text field when the main data model is updated. */
    @Override
    public void stateChanged(ChangeEvent e) {
        int min = sliderModel.getMinimum();
        int max = sliderModel.getMaximum();
        double value = sliderModel.getDoubleValue();
        NumberFormatter formatter = (NumberFormatter) textField.getFormatter();
        formatter.setMinimum(new Double(min));
        formatter.setMaximum(new Double(max));
        textField.setValue(new Double(value));
    }

    /**
     * Responds to the user choosing a new unit from the combo box.
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        int i = unitChooser.getSelectedIndex(); // Combo box event. Set new maximums for the sliders.
        sliderModel.setMultiplier(units[i].multiplier);
        controller.resetMaxValues(false);
    }

    /**
     * Detects when the value of the text field (not necessarily the same number
     * as you'd get from getText) changes.
     */
    @Override
    public void propertyChange(PropertyChangeEvent e) {
        if ("value".equals(e.getPropertyName())) {
            Number value = (Number) e.getNewValue();
            sliderModel.setDoubleValue(value.doubleValue());
        }
    }
}