2011-12-02 3 views
1

J'ai un programme Swing qui effectue une recherche en fonction du contenu de certains champs de texte et des paramètres d'une paire de boutons radio (dans un groupe de boutons). Le programme recherche automatiquement lorsque certains champs de texte perdent leur focus. Le problème survient lorsque l'événement de perte de focus est déclenché par un clic sur l'un des boutons radio. L'événement de focus perdu sur le champ de texte est traité avant que les valeurs du bouton radio isSelected() aient changé, donc la recherche est effectuée avec les mauvais paramètres (ie vieux), au lieu des paramètres basés sur le nouveau réglage de la radio boutons.Comment attendre que le bouton radio soit sélectionné en événement de focus perdu

J'ai essayé d'invoquer la recherche en utilisant ma propre méthode invokeWhenIdle (montrée ci-dessous) pour lancer la recherche après que la file d'attente d'événements se soit stabilisée, mais elle utilise toujours l'ancien réglage des boutons radio.

Ma seule solution de travail est de retarder pendant 250 millisecondes dans l'événement focus perdu avant d'exécuter la recherche, de sorte que les boutons radio ont le temps de changer. Cela fonctionne, mais cela rend l'interface utilisateur léthargique.

De meilleures idées?

public static void invokeWhenIdle(final int a_max_retry, final Runnable a_runnable) { 
    if (a_max_retry <= 0) { 
    throw new IllegalStateException("invokeWhenIdle: Could not run " + a_runnable); 
    } 

    // get the next event on the queue 
    EventQueue l_queue = Toolkit.getDefaultToolkit().getSystemEventQueue(); 
    AWTEvent l_evt = l_queue.peekEvent(); 
    if (l_evt == null) { 
    // nothing left on the queue (but us), we can do it 
    SwingUtilities.invokeLater(a_runnable); 
    } else { 
    // still something in the queue, try again 
    SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
     invokeWhenIdle(a_max_retry - 1, a_runnable); 
     } 
    }); 
    } 
} 
+0

Eh bien, il n'y a pas de véritable moyen de savoir quand vous devriez lancer la recherche ou quand vous devriez attendre parce que l'utilisateur peut cliquer sur un bouton radio.Vous devrez soit utiliser une minuterie pour lancer la recherche après un certain laps de temps depuis la dernière frappe de l'utilisateur, soit ne pas le faire et laisser l'utilisateur démarrer la recherche en cliquant sur le bouton «Rechercher». Vous ne devriez pas avoir besoin de jouer avec 'EventQueue' comme ceci - jetez un coup d'œil à [How to Use Swing Timers] (http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html). Lorsque vous détectez une frappe, redémarrez le minuteur –

+0

Ma solution de contournement pour cela était de rendre les boutons radio non focalisables (setFocusable (false)) afin qu'ils ne provoquent pas l'événement de focus perdu sur les champs de texte. Toujours curieux s'il y a une solution dans l'autre sens. – Mitch

+0

Vous n'avez pas besoin de faire cela non plus, vous pouvez utiliser une minuterie connectée aux frappes sur le champ de recherche. –

Répondre

1

Pas une réponse, mais une explication de ce qui se passe. Peut-être que ça va déclencher une idée ...

Le problème est qu'un mousePressed arme le modèle de bouton et que mouseReleased change réellement la valeur sélectionnée du modèle.

Lorsque vous exécutez le code FocusListener le bouton radio le modèle est dans un état indéfini. Même si vous ajoutez le code FocusListener à la fin de l'EDT en utilisant invokeLater, le code sera toujours exécuté avant que l'événement mouseReleased ne soit généré.

Voici comment vous pouvez coder l'écouteur pour gérer cela. Il suppose que l'état du bouton est sur le point de changer:

import java.awt.*; 
import java.awt.event.*; 
import javax.swing.*; 

public class FocusSSCCE extends JPanel 
{ 
    public FocusSSCCE() 
    { 
     final JRadioButton radio = new JRadioButton("Radio"); 
     add(radio); 
     radio.setMnemonic('R'); 

     JTextField textField = new JTextField(10); 
     add(textField); 

     JButton button = new JButton("Button"); 
     add(button); 

     textField.addFocusListener(new FocusAdapter() 
     { 
      public void focusLost(FocusEvent e) 
      { 
       boolean isSelected = radio.isSelected(); 

       // Assumes selected state will change 

       if (radio.getModel().isArmed()) 
        isSelected = !isSelected; 

       System.out.println(isSelected); 
      } 
     }); 
    } 

    private static void createAndShowUI() 
    { 
     JFrame frame = new JFrame("FocusSSCCE"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.add(new FocusSSCCE()); 
     frame.pack(); 
     frame.setLocationRelativeTo(null); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) 
    { 
     EventQueue.invokeLater(new Runnable() 
     { 
      public void run() 
      { 
       createAndShowUI(); 
      } 
     }); 
    } 
} 

Cependant, même cette approche ne peut pas être garantie pour fonctionner. Si, pour une raison quelconque, l'utilisateur génère l'événement mousePressed sur le bouton radio et les éloigne de la souris du bouton radio avant de relâcher la souris, l'état sélectionné du bouton radio n'est pas modifié. Similairement, même votre mise en œuvre originale pour dormir pendant 250ms ne peut pas être garantie de fonctionner parce que l'utilisateur pourrait théoriquement maintenir la souris pendant plus de 250ms, ce qui générerait également la mauvaise valeur.

Ma solution pour ce faire était les boutons radio non focalisable

Je ne peux pas penser à une meilleure approche.

Edit:

Je viens de penser à une solution sauvage. Fondamentalement, votre code de traitement ne s'exécutera pas tant que la souris n'aura pas été relâchée lorsque vous cliquerez sur le bouton radio.

+0

C'est une bonne information. Je n'ai jamais entendu parler d'armer avant. – Mitch

+0

@Mitch, voir mon edit pour une solution étrange :) – camickr

+0

merci pour l'idée brutte-force, puisque j'allais implémenter Timer directement, votre hack fonctionne vraiment, mais en déséquilibrant par Timer à travers l'instance JVM entière et actuelle +1 – mKorbel

Questions connexes