olá pessoal,
Eu tinha pensado em usar listener, mas tbém achei que nao seria a solução mais adequada, visto que eu teria que tratar vários eventos, porém agradeço a atenção.
Eu estava usando Document também… com ele eu consigo bloquear a inserção de chars no meio do campo, mas ainda sim dá pra posicionar o cursor lá.
Depois de muito pesquisar, vi que existe o NavigationFilter que serve exatamente pra isso: controlar a movimentação do cursor.
http://download.oracle.com/javase/1.5.0/docs/api/javax/swing/text/NavigationFilter.html
Segue abaixo a minha solução.
JMonetaryField.java:
package javax.swing.extras;
import javax.swing.JTextField;
import javax.swing.text.NavigationFilter;
import javax.swing.text.Position;
public class JMonetaryField extends JTextField {
public JMonetaryField() {
this("");
}
public JMonetaryField(String text) {
super(text);
setNavigationFilter(new MyNavigationFilter());
setDocument(new MonetaryDocument());
}
@Override
public final void setNavigationFilter(NavigationFilter filter) {
if (filter == null || !filter.getClass().isAssignableFrom(MyNavigationFilter.class)) {
throw new IllegalArgumentException("The navigation filter must be a instance of "
+ "MyNavigationFilter and cannot be null.");
}
super.setNavigationFilter(filter);
}
protected final class MyNavigationFilter extends NavigationFilter {
@Override
public void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias) {
super.setDot(fb, getText().length(), bias);
}
@Override
public void moveDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias) {
super.moveDot(fb, getText().length(), bias);
}
}
}
MonetaryDocument.java:
package javax.swing.extras;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
public class MonetaryDocument extends PlainDocument {
protected final DecimalFormatSymbols formatSymbols;
public MonetaryDocument() {
this(Locale.getDefault());
}
public MonetaryDocument(Locale locale) {
super();
formatSymbols = DecimalFormatSymbols.getInstance(locale);
}
@Override
public void insertString(final int offset, final String str, final AttributeSet a) throws BadLocationException {
if (str == null) {
return;
}
final String toInsert = cleanString(str);
if (toInsert.isEmpty()) {
return;
}
final String oldValue = getText();
int centIndex = oldValue.indexOf(formatSymbols.getDecimalSeparator());
if (offset > centIndex) {
remove(0, getLength());
final String beforeCur = cleanString(oldValue.substring(0, offset));
final String afterCur = (oldValue.substring(offset));
StringBuffer strBuf = new StringBuffer(beforeCur).append(toInsert).append(afterCur);
if (strBuf.length() < 3) {
strBuf.insert(0, formatSymbols.getDecimalSeparator());
} else {
strBuf.insert(strBuf.length() - 2, formatSymbols.getDecimalSeparator());
}
super.insertString(0, strBuf.toString(), a);
} else {
super.insertString(offset, str, a);
}
}
@Override
public void remove(final int offset,
final int length) throws BadLocationException {
String oldValue = getText();
int centIndex = oldValue.indexOf(formatSymbols.getDecimalSeparator());
if (offset == centIndex && length == 1) {
// nao deixa remover o separador.
return;
} else if (offset + length > centIndex) {
super.remove(offset, length);
String texto = cleanString(getText());
super.remove(0, getLength());
insertString(0, texto, null);
} else {
super.remove(offset, length);
}
}
private String getText() throws BadLocationException {
return getText(0, getLength());
}
private String cleanString(String s) {
return s.replaceAll("[^0-9]", "");
}
}
PS: Na real, o que eu queria fazer era um TextField para valores monetários, para que a pessoa só digite os dígitos e o próprio campo coloque o separador de centavos automatcamente.
Ja vi vários exemplos no próprio GUJ e em outros locais, mas nenhum funcionava 100%. Geralmente qdo se insere ou remove chars ele distorce a posição do cursor, por causa dos chars removidos/adicionados programaticamente para casar com o formato.
Desse modo, resolvi fazer um TextField que só aceite o cursor no fim do mesmo.
abraços.