I l@ve RuBoard |
![]() ![]() |
20.3 The DefaultFormatter ClassDefaultFormatter is a concrete implementation of AbstractFormatter that provides enough functionality for many purposes. It can be used for classes with a constructor that takes a single String. To support other classes, you may have to override DefaultFormatter's stringToValue( ) method and its valueToString( ) method if the class's toString( ) method is not adequate. DefaultFormatter maintains the field's editValid property, checking to see if the current edit is valid after every user keystroke and calling setEditValid( ) as appropriate. 20.3.1 PropertiesTable 20-3 shows the properties defined by DefaultFormatter.
The allowsInvalid property controls whether the field's content may be temporarily invalid during an edit. Consider an integer field with a current value of 25 that the user wants to change to 19. The user may decide to do this by pressing the Backspace key twice, then the 1 key and the 9 key. After the first backspace, the content of the field is 2. After the second backspace, the content of the field is the empty string, but if allowsInvalid were false, this would not be allowed (since the empty string is not a valid integer), and the field would refuse to allow the 2 to be deleted. Setting this property to false can be effective in certain cases but should be done only after careful consideration. commitsOnValidEdit controls how often a field's value is set during an edit. If this property is true, then the field attempts to commit its content after every keystroke. The default value is false, which means the field does not commit until something special happens, such as when the field loses focus or the user presses the Enter key. Consider again the situation from the previous paragraph. After the first backspace, the field shows 2. If commitsOnValidEdit is false, nothing is committed, and the field's value remains 25. If the user later cancels the edit (using the Escape key, for example), the content of the field reverts to 25. If commitsOnValidEdit is true, the 2 is committed immediately, and the field's value becomes 2. When the overwriteMode property is true, characters entered by the user (and even pastes from the clipboard) replace characters in the field. When it is false (insert mode), new characters are inserted without deleting any of the existing characters. The default value is true, which is often not what you want. Subclasses of DefaultFormatter often call setOverwriteMode(false) in their constructors. The valueClass property determines the type of object that the stringToValue( ) method attempts to return. It is usually fine for this property to remain null, in which case stringToValue( ) attempts to return an object of the same type[6] as getFormattedTextField.getValue( ). (If you override the stringToValue( ) method, the valueClass property is ignored unless you handle it manually or invoke super.stringToValue( ).)
20.3.2 Constructor
20.3.3 Public Methods
20.3.4 ExampleHere, we extend DefaultFormatter to create a formatter that can edit combinations such as those used by combination locks. The String representation of a combination is something like 15-45-22 or 35-30-11-19. The Object representation is an int[] array.[7]
We override stringToValue( ) and valueToString( ) to convert between these representations, and we override getActions( ) so that the number under the caret can be incremented or decremented from the keyboard. We also provide a sample main( ) method that shows how CombinationFormatter could be used. (See Figure 20-3.) // CombinationFormatter.java // import javax.swing.*; import javax.swing.text.*; public class CombinationFormatter extends DefaultFormatter { public CombinationFormatter( ) { setOverwriteMode(false); } public Object stringToValue(String string) throws java.text.ParseException { // Input: string of form "15-45-22" (any number of hyphen-delimited numbers) // Output: int array String s[] = string.split("-"); int a[] = new int[s.length]; for (int j=0; j<a.length; j+=1) try { a[j] = Integer.parseInt(s[j]); } catch (NumberFormatException nfe) { throw new java.text.ParseException(s[j] + " is not an int", 0); } return a; } public String valueToString(Object value) throws java.text.ParseException { // Input: int array // Output: string of numerals separated by hyphens if (value == null) return null; if (! (value instanceof int[])) throw new java.text.ParseException("expected int[]", 0); int a[] = (int[])value; StringBuffer sb = new StringBuffer( ); for (int j=0; j < a.length; j+=1) { if (j > 0) sb.append('-'); sb.append(a[j]); } return sb.toString( ); } protected Action[] getActions( ) { Action[] actions = { new CombinationIncrementer("increment", 1), new CombinationIncrementer("decrement", -1) }; return actions; } // Begin inner class ---------------------------------------- public class CombinationIncrementer extends AbstractAction { protected int delta; public CombinationIncrementer(String name, int delta) { // Constructor super(name); // 'name' must match something in the component's InputMap or else // this Action is not invoked automatically. Valid names include // "reset-field-edit", "increment", "decrement", and "unselect" // (see Appendix B). this.delta = delta; } public void actionPerformed(java.awt.event.ActionEvent ae) { JFormattedTextField ftf = getFormattedTextField( ); // From AbstractFormatter if (ftf == null) return; String text = ftf.getText( ); if (text == null) return; int pos = ftf.getCaretPosition( ); int hyphenCount = 0; for (int j=0; j < pos; j+=1) // How many hyphens precede the caret? if (text.charAt(j) == '-') hyphenCount += 1; try { int a[] = (int[])stringToValue(text); a[hyphenCount] += delta; // Change the number at caret position. if (a[hyphenCount] < 0) a[hyphenCount] = 0; String newText = valueToString(a); ftf.setText(newText); // Does not retain caret position if ((text.charAt(pos) == '-') && (newText.length( ) < text.length( )) ) pos -= 1; // Don't let caret move past - when 10 changes to 9. ftf.setCaretPosition(pos); } catch (Exception e) { return; } } } // End inner class ---------------------------------------- public static void main(String argv[]) { // A demo main( ) method to show how CombinationFormatter could be used int comb1[] = { 35, 11, 19 }; int comb2[] = { 10, 20, 30 }; final JFormattedTextField field1 = new JFormattedTextField(new CombinationFormatter( )); field1.setValue(comb1); final JFormattedTextField field2 = new JFormattedTextField(new CombinationFormatter( )); field2.setValue(comb2); JPanel pan = new JPanel( ); pan.add(new JLabel("Change the combination from")); pan.add(field1); pan.add(new JLabel("to")); pan.add(field2); JButton b = new JButton("Submit"); b.addActionListener(new java.awt.event.ActionListener( ) { public void actionPerformed(java.awt.event.ActionEvent ae) { try { field1.commitEdit( ); // Make sure current edit (if any) is committed. field2.commitEdit( ); } catch (java.text.ParseException pe) { } int oldc[] = (int[])field1.getValue( ); int newc[] = (int[])field2.getValue( ); // // Code to validate oldc[] and change to newc[] goes here. // } }); pan.add(b); JFrame f = new JFrame("CombinationFormatter Demo"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setContentPane(pan); f.setSize(360, 100); f.setVisible(true); } } Figure 20-3. JFormattedTextFields using CombinationFormatters![]() Some combination locks require each combination to have exactly three numbers and require each number to be no higher than 59. CombinationFormatter doesn't enforce either of these restrictions, but it wouldn't be hard to incorporate them. |
I l@ve RuBoard |
![]() ![]() |