JTextField, ограничивающий ввод количества символов и принимающий только цифры
Вот код, который я имею о том, как ограничить длину ввода символов
class JTextFieldLimit extends PlainDocument {
private int limit;
// optional uppercase conversion
private boolean toUppercase = false;
JTextFieldLimit(int limit) {
super();
this.limit = limit;
}
JTextFieldLimit(int limit, boolean upper) {
super();
this.limit = limit;
toUppercase = upper;
}
@Override
public void insertString
(int offset, String str, AttributeSet attr)
throws BadLocationException {
if (str == null) return;
if ((getLength() + str.length()) <= limit) {
if (toUppercase) str = str.toUpperCase();
super.insertString(offset, str, attr);
}
}
}
может быть реализовано txtSample.setDocument(new JTextFieldLimit(30));
и вот что я имею при приеме только числовых чисел (он принимает десятичные числа, хотя w/c i не нужно)
class NumericDocument extends PlainDocument {
protected int decimalPrecision = 0;
protected boolean allowNegative = false;
public NumericDocument(int decimalPrecision, boolean allowNegative) {
super();
this.decimalPrecision = decimalPrecision;
this.allowNegative = allowNegative;
}
@Override
public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException {
if (str != null){
if (StringFormat.isNumeric(str) == false && str.equals(".") == false && str.equals("-") == false){ //First, is it a valid character?
Toolkit.getDefaultToolkit().beep();
return;
}
else if (str.equals(".") == true && super.getText(0, super.getLength()).contains(".") == true){ //Next, can we place a decimal here?
Toolkit.getDefaultToolkit().beep();
return;
}
else if (StringFormat.isNumeric(str) == true && super.getText(0, super.getLength()).indexOf(",") != -1 && offset>super.getText(0, super.getLength()).indexOf(",") && super.getLength()-super.getText(0, super.getLength()).indexOf(".")>decimalPrecision && decimalPrecision > 0){ //Next, do we get past the decimal precision limit?
Toolkit.getDefaultToolkit().beep();
return;
}
else if (str.equals("-") == true && (offset != 0 || allowNegative == false)){ //Next, can we put a negative sign?
Toolkit.getDefaultToolkit().beep();
return;
}
super.insertString(offset, str, attr);
}
return;
}
public static class StringFormat
{
public StringFormat()
{
}
public static boolean isNumeric(String str)
{
try
{
int x = Integer.parseInt(str);
System.out.println(x); return true;
} catch(NumberFormatException nFE)
{
System.out.println("Not an Integer"); return false;
}
}
}
}
и вот как использовать этот код: txtSample.setDocument(new NumericDocument(0,false));
Теперь проблема заключается в txtSample
может только setDocument
один раз. Как ограничить длину поля jtext и одновременно принимать числа? Или есть более простой способ сделать это? Благодарю.:D
7 ответов
Вы на правильном пути, за исключением того, что вы захотите использовать DocumentFilter вместо реализации своего собственного документа.
В блоге MDP есть множество отличных примеров (включая ограничение длины и типа символов).
Теперь на ваш вопрос, вы можете создать каскадный фильтр, где вы можете объединить несколько фильтров в цепочку.
Это позволит вам вызывать каждый фильтр по очереди.
public class ChainableFilter extends DocumentFilter {
private List<DocumentFilter> filters;
private AttributeSet attr;
public ChainableFilter() {
filters = new ArrayList<DocumentFilter>(25);
}
public void addFilter(DocumentFilter filter) {
filters.add(filter);
}
public void removeFilter(DocumentFilter filter) {
filters.remove(filter);
}
public void insertString(DocumentFilter.FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
for (DocumentFilter filter : filters) {
filter.insertString(fb, offset, string, attr);
}
}
public void remove(DocumentFilter.FilterBypass fb, int offset, int length) throws BadLocationException {
for (DocumentFilter filter : filters) {
filter.remove(fb, offset, length);
}
}
public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
for (DocumentFilter filter : filters) {
filter.replace(fb, offset, length, text, attrs);
}
}
}
Теперь было бы хорошо, если бы фильтр мог на самом деле сказать цепочке, изменил ли он вообще документ, но я оставлю это на ваше усмотрение.
ОБНОВЛЕНО
Основная концепция между тем, что вы сделали, и как DocumentFilters
работа почти такая же. Преимущество в том, что вы не ограничиваете себя PlainDocument
Вы можете, теоретически, применить его к JTextPane
или же JEditorPane
,
Основная идея цепочки фильтров проста.
ChainableFilter chainableFilter = new ChainableFilter();
chainableFilter.addFilter(new RestrictedLengthFilter()); // User supplied filter
chainableFilter.addFilter(new NumericFilter()); // User supplied filter
((AbstractDocument)textField.getDocument()).setDocumentFilter(chainableFilter);
Что касается фактических фильтров, я бы проверил ссылку, которую я разместил ранее. Вы на правильном пути со своими идеями, хотя
ОБНОВЛЕНО
SizeFilter sizeFilter = new SizeFilter(12);
NumberFilter numFilter = new NumberFilter();
ChainableFilter chainFilter = new ChainableFilter();
chainFilter.addFilter(sizeFilter);
chainFilter.addFilter(numFilter);
JTextField field = new JTextField();
((AbstractDocument) field.getDocument()).setDocumentFilter(chainFilter);
public class NumberFilter extends DocumentFilter {
private int decimalPrecision = 2;
private boolean allowNegative = false;
public NumberFilter() {
}
public NumberFilter(int decimals, boolean negatives) {
decimalPrecision = decimals;
allowNegative = negatives;
}
protected boolean accept(FilterBypass fb, int offset, String str) throws BadLocationException {
boolean accept = true;
int length = fb.getDocument().getLength();
String currentText = fb.getDocument().getText(0, length);
if (str != null) {
if (!isNumeric(str) && !str.equals(".") && !str.equals("-")) { //First, is it a valid character?
Toolkit.getDefaultToolkit().beep();
accept = false;
} else if (str.equals(".") && currentText.contains(".")) { //Next, can we place a decimal here?
Toolkit.getDefaultToolkit().beep();
accept = false;
} else if (isNumeric(str)
&& currentText.indexOf(",") != -1
&& offset > currentText.indexOf(",")
&& length - currentText.indexOf(".") > decimalPrecision
&& decimalPrecision > 0) { //Next, do we get past the decimal precision limit?
Toolkit.getDefaultToolkit().beep();
accept = false;
} else if (str.equals("-") && (offset != 0 || !allowNegative)) { //Next, can we put a negative sign?
Toolkit.getDefaultToolkit().beep();
accept = false;
}
}
return accept;
}
@Override
public void insertString(FilterBypass fb, int offset, String str, AttributeSet as) throws BadLocationException {
if (accept(fb, offset, str)) {
super.insertString(fb, offset, str, as);
}
}
@Override
public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
if (accept(fb, offset, text)) {
super.replace(fb, offset, length, text, attrs);
}
}
public boolean isNumeric(String str) {
try {
int x = Integer.parseInt(str);
System.out.println(x);
return true;
} catch (NumberFormatException nFE) {
System.out.println("Not an Integer");
return false;
}
}
}
public class SizeFilter extends DocumentFilter {
private int maxCharacters;
public SizeFilter(int maxChars) {
maxCharacters = maxChars;
}
public void insertString(FilterBypass fb, int offs, String str, AttributeSet a)
throws BadLocationException {
if ((fb.getDocument().getLength() + str.length()) <= maxCharacters) {
super.insertString(fb, offs, str, a);
} else {
Toolkit.getDefaultToolkit().beep();
}
}
public void replace(FilterBypass fb, int offs, int length, String str, AttributeSet a)
throws BadLocationException {
if ((fb.getDocument().getLength() + str.length()
- length) <= maxCharacters) {
super.replace(fb, offs, length, str, a);
} else {
Toolkit.getDefaultToolkit().beep();
}
}
}
Опять же, я знаю, что он компилируется, но я не проверял его (в частности, числовой фильтр), но это было бы хорошим упражнением в отладке;)
JTextComponent txt = new JFormattedTextField( new LimitedIntegerFormatter(limit) );
txt.addPropertyChangeListener("value", yourPropertyChangeListener);
import javax.swing.text.DefaultFormatter;
import java.text.ParseException;
public class LimitedIntegerFormatter extends DefaultFormatter {
static final long serialVersionUID = 1l;
private int limit;
public LimitedIntegerFormatter( int limit ) {
this.limit = limit;
setValueClass(Integer.class);
setAllowsInvalid(false);
setCommitsOnValidEdit(true);
}
@Override
public Object stringToValue(String string) throws ParseException {
if (string.equals("")) return null;
if (string.length() > limit) throw new ParseException(string, limit);
return super.stringToValue(string);
}
}
yourPropertyChangeListener будет вызываться с
new PropertyChangeEvent ("value", Integer oldValue, Integer newValue)
(oldValue или newValue будут нулевыми в текстовом случае "")
после каждого действительного редактирования
public static boolean validateInt(String txt) {
String regx = "^[(0-9),;]+$";
Pattern pattern = Pattern.compile(regx, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(txt);
boolean b = matcher.find();
return b;
}
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JTextField;
public class Validation {
public static void validateInt(final JTextField txt){
txt.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
char c = e.getKeyChar();
if ( ((c < '0') || (c > '9'))
&& (c != KeyEvent.VK_BACK_SPACE)) {
e.consume(); // ignore event
}
}
});
}
public static void validatelength(final JTextField txt,final int size){
txt.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
String text = txt.getText();
int length = text.length();
if (length == size) {
e.consume();// ignore event
}
}
});
}
}
В настоящее время я работаю над небольшим проектом с помощью решения для судоку. Я ограничил свои входные данные только числами, проверив, входные данные были в строковом массиве с числами. Я написал это прямо в JTextFieldLimit.
class JTextFieldLimit extends PlainDocument {
private int limit;
//Added the following 2 lines
String[] numbers = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
boolean isAccepted = false;
//
JTextFieldLimit(int limit) {
super();
this.limit = limit;
}
public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException {
if (str == null)
return;
//And the following 2 lines
for (String thisnumber : numbers) {
isAccepted = str.equals(thisnumber);
if (isAccepted) {
//
if ((getLength() + str.length()) <= limit) {
super.insertString(offset, str, attr);
}
}
}
}
}
import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.DocumentFilter;
import javax.swing.text.JTextComponent;
import org.apache.commons.lang.StringUtils;
public class NumberTextField extends JTextField {
protected int maxlength = 0;
public NumberTextField() {
this(10);
}
public NumberTextField(int length) {
super();
this.maxlength = length;
initializeForNumbers();
}
public void setMaxLength(int length) {
this.maxlength = length;
}
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_ESCAPE) {
return false;
}
return super.processKeyBinding(ks, e, condition, pressed);
}
private void initializeForNumbers() {
Document document = getDocument();
if (document != null) {
((AbstractDocument) document).setDocumentFilter(new DocumentHandler());
}
}
private class DocumentHandler extends DocumentFilter {
public void insertString(DocumentFilter.FilterBypass fb,
int offset, String string, AttributeSet attr)
throws BadLocationException {
if (string == null) {
return;
} else {
replace(fb, offset, 0, string, attr);
}
}
public void remove(DocumentFilter.FilterBypass fb,
int offset, int length)
throws BadLocationException {
replace(fb, offset, length, "", null);
}
public void replace(final DocumentFilter.FilterBypass fb,
int offset, int length, String text, AttributeSet attrs)
throws BadLocationException {
Document doc = fb.getDocument();
int currentLength = doc.getLength();
String currentContent = doc.getText(0, currentLength);
String before = currentContent.substring(0, offset);
String after = currentContent.substring(length + offset, currentLength);
String newValue = before + (text == null ? "" : text) + after;
if (newValue.length() > maxlength) {
return;
} else {
checkInput(newValue, offset);
fb.replace(offset, length, text, attrs);
if (doc.getLength() >= maxlength) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
if (c != null && c instanceof JTextComponent) {
JTextComponent component = (JTextComponent) c;
Document compDoc = component.getDocument();
if (compDoc.equals(fb.getDocument())) {
KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent();
}
}
}
});
return;
}
}
}
private void checkInput(String proposedValue, int offset)
throws BadLocationException {
if (proposedValue.length() > 0 && !StringUtils.isNumeric(proposedValue)) {
throw new BadLocationException(
proposedValue, offset);
}
}
}
}
yourJTextField.addKeyListener(new KeyAdapter()
{
@Override
public void keyTyped(KeyEvent e)
{
// Here limiting the character of your number. for examlpe this wil only accept one digit
if (yourJTextField.getText().length() == 1) {
e.consume();
}
// Here limiting your input to only number
char c = e.getKeyChar();
if(!((c >= '0') && (c <= '7') || (c == KeyEvent.VK_BACK_SPACE) || (c == KeyEvent.VK_DELETE)))
{
//do what so ever you want
}
else
{
//do what so ever you want
}
}
})