Hello, guys
I'm trying to create a text field where I could enter any valid
digits for percents (let's say -900 .. 900) and have it displayed
with '%' sign, when the field looses focus or ENTER is pressed.
I am using NumberFormat.getPercentInstance() to get formatter and
it displays the initial value fine (with '%' sign). I limit my
input to digits with PercentInputFilter. The problem is, when I
delete the current contents of the field, enter my new value
(let's say 50) and click the button the value is not accepted
and reverted to the previous one. As far as I understand, with
getPercentInstance() it requires '%' char to be present. But
what I'm trying to achieve is to enter digits and have '%' to
be automatically displayed (if it's not there yet).
I am browsing doc on JFormattedText field, but could come up
with a good solution to meed my above mentioned needs.
Any suggestions, help, sample code are appreciated.
See sample code below. You can actually compile and run it
to see the problem.
Thanks
-Dmitri
<sample_code>
import javax.swing.JFormattedTextField;
import java.text.NumberFormat;
import javax.swing.text.DocumentFilter;
import javax.swing.text.BadLocationException;
import javax.swing.text.AttributeSet;
import javax.swing.text.DefaultFormatterFactory;
import javax.swing.text.NumberFormatter;
public class SpeedField extends JFormattedTextField {
public static final int DEFAULT_COLUMNS = 11;
public static final double DEFAULT_VALUE = 1.00; // 100%
public SpeedField() {
//super(NumberFormat.getPercentInstance());
super();
setColumns(DEFAULT_COLUMNS);
setHorizontalAlignment(javax.swing.JTextField.RIGHT);
NumberFormatter formatter =
new NumberFormatter (NumberFormat.getPercentInstance()) {
protected DocumentFilter getDocumentFilter() {
return filter;
}
private DocumentFilter filter = new PercentInputFilter();
};
DefaultFormatterFactory dff = new DefaultFormatterFactory();
dff.setDefaultFormatter(formatter);
dff.setDisplayFormatter(formatter);
dff.setEditFormatter(formatter);
setFormatterFactory(dff);
setValue(new Double(DEFAULT_VALUE));
}
protected int getColumnWidth() {
return getFontMetrics(getFont()).charWidth('0');
}
private class PercentInputFilter extends DocumentFilter {
public void insertString(FilterBypass fb,
int offset,
String string,
AttributeSet attr) throws BadLocationException {
StringBuffer buffer = new StringBuffer(string);
for (int i = buffer.length() - 1; i >= 0; i--) {
char ch = buffer.charAt(i);
if (!Character.isDigit(ch) && ch != '-')
buffer.deleteCharAt(i);
}
super.insertString(fb, offset, buffer.toStri343ng(), attr);
}
public void replace(FilterBypass fb,
int offset,
int length,
String string,
AttributeSet attr) throws BadLocationException {
if (string != null) {
StringBuffer buffer = new StringBuffer(string);
for (int i = buffer.length() - 1; i >= 0; i--) {
char ch = buffer.charAt(i);
if (!Character.isDigit(ch) && ch != '-')
buffer.deleteCharAt(i);
}
string = buffer.toString();
}
super.replace(fb, offset, length, string, attr);
}
} // class PercentInputField
/** -----------------------------------------------------------------------
* TEST UNIT
*/
public static void main(String[] argv) {
javax.swing.JFrame frame = new javax.swing.JFrame("SpeedField Test");
frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
final SpeedField speed = new SpeedField();
javax.swing.JPanel p = new javax.swing.JPanel();
p.add(speed);
javax.swing.JButton btValue = new javax.swing.JButton("Value");
btValue.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) {
System.err.println("value = " + speed.getValue());
}
});
p.add(btValue);
frame.getContentPane().add(p, java.awt.BorderLayout.NORTH);
frame.pack();
frame.setSize(new java.awt.Dimension(200, 80));
frame.setLocation(100,100);
frame.setVisible(true);
}
} // class SpeedField
</sample_code
Andrew Thompson - 22 Nov 2004 19:38 GMT
I'm not so experienced with JFormattedTextField to solve your
problem, though I played with your code out of interest.
> See sample code below. You can actually compile and run it
> to see the problem.
Good example! Except for one (late I assume) typo.
> <sample_code>
....
> for (int i = buffer.length() - 1; i >= 0; i--) {
> char ch = buffer.charAt(i);
> if (!Character.isDigit(ch) && ch != '-')
> buffer.deleteCharAt(i);
> }
> super.insertString(fb, offset, buffer.toStri343ng(), attr);
....
Where did the '343' come from?
BTW - Please do not x-post so widely, I recommend you set the
Follow-Ups to c.l.j.gui *only* (as I have done).
The people that can answer this are on c.l.j.gui, if anywhere.

Signature
Andrew Thompson
http://www.PhySci.org/codes/ Web & IT Help
http://www.PhySci.org/ Open-source software suite
http://www.1point1C.org/ Science & Technology
http://www.LensEscapes.com/ Images that escape the mundane
Dmitri - 23 Nov 2004 14:42 GMT
> I'm not so experienced with JFormattedTextField to solve your
> problem, though I played with your code out of interest.
[quoted text clipped - 15 lines]
>
> Where did the '343' come from?
Sure, it's a typo... probably my emacs editing window had focus
when I was typing line number for debugging...
Thanks for reply anyway!
Shane Mingins - 29 Nov 2004 03:39 GMT
> Any suggestions, help, sample code are appreciated.
> See sample code below. You can actually compile and run it
> to see the problem.
Did not have time to compile and run, sorry. Here's ours ... have fun ;-)
Cheers
Shane

Signature
"there's a lot of bad code out there, and someone must be writing it" - Ron
Jeffries
public class PercentField extends JFormattedTextField
{
/**
* A formatter to check the format of percent fields.
* It provides additional functionality by adding a DocumentFilter.
*/
private static class PercentFormatter extends NumberFormatter
{
private DocumentFilter documentFilter;
public PercentFormatter(int noDecimals)
{
super(getPercentEditFormat(noDecimals));
}
public DocumentFilter getDocumentFilter()
{
if (documentFilter == null)
{
documentFilter = new BasicDocumentFilter(new char[]{'0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', ',', '.', '+', '-', '%'});
}
return documentFilter;
}
}
/**
* Get the appropriate NumberFormat for editing this component.
*
* @return
*/
private static NumberFormat getPercentEditFormat(int noDecimals)
{
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMinimumFractionDigits(noDecimals);
nf.setMaximumFractionDigits(noDecimals);
return nf;
}
/**
* Create a PercentField that will use a PercentFormatter and a
BasicInputVerifier.
*/
public PercentField(int noDecimals)
{
super(new DefaultFormatterFactory(new
NumberFormatter(NumberFormatUtils.getPercentDisplayFormat(noDecimals)),
new
NumberFormatter(NumberFormatUtils.getPercentDisplayFormat(noDecimals)),
new PercentFormatter(noDecimals)));
super.setInputVerifier(new BasicInputVerifier());
}
// default 'null' constructor for 4 decimal places.
public PercentField()
{
this(4);
}
/**
* Returns value as a BigDecimal
*
* @return the last valid value as a BigDecimal
* @see java.math.BigDecimal
* @see javax.swing.JFormattedTextField
*/
public BigDecimal getBigDecimalValue()
{
// return new BigDecimal(((Number) getValue()).doubleValue());
return ((getValue() == null) ? null : new BigDecimal(((Number)
getValue()).doubleValue()));
}
}