Есть ли способ принимать только числовые значения в JTextField
? Есть ли какой-то специальный метод для этого?
Есть ли способ принимать только числовые значения в JTextField?
Ответы (17)
Поскольку этот вопрос повторяется довольно часто, я приложил больше усилий к этому ответу, чем обычно.
Мой голос принадлежит JFormattedTextField
. IMO каждый разработчик Swing должен иметь улучшенную версию этого класса в своем наборе инструментов, поскольку он позволяет проверить практически все, что вы можете придумать, с помощью правильного выбора Format
. Примеры, для которых я уже использовал это:
- Строковый ввод, где
String
не может быть пустым - Ввод координат
- Ввод даты
- Редактор на
JSpinner
- Масштабы карты
- Числа
- ...
Это также позволяет визуальную обратную связь, когда ввод недействителен, что, например, не в случае с InputVerifier
. Он по-прежнему позволяет пользователю вводить что угодно, но это значение просто не принимается, если оно недействительно, и это значение никогда не покидает пользовательский интерфейс. Я думаю (но опять же, это мое мнение), что лучше позволить пользователю вводить неверный ввод, который просто удаляет это автоматически, например. DocumentFilter
. Я бы заподозрил баг, когда набираешь символ в текстовом поле, а он не появляется.
Позвольте мне проиллюстрировать это с помощью некоторого кода (на самом деле довольно большого количества кода). Сначала небольшое демонстрационное приложение. Это приложение просто показывает JFormattedTextField
для чисел. Просто использование другого формата позволяет повторно использовать этот компонент для совершенно разных проверок.
import be.pcl.swing.ImprovedFormattedTextField;
import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.NumberFormat;
/**
* See http://stackoverflow.com/q/1313390/1076463
*/
public class FormattedTextFieldDemo {
public static void main( String[] args ) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame testFrame = new JFrame( "FormattedTextFieldDemo" );
NumberFormat integerNumberInstance = NumberFormat.getIntegerInstance();
ImprovedFormattedTextField integerFormattedTextField = new ImprovedFormattedTextField( integerNumberInstance, 100 );
integerFormattedTextField.setColumns( 20 );
testFrame.add( createButtonPanel( integerFormattedTextField ), BorderLayout.NORTH );
final JTextArea textArea = new JTextArea(50, 50);
PropertyChangeListener updateTextAreaListener = new PropertyChangeListener() {
@Override
public void propertyChange( PropertyChangeEvent evt ) {
textArea.append( "New value: " + evt.getNewValue() + "\n" );
}
};
integerFormattedTextField.addPropertyChangeListener( "value", updateTextAreaListener );
testFrame.add( new JScrollPane( textArea ), BorderLayout.CENTER );
testFrame.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE );
testFrame.pack();
testFrame.setVisible( true );
}
} );
}
private static JPanel createButtonPanel( final JFormattedTextField aTextField ){
JPanel panel = new JPanel( new BorderLayout( ) );
panel.add( aTextField, BorderLayout.WEST );
Action action = new AbstractAction() {
{
aTextField.addPropertyChangeListener( "editValid", new PropertyChangeListener() {
@Override
public void propertyChange( PropertyChangeEvent evt ) {
setEnabled( ( ( Boolean ) evt.getNewValue() ) );
}
} );
putValue( Action.NAME, "Show current value" );
}
@Override
public void actionPerformed( ActionEvent e ) {
JOptionPane.showMessageDialog( null, "The current value is [" + aTextField.getValue() + "] of class [" + aTextField.getValue().getClass() + "]" );
}
};
panel.add( new JButton( action ), BorderLayout.EAST );
return panel;
}
}
который просто показывает ImprovedFormattedTextField
и JButton
, которые включены только тогда, когда ввод действителен (ага, съешьте это решение DocumentFilter
). Он также показывает JTextArea
, в котором значение печатается каждый раз, когда встречается новое допустимое значение. При нажатии на кнопку отображается значение.
Код для ImprovedFormattedTextField
можно найти ниже вместе с ParseAllFormat
, от которого он зависит.
package be.pcl.swing;
import javax.swing.JFormattedTextField;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.Color;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.text.Format;
import java.text.ParseException;
/**
* <p>Extension of {@code JFormattedTextField} which solves some of the usability issues</p>
*/
public class ImprovedFormattedTextField extends JFormattedTextField {
private static final Color ERROR_BACKGROUND_COLOR = new Color( 255, 215, 215 );
private static final Color ERROR_FOREGROUND_COLOR = null;
private Color fBackground, fForeground;
/**
* Create a new {@code ImprovedFormattedTextField} instance which will use {@code aFormat} for the
* validation of the user input.
*
* @param aFormat The format. May not be {@code null}
*/
public ImprovedFormattedTextField( Format aFormat ) {
//use a ParseAllFormat as we do not want to accept user input which is partially valid
super( new ParseAllFormat( aFormat ) );
setFocusLostBehavior( JFormattedTextField.COMMIT_OR_REVERT );
updateBackgroundOnEachUpdate();
//improve the caret behavior
//see also http://tips4java.wordpress.com/2010/02/21/formatted-text-field-tips/
addFocusListener( new MousePositionCorrectorListener() );
}
/**
* Create a new {@code ImprovedFormattedTextField} instance which will use {@code aFormat} for the
* validation of the user input. The field will be initialized with {@code aValue}.
*
* @param aFormat The format. May not be {@code null}
* @param aValue The initial value
*/
public ImprovedFormattedTextField( Format aFormat, Object aValue ) {
this( aFormat );
setValue( aValue );
}
private void updateBackgroundOnEachUpdate() {
getDocument().addDocumentListener( new DocumentListener() {
@Override
public void insertUpdate( DocumentEvent e ) {
updateBackground();
}
@Override
public void removeUpdate( DocumentEvent e ) {
updateBackground();
}
@Override
public void changedUpdate( DocumentEvent e ) {
updateBackground();
}
} );
}
/**
* Update the background color depending on the valid state of the current input. This provides
* visual feedback to the user
*/
private void updateBackground() {
boolean valid = validContent();
if ( ERROR_BACKGROUND_COLOR != null ) {
setBackground( valid ? fBackground : ERROR_BACKGROUND_COLOR );
}
if ( ERROR_FOREGROUND_COLOR != null ) {
setForeground( valid ? fForeground : ERROR_FOREGROUND_COLOR );
}
}
@Override
public void updateUI() {
super.updateUI();
fBackground = getBackground();
fForeground = getForeground();
}
private boolean validContent() {
AbstractFormatter formatter = getFormatter();
if ( formatter != null ) {
try {
formatter.stringToValue( getText() );
return true;
} catch ( ParseException e ) {
return false;
}
}
return true;
}
@Override
public void setValue( Object value ) {
boolean validValue = true;
//before setting the value, parse it by using the format
try {
AbstractFormatter formatter = getFormatter();
if ( formatter != null ) {
formatter.valueToString( value );
}
} catch ( ParseException e ) {
validValue = false;
updateBackground();
}
//only set the value when valid
if ( validValue ) {
int old_caret_position = getCaretPosition();
super.setValue( value );
setCaretPosition( Math.min( old_caret_position, getText().length() ) );
}
}
@Override
protected boolean processKeyBinding( KeyStroke ks, KeyEvent e, int condition, boolean pressed ) {
//do not let the formatted text field consume the enters. This allows to trigger an OK button by
//pressing enter from within the formatted text field
if ( validContent() ) {
return super.processKeyBinding( ks, e,
condition, pressed ) && ks != KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 );
}
else {
return super.processKeyBinding( ks, e,
condition, pressed );
}
}
private static class MousePositionCorrectorListener extends FocusAdapter {
@Override
public void focusGained( FocusEvent e ) {
/* After a formatted text field gains focus, it replaces its text with its
* current value, formatted appropriately of course. It does this after
* any focus listeners are notified. We want to make sure that the caret
* is placed in the correct position rather than the dumb default that is
* before the 1st character ! */
final JTextField field = ( JTextField ) e.getSource();
final int dot = field.getCaret().getDot();
final int mark = field.getCaret().getMark();
if ( field.isEnabled() && field.isEditable() ) {
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
// Only set the caret if the textfield hasn't got a selection on it
if ( dot == mark ) {
field.getCaret().setDot( dot );
}
}
} );
}
}
}
}
Класс ParseAllFormat
:
package be.pcl.swing;
import java.text.AttributedCharacterIterator;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
/**
* <p>Decorator for a {@link Format Format} which only accepts values which can be completely parsed
* by the delegate format. If the value can only be partially parsed, the decorator will refuse to
* parse the value.</p>
*/
public class ParseAllFormat extends Format {
private final Format fDelegate;
/**
* Decorate <code>aDelegate</code> to make sure if parser everything or nothing
*
* @param aDelegate The delegate format
*/
public ParseAllFormat( Format aDelegate ) {
fDelegate = aDelegate;
}
@Override
public StringBuffer format( Object obj, StringBuffer toAppendTo, FieldPosition pos ) {
return fDelegate.format( obj, toAppendTo, pos );
}
@Override
public AttributedCharacterIterator formatToCharacterIterator( Object obj ) {
return fDelegate.formatToCharacterIterator( obj );
}
@Override
public Object parseObject( String source, ParsePosition pos ) {
int initialIndex = pos.getIndex();
Object result = fDelegate.parseObject( source, pos );
if ( result != null && pos.getIndex() < source.length() ) {
int errorIndex = pos.getIndex();
pos.setIndex( initialIndex );
pos.setErrorIndex( errorIndex );
return null;
}
return result;
}
@Override
public Object parseObject( String source ) throws ParseException {
//no need to delegate the call, super will call the parseObject( source, pos ) method
return super.parseObject( source );
}
}
Возможные улучшения:
setBackground
не уважают все взгляды и чувства. Иногда вместо этого можно использоватьsetForeground
, но даже это не гарантируется соблюдением всеми L&F. Поэтому для визуальной обратной связи может быть лучше использовать восклицательный знак, размещенный рядом с полем. Недостаток в том, что это может испортить макет, если вы внезапно добавите/удалите значок.- обратная связь только указывает, что ввод действителен/недействителен. Нет ничего, что указывало бы на ожидаемый формат. Возможное решение — использовать самостоятельно созданное расширение
Format
, которое включает описание/пример допустимого ввода, и поместить его в качестве всплывающей подсказки вJFormattedTextField
.
JFormattedTextField
для разрешения только алфавитов переменной длины?
- person Akshat; 01.04.2015
java.util.format
реализацией, о которой вы только можете подумать.
- person Robin; 01.04.2015
Этот вопрос был назван «точной копией» другого вопроса, который с тех пор был закрыт. Ответы на этот вопрос были настолько плохими, что я был вдохновлен помочь любому, кто мог бы найти его позже, сославшись на гораздо лучший ответ для этого варианта использования.
Это ответ на закрытый вопрос и может быть резюмирован как..
Вместо этого используйте JSpinner
.
Хотя есть чистое зло JFormattedTextField
, нет тривиального способа сделать это, используя только библиотеку Swing. Лучший способ реализовать такую функцию — использовать DocumentFilter
.
Некоторый код, который я подготовил ранее. Небольшое описание.
Простой подход заключается в создании подкласса JTextField и переопределении метода createDefaultModel() путем возврата настроенного подкласса PlainDocument. Пример - текстовое поле только для целых чисел:
public class NumberField extends JTextField {
@Override
protected Document createDefaultModel() {
return new Numberdocument();
}
class Numberdocument extends PlainDocument
{
String numbers="1234567890-";
@Override
public void insertString(int offs, String str, AttributeSet a)
throws BadLocationException {
if(!numbers.contains(str));
else super.insertString(offs, str, a);
}
}
Обрабатывать ввод в insertString() любым способом.
Быстрое решение:
JTextField textField = new JTextField() {
public void processKeyEvent(KeyEvent ev) {
char c = ev.getKeyChar();
if (c >= 48 && c <= 57) { // c = '0' ... c = '9'
super.processKeyEvent(ev);
}
}
};
Проблема с приведенным выше решением заключается в том, что пользователь не может использовать клавиши «Удалить», «Стрелка влево», «Стрелка вправо» или «Забой» в текстовом поле, поэтому я предлагаю использовать это решение:
this.portTextField = new JTextField() {
public void processKeyEvent(KeyEvent ev) {
char c = ev.getKeyChar();
try {
// Ignore all non-printable characters. Just check the printable ones.
if (c > 31 && c < 127) {
Integer.parseInt(c + "");
}
super.processKeyEvent(ev);
}
catch (NumberFormatException nfe) {
// Do nothing. Character inputted is not a number, so ignore it.
}
}
};
if (JTextField.getText().equals("") || !(Pattern.matches("^[0-9]+$", JTextField.getText()))) {
JOptionPane.showMessageDialog(null, " JTextField Invalide !!!!! ");
}
- if JTextField.getText().equals("") ==-> если JTextField пуст
- if(!(Pattern.matches("^[0-9]+$", JTextField.getText()))) ==-> если TextField содержит символы, отличные от указанных
- JOptionPane.showMessageDialog(null, "JTextField Invalide !!!!!");==-> поэтому это сообщение будет разделено
Используйте formatter
для форматирования текстового поля.
NumberFormat format = NumberFormat.getInstance();
format.setGroupingUsed(false);
NumberFormatter formatter = new NumberFormatter(format);
formatter.setValueClass(Integer.class);
formatter.setMaximum(65535);
formatter.setAllowsInvalid(false);
formatter.setCommitsOnValidEdit(true);
myTextField = new JFormattedTextField(formatter);
Учитывая количество просмотров этого вопроса, я не нашел ни одного из приведенных выше решений, подходящих для моей проблемы. Я решил создать собственный PlainDocument< /strong> в соответствии с моими потребностями. Это решение также издает звуковой сигнал, когда достигается максимальное количество используемых символов или вставленный текст не является целым числом.
private class FixedSizeNumberDocument extends PlainDocument
{
private JTextComponent owner;
private int fixedSize;
public FixedSizeNumberDocument(JTextComponent owner, int fixedSize)
{
this.owner = owner;
this.fixedSize = fixedSize;
}
@Override
public void insertString(int offs, String str, AttributeSet a)
throws BadLocationException
{
if (getLength() + str.length() > fixedSize) {
str = str.substring(0, fixedSize - getLength());
this.owner.getToolkit().beep();
}
try {
Integer.parseInt(str);
} catch (NumberFormatException e) {
// inserted text is not a number
this.owner.getToolkit().beep();
return;
}
super.insertString(offs, str, a);
}
}
реализовано следующим образом:
JTextField textfield = new JTextField();
textfield.setDocument(new FixedSizeNumberDocument(textfield,5));
Попробуйте это в событии нажатия клавиши для связанного JTextField.
private void JTextField(java.awt.event.KeyEvent evt) {
// TODO add your handling code here:
char enter = evt.getKeyChar();
if(!(Character.isDigit(enter))){
evt.consume();
}
}
Очень простое решение — использовать прослушиватель действий.
TextFieldActionPerformed(java.awt.event.ActionEvent evt) {
String textFieldValue = TextField.getText();
try {
Integer.parseInteger(textFieldValue);
} catch(Exception e){
JOptionPane.showMessageDialog(null, "Please insert Valid Number Only");
TextField.setText(textFieldValue.substring(0,textFieldValue.length()-1));
}
}
Вы также можете использовать его для Double:
Double.parseDouble(TextField.getText());
запишите этот код в набранный ключ
char c=evt.getKeyChar();
if(!(Character.isDigit(c) || (c==KeyEvent.VK_BACK_SPACE || c==KeyEvent.VK_DELETE)))
{
getToolkit().beep();
evt.consume();
}
numberField = новый JFormattedTextField (NumberFormat.getInstance());
Руководство по форматированию текстовых полей
Вы хотели бы взглянуть на JFormattedTextField.
Форматированные текстовые поля позволяют разработчикам указывать допустимый набор символов, которые можно вводить в текстовое поле
Это подкласс JTextField, поэтому вы можете использовать его следующим образом:
JTextField textField = new JFormattedTextField(NumberFormat.getInstance());
Я думаю, что это лучшее решение:
JTextField textField = new JFormattedTextField(new MaskFormatter("###")); //
Вы можете создать красивое текстовое поле в java, которое принимает или разрешает только числовые значения. Вы даже можете установить точность для значений с плавающей запятой... проверьте код в зибокодах