2016-04-16 2 views
3

J'essaye de construire un JToggleButton qui affiche un JDialog (qui contient un JList) quand le JToggleButton est pressé. Et avoir le JDialog disparaître lorsque le JToggleButton est pressé à nouveau, OU si l'utilisateur clique loin ou ailleurs dans le cadre (j'ai simulé cela via un FocusListener sur le lorsque le focus est perdu).gérer des séquences d'état et d'événements de JDialog et JToggleButton

Si vous appuyez sur le bouton dans la séquence, l'affichage JDialog s'affichera correctement.

Cependant, le problème est lorsque le JDialog est visible, et je clique ailleurs sur le cadre, le JDialog disparaît correctement lorsque le focus est perdu. Cependant, l'état du JToggleButton reste incorrectement défini comme sélectionné. Cela signifie que cliquer sur le JToggleButton n'affiche plus le JDialog car l'état du JToggleButton est désynchronisé. Au lieu de cela, je vais devoir appuyer deux fois sur JToggleButton pour afficher de nouveau le JDialog. Mon exemple de code ci-dessous illustre ce problème.

Je n'arrive pas à obtenir le focus perdu du JList pour se synchroniser avec l'état du JToggleButton. Cela semble être un problème simple, mais je suis coincé en essayant de trouver une solution. Quelqu'un peut-il aider? Merci.

Voir mon code ci-dessous:

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.FocusEvent; 
import java.awt.event.FocusListener; 

public class MultiComboBox extends JToggleButton 
{ 
    public MultiComboBox(JFrame frame, String buttonText) 
    { 
     super(buttonText); 

     JDialog dialog = new JDialog(frame, false); 
     dialog.setLayout(new BorderLayout()); 

     Object[] items = new Object[] { "one", "two", "three" }; 
     JList list = new JList(items); 
     list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 

     JScrollPane listScrollPane = new JScrollPane(list, 
      JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 
      JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 
     listScrollPane.setPreferredSize(list.getPreferredSize()); 

     dialog.add(listScrollPane, BorderLayout.CENTER); 
     dialog.pack(); 

     addActionListener(new ActionListener() 
     { 
      @Override 
      public void actionPerformed(ActionEvent e) 
      { 
       final JToggleButton button = (JToggleButton) e.getSource(); 
       System.out.println("button clicked: " + button.isSelected()); 
       if (button.isSelected()) 
       { 
        Point p = button.getLocation(); 
        p.setLocation(p.getX() + 300, p.getY()); 
        SwingUtilities.convertPointToScreen(p, button); 
        dialog.setLocation(p); 
        dialog.setVisible(true); 
       } 
       else 
        dialog.setVisible(false); 
      } 
     }); 

     list.addFocusListener(new FocusListener() 
     { 
      @Override 
      public void focusGained(FocusEvent e) 
      { 
      } 

      @Override 
      public void focusLost(FocusEvent e) 
      { 
       System.out.println("list focusLost, dialog: " + dialog.isVisible()); 
       dialog.setVisible(false); 
      } 
     }); 
    } 

    public static void main(String[] args) 
    { 
     JFrame frame = new JFrame("Test"); 
     frame.setPreferredSize(new Dimension(300, 300)); 
     frame.setLayout(new BorderLayout()); 

     MultiComboBox mcb = new MultiComboBox(frame, "Toggle"); 

     JPanel buttonPanel = new JPanel(new BorderLayout()); 
     buttonPanel.setPreferredSize(new Dimension(80, 30)); 
     buttonPanel.add(mcb, BorderLayout.CENTER); 

     JPanel blankPanel = new JPanel(new BorderLayout()); 
     frame.add(blankPanel, BorderLayout.CENTER); 

     frame.add(buttonPanel, BorderLayout.PAGE_START); 
     frame.pack(); 
     frame.setVisible(true); 
     frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
    } 
} 

Répondre

1

Suggestions:

  • Ne pas ajouter un ActionListener au JToggleButton
  • mais au lieu d'ajouter un ItemListener. Cela répondra aux changements de l'état de sélection de l'interrupteur
  • Dans cet écouteur, changez l'état visible de la boîte de dialogue.
  • Dans votre FocusListener, ne modifiez pas l'état visible de la boîte de dialogue, mais changez plutôt l'état de sélection de la bascule.
  • Utilisez un WindowFocusListener ajouté à JDialog lui-même pour être averti s'il perd le focus. De cette façon, le code d'écoute peut être en dehors du code pour les composants de dialogue, une solution OOPs plus propre.

Par exemple:

import javax.swing.*; 

import java.awt.*; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.FocusEvent; 
import java.awt.event.FocusListener; 
import java.awt.event.ItemEvent; 
import java.awt.event.ItemListener; 

public class MultiComboBox2 extends JToggleButton { 
    public MultiComboBox2(JFrame frame, String buttonText) { 
     super(buttonText); 

     JDialog dialog = new JDialog(frame, false); 
     dialog.setLayout(new BorderLayout()); 

     Object[] items = new Object[] { "one", "two", "three" }; 
     JList list = new JList(items); 
     list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 

     JScrollPane listScrollPane = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 
       JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 
     listScrollPane.setPreferredSize(list.getPreferredSize()); 

     dialog.add(listScrollPane, BorderLayout.CENTER); 
     dialog.pack(); 

     addItemListener(new ItemListener() { 

      @Override 
      public void itemStateChanged(ItemEvent e) { 
       final JToggleButton button = (JToggleButton) e.getSource(); 
       if (e.getStateChange() == ItemEvent.SELECTED) { 
        Point p = button.getLocation(); 
        p.setLocation(p.getX() + 300, p.getY()); 
        SwingUtilities.convertPointToScreen(p, button); 
        dialog.setLocation(p); 
        dialog.setVisible(true); 
       } else { 
        dialog.setVisible(false); 
       } 
      } 
     }); 
     // addActionListener(new ActionListener() { 
     // @Override 
     // public void actionPerformed(ActionEvent e) { 
     // final JToggleButton button = (JToggleButton) e.getSource(); 
     // System.out.println("button clicked: " + button.isSelected()); 
     // if (button.isSelected()) { 
     // Point p = button.getLocation(); 
     // p.setLocation(p.getX() + 300, p.getY()); 
     // SwingUtilities.convertPointToScreen(p, button); 
     // dialog.setLocation(p); 
     // dialog.setVisible(true); 
     // } else 
     // dialog.setVisible(false); 
     // } 
     // }); 

     list.addFocusListener(new FocusListener() { 
      @Override 
      public void focusGained(FocusEvent e) { 
      } 

      @Override 
      public void focusLost(FocusEvent e) { 
       System.out.println("list focusLost, dialog: " + dialog.isVisible()); 
       // dialog.setVisible(false); 
       MultiComboBox2.this.setSelected(false); 
      } 
     }); 
    } 

    public static void main(String[] args) { 
     JFrame frame = new JFrame("Test"); 
     frame.setPreferredSize(new Dimension(300, 300)); 
     frame.setLayout(new BorderLayout()); 

     MultiComboBox2 mcb = new MultiComboBox2(frame, "Toggle"); 

     JPanel buttonPanel = new JPanel(new BorderLayout()); 
     buttonPanel.setPreferredSize(new Dimension(80, 30)); 
     buttonPanel.add(mcb, BorderLayout.CENTER); 

     JPanel blankPanel = new JPanel(new BorderLayout()); 
     frame.add(blankPanel, BorderLayout.CENTER); 

     frame.add(buttonPanel, BorderLayout.PAGE_START); 
     frame.pack(); 
     frame.setVisible(true); 
     frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
    } 
} 
+0

Merci pour les suggestions, mais malheureusement, cela reste un problème. En utilisant votre exemple de code, le 'JDialog' disparaîtra une fois que le focus est perdu, mais il introduit un nouveau problème où cliquer sur le' JToggleButton' rend maintenant le 'JDialog' visible tout le temps; au lieu du comportement précédent où le 'JDialog' apparaît et disparaît comme le' JToggleButton' est sélectionné/non sélectionné chaque fois. – malaccan