2012-08-01 11 views
5

Tengo problemas con las máscaras en un JFormattedTextFieldproblemas con la supresión de las máscaras en un JFormattedTextField

entiendo que reemplaza los caracteres no válidos con un espacio, o lo que se define a través de setPlaceholderCharacter, pero lo que se necesita do permite la eliminación o el retroceso, y NO inserta un espacio en lugar del carácter que eliminé , siempre que el resto de la cadena esté permitida en la máscara.

Por ejemplo, con la máscara: *#*****, la cadena "12 abc" es válida.
Si coloca el cursor entre los caracteres byc y presiona el botón de retroceso, necesito que elimine el b, lo que da como resultado "12 ac". En cambio, lo elimina y agrega un espacio, convirtiéndose en: "12 a c".

A continuación se muestra un ejemplo de código simple para demostrar.

Agradeceré cualquier idea o ejemplo para evitar este problema.


public class testFrame extends javax.swing.JFrame { 

    public testFrame() { 

     setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); 
     getContentPane().setLayout(new java.awt.FlowLayout()); 

     setMinimumSize(new Dimension(300,150)); 

     java.awt.Button closeButton = new java.awt.Button(); 
     JFormattedTextField maskTextField = new JFormattedTextField(); 
     maskTextField.setMinimumSize(new Dimension(100,30)); 

     getContentPane().add(maskTextField); 

     closeButton.setLabel("close"); 
     closeButton.addActionListener(new java.awt.event.ActionListener() { 
      public void actionPerformed(java.awt.event.ActionEvent evt) { 
       System.exit(0); 
      } 
     }); 

     getContentPane().add(closeButton); 

     try { 
      MaskFormatter someMask = new MaskFormatter("*#****"); 
      DefaultFormatterFactory formatterFactory 
       = new DefaultFormatterFactory(someMask); 
      maskTextField.setFormatterFactory(formatterFactory); 
     } catch (ParseException ex) { 
      ex.printStackTrace(); 
     } 
     maskTextField.setText("12 abc"); 

     pack(); 

    } 

    public static void main(String args[]) { 
     java.awt.EventQueue.invokeLater(new Runnable() { 

      public void run() { 
       new testFrame().setVisible(true); 
      } 
     }); 
    } 
} 

código de actualización para reflejar respuesta a continuación. Agregué un segundo campo para que pueda ver el comportamiento con y sin la solución. También una solución menor, cambié el tamaño de las ventanas y lo centré en la pantalla para hacerlo más amigable.

testFrame clase pública se extiende javax.swing.JFrame {

public testFrame() { 
    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); 
    setMinimumSize(new java.awt.Dimension(300, 200)); 
    getContentPane().setLayout(new java.awt.FlowLayout()); 


    JFormattedTextField maskTextField = new JFormattedTextField(); 
    maskTextField.setMinimumSize(new Dimension(100,30)); 
    getContentPane().add(maskTextField); 


    JFormattedTextField maskTextField2 = new JFormattedTextField(); 
    maskTextField2.setMinimumSize(new Dimension(100,30)); 
    getContentPane().add(maskTextField2); 

    java.awt.Button closeButton = new java.awt.Button(); 
    closeButton.setLabel("close"); 
    closeButton.addActionListener(new java.awt.event.ActionListener() { 

     public void actionPerformed(java.awt.event.ActionEvent evt) { 
      System.exit(0); 
     } 
    }); 

    getContentPane().add(closeButton); 

    try { 

     MaskFormatter someMask = new MaskFormatter("*#****"); 
     DefaultFormatterFactory formatterFactory = 
      new DefaultFormatterFactory(someMask); 
     maskTextField.setFormatterFactory(formatterFactory); 

     MaskFormatter someMask2 = new MaskFormatter("*#****"); 
     DefaultFormatterFactory formatterFactory2 = 
      new DefaultFormatterFactory(someMask2); 
     maskTextField2.setFormatterFactory(formatterFactory2); 

    } catch (ParseException ex) { 
     ex.printStackTrace(); 
    } 

    maskTextField.setText("12 abc"); 
    maskTextField2.setText("12 abc"); 

    // added per suggestion below 
    if (maskTextField.getFormatter() instanceof DefaultFormatter) { 
     DefaultFormatter f = (DefaultFormatter) maskTextField.getFormatter(); 
     f.setAllowsInvalid(true); 

     // options are: 
     // JFormattedTextField.COMMIT 
     // JFormattedTextField.COMMIT_OR_REVERT --> default 
     // JFormattedTextField.REVERT 
     // JFormattedTextField.PERSIST 
     maskTextField.setFocusLostBehavior(JFormattedTextField.PERSIST); 
    } 
    pack(); 
    this.setLocationRelativeTo(null); 

} 

public static void main(String args[]) { 
    java.awt.EventQueue.invokeLater(new Runnable() { 

     public void run() { 
      new testFrame().setVisible(true); 
     } 
    }); 
} 

}

+0

suena como un pequeño error: una máscara es algo con un lenght_ _fixed, cada posición de llenado, ya sea por un carácter válido o una placeHolder. Al eliminar un char, se reemplaza por placeHolder, en su ejemplo es el predeterminado, que es un espacio. Entonces el primer espacio es diferente del segundo, semánticamente :-) Cambie el lugar al otro, para ver la diferencia.Como su requisito parece ser algo así como cualquier-char-up-to-a-max-of-4-after-a-digit, tendrá que implementar un formateador personalizado ya que @DuncanJones ya sugirió – kleopatra

+0

No me importa siendo de longitud fija, y supongo que solo tengo un caso de uso especial que no atendieron. Por ejemplo, si su máscara era (###) ### - ### e intentaba escribir (123) 456-7890 y había escrito accidentalmente dos 2, es decir, (122) 345-6789, la máscara estándar comportamiento parece significar que si elimina los primeros 2, sustituiría un espacio (marcador de posición) que le da (12) 345-6789. Necesito que elimine el carácter y agregue el marcador de posición al final de la cadena. Pero supongo que esto no es algo que otros necesitan, así que tendré que hacer un formateador personalizado :( –

+0

ahh ... ya veo, gracias por la aclaración – kleopatra

Respuesta

5

En primer lugar, gracias por publicar un ejemplo de trabajo decente.

Parece que el DefaultFormatter es el formateador utilizado por su campo de texto enmascarado. Me di cuenta que podía permitir que las modificaciones no válidos temporales de la siguiente manera:

if (maskTextField.getFormatter() instanceof DefaultFormatter) { 
    DefaultFormatter f = (DefaultFormatter) maskTextField.getFormatter(); 
    f.setAllowsInvalid(true);   
} 

Esperamos que esto lo suficiente de un puntero para que pueda empezar. Aunque tenga en cuenta que esta solución rápida tiene el interesante comportamiento de borrar por completo el contenido del campo de texto si cambia el foco mientras que hay un valor no válido en el campo. Esto parece contrario al JavaDoc para JFormattedTextField, que sugiere que el comportamiento predeterminado es COMMIT_OR_REVERT.

+0

Creo que esto es definitivamente una gran pista. Solo una cosa para agregar, si pones en maskTextField.setFocusLostBehavior (JFormattedTextField.PERSIST); parece que no aclara los resultados. Gracias! –

+0

probando un poco más, me doy cuenta de que esto no soluciona realmente el problema. Agregar setAllowsInvalid (true) lo hace ignorar la máscara, que no es lo que quiero. Lo que estoy esperando es SI la acción de borrar no invalida la máscara (cambiando el carácter que está a la derecha de los caracteres eliminados) y luego eliminar o retroceder debería hacer eso, y no agregar una espacio. Supongo que si la acción de borrar invalida la máscara, entonces no debería funcionar. –

+1

Si la máscara tiene una longitud fija (presumiblemente la mayoría/todos son?), entonces no sería una eliminación * siempre * invalidaría ¿la máscara? O permites temporalmente que se rompan las reglas de la máscara, o siempre terminarás con este extraño comportamiento espacial. ¿Quizás podrías buscar escribir tu propia subclase de 'AbstractFormatter'? –

1

Solo una idea, definitivamente como, no es adecuada para la producción y probablemente no sea posible en el caso general: podría intentar envolver el documento por defecto e invocar controles/manipulaciones personalizados antes/después de llamar al delegado.

He aquí un fragmento que parece funcionar para el ejemplo particular en su pregunta:

public static class MyMaskFormatter extends MaskFormatter { 

    DocumentFilter filter; 

    /** 
    * @param string 
    * @throws ParseException 
    */ 
    public MyMaskFormatter(String string) throws ParseException { 
     super(string); 
    } 

    @Override 
    protected DocumentFilter getDocumentFilter() { 
     if (filter == null) { 
      filter = new MyDocumentFilter(super.getDocumentFilter()); 
     } 
     return filter; 
    } 

    public class MyDocumentFilter extends DocumentFilter { 

     DocumentFilter delegate; 

     MyDocumentFilter(DocumentFilter delegate) { 
      this.delegate = delegate; 
     } 

     @Override 
     public void remove(FilterBypass fb, int offset, int length) 
       throws BadLocationException { 
      String toRemove = fb.getDocument().getText(offset, length); 
      delegate.remove(fb, offset, length); 
      String replaced = fb.getDocument().getText(offset, length); 
      if (replaced.charAt(0) == getPlaceholderCharacter() && 
       toRemove.charAt(0) != getPlaceholderCharacter() ) { 
       int sublength = fb.getDocument().getLength() - offset; 
       String text = fb.getDocument().getText(offset, sublength); 
       text = text.substring(1) + text.charAt(0); 
       replace(fb, offset, sublength, text, null); 
       getFormattedTextField().setCaretPosition(offset); 
       //getNavigationFilter().setDot(fb, offset, null); 
      } 
     } 

     @Override 
     public void insertString(FilterBypass fb, int offset, 
       String string, AttributeSet attr) 
       throws BadLocationException { 
      delegate.insertString(fb, offset, string, attr); 
     } 

     @Override 
     public void replace(FilterBypass fb, int offset, int length, 
       String text, AttributeSet attrs) 
       throws BadLocationException { 
      delegate.replace(fb, offset, length, text, attrs); 
     } 

    } 

} 
Cuestiones relacionadas