问题
Here is the code, I am hoping that this will add the constraint that only integer input is valid for the text field:
JFormattedTextField ftf = new JFormattedTextField ();
NumberFormat format = NumberFormat.getNumberInstance();
format.setParseIntegerOnly(true);
ftf.setFormatterFactory(new DefaultFormatterFactory(new NumberFormatter(format), new NumberFormatter(format), new NumberFormatter(format)));
But what suprised me is that when I input 100aaa, it is valid, 100.111, it is valid too. how can I make it to restrict that only integer is valid input?
回答1:
You need to setAllowsInvalid(false)
on the NumberFormatter objects.
回答2:
check this code, there only digits input is allowed, (code from Old.Sun.Com.Tutorial and little bit amended in mvc rules)
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());
}
}
}
来源:https://stackoverflow.com/questions/6148094/a-question-about-numberformatter