2017-01-24 2 views
0

Par analogie avec la méthode setDismissDelay (int millisecondes) ToolTipManager, je voudrais implémenter un délai de rejet pour l'effet de retournement sur un JButton.Comment régler le délai de rejet sur l'effet de survol de JButton?

Dans mon application swing, j'ai défini différentes icônes pour mes méthodes JButtons (setIcon, setPressedIcon et setRolloverIcon), mais j'essaie de résoudre un problème survenant lorsqu'un bouton JButton particulier, qui devrait ouvrir une boîte de dialogue modale, est pressé. Lorsque le bouton est enfoncé et que la boîte de dialogue modale est affichée, le bouton j affiche toujours l'icône de survol, même si j'ai passé l'icône "normal" à la méthode setPressIcon. De même, l'icône de survol ne disparaîtra pas jusqu'à ce que le curseur revienne à l'image principale, même si jdialog a été fermé.

J'ai fait un exemple pour montrer ce que je veux dire. J'ai placé seulement deux boutons dans le cadre principal, chaque bouton a une icône carrée verte comme icône "normale", et une icône rouge pour l'effet de renversement. Comme je l'ai dit, je voudrais que les boutons montrent de nouveau l'icône verte quand ils sont pressés. Le premier bouton se comportera "à tort", puisque l'icône rouge est visible après la création de jdialog. Pour le second bouton j'ai résolu ce problème en remplaçant la méthode isPressed() (dans son DefaultButtonModel), en appelant setRollover (false) quand le bouton est enfoncé.

Je ne je préférerais ne pas pense pas que ce soit la meilleure solution, d'agir directement sur ButtonModel. Donc, je voudrais savoir si vous avez une meilleure idée, peut-être quelque chose de similaire à une méthode setDismissDelay, comme je l'ai déjà dit. Merci !

Ici il y a un SSCE:

import java.awt.Color; 
import java.awt.Cursor; 
import java.awt.Graphics; 
import java.awt.image.BufferedImage; 
import java.awt.event.ActionEvent; 
import javax.swing.AbstractAction; 
import javax.swing.DefaultButtonModel; 
import javax.swing.ImageIcon; 
import javax.swing.JButton; 
import javax.swing.JDialog; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.SwingUtilities; 
public class SSCE 
{ 
    public static void main (String[] a) { 
     SwingUtilities.invokeLater (new Runnable() { 
      public void run() { 
       JFrame frame = new JFrame ("Icon Test"); 
       frame.setContentPane (new MainPanel (frame)); 
       frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); 
       frame.setResizable (false); 
       frame.pack(); 
       frame.setLocationRelativeTo (null); 
       frame.setVisible (true); 
      } 
     }); 
    } 
} 
class MainPanel extends JPanel 
{ 
    public MainPanel (JFrame parent) { 
     JButton firstButton = createButton (createButtonImage (Color.GREEN), createButtonImage (Color.RED), parent); 
     JButton secondButton = createButton (createButtonImage (Color.GREEN), createButtonImage (Color.RED), parent); 
     secondButton.setModel (new DefaultButtonModel() { 
      @Override public boolean isPressed() { 
       boolean isPressed = super.isPressed(); 
       if (isPressed) setRollover (false); 
       return isPressed; 
      } 
     }); 
     add (firstButton); 
     add (secondButton); 
    } 
    private JButton createButton (BufferedImage normalImage, BufferedImage rolloverImage, final JFrame parent) { 
     ImageIcon normalIcon = new ImageIcon (normalImage), rolloverIcon = new ImageIcon (rolloverImage); 
     JButton button = new JButton (new AbstractAction() { 
      public void actionPerformed (ActionEvent e) { 
       JDialog dialog = new JDialog (parent, "Test Dialog",true); 
       dialog.setSize (400, 400); 
       dialog.setLocationRelativeTo (parent); 
       dialog.setVisible (true); 
      } 
     }); 
     button.setBorderPainted (false); 
     button.setCursor (Cursor.getPredefinedCursor (Cursor.HAND_CURSOR)); 
     button.setFocusPainted (false); 
     button.setContentAreaFilled (false); 
     button.setIcon (normalIcon);  
     button.setPressedIcon (normalIcon); 
     button.setRolloverEnabled (true); 
     button.setRolloverIcon (rolloverIcon); 
     return button; 
    } 
    private BufferedImage createButtonImage (Color color) { 
     BufferedImage image = new BufferedImage (20, 20, BufferedImage.TYPE_INT_RGB); 
     Graphics g = image.getGraphics(); 
     g.setColor (color); 
     g.fillRect (0, 0, 20, 20); 
     g.dispose(); 
     return image; 
    } 
} 

EDIT:

Comme @camickr suggéré, je l'ai essayé d'envelopper le code ActionListener dans un SwingUtilities.invokeLater().

Je ne rediffuser le code complet, je n'ai remplacé ces lignes:

JButton button = new JButton (new AbstractAction() { 
       public void actionPerformed (ActionEvent e) { 
        JDialog dialog = new JDialog (parent, "Test Dialog",true); 
        dialog.setSize (400, 400); 
        dialog.setLocationRelativeTo (parent); 
        dialog.setVisible (true); 
       } 
      }); 

avec:

JButton button = new JButton(); 
    button.addActionListener (new ActionListener() { 
      public void actionPerformed (ActionEvent e) { 
       SwingUtilities.invokeLater (new Runnable() { 
        public void run() { 
         JDialog dialog = new JDialog (parent, "Test Dialog",true); 
         dialog.setSize (400, 400); 
         dialog.setLocationRelativeTo (parent); 
         dialog.setVisible (true); 
        } 
       }); 
      } 
     }); 

Cependant, cela ne résout pas mon problème, l'icône rouge est encore visible lorsque le dialogue est créé. J'ai essayé quelques petits ajustements, avec addActionListener ou setAction, appelant aussi uniquement setVisible dans l'appel invokeLater, mais cela ne fonctionne toujours pas.

Aussi, comment pourrais-je utiliser une minuterie sans utiliser le même code sur ButtonModel que je me sers maintenant? J'ai déjà essayé quelques "hacks" en mettant "normal" dans l'actionPerformed puis en invoquant l'autre Action avec un ActionEvent "custom", mais je voudrais avoir une solution "propre".

+1

Essayez d'utiliser SwingWorker pour ça ou jamais SwingUtilities.invokeLater() – Antoniossss

+0

Un javax.swing.Timer fonctionnerait aussi bien. Vous pouvez même définir le délai initial – Jayfray

Répondre

2

Tout le code dans un auditeur exécute sur le Event Dispatch Thread (EDT).

Le problème est que l'état du bouton n'est pas modifié avant l'appel du code ActionListener. Une fois la boîte de dialogue modale affichée, le code de changement d'état du bouton n'est pas exécuté tant que la boîte de dialogue n'est pas fermée.

Enroulez le code dans le ActionListener dans un SwingUtilities.invokeLater(). Ce code sera ajouté à la fin du EDT permettant le traitement normal du bouton pour terminer avant l'affichage de la boîte de dialogue.

Pour plus d'informations sur le EDT, lisez la section du didacticiel Swing sur Concurrency dans Swing.

Edit:

je préférerais ne pas agir directement sur ButtonModel

Consacrez plus de temps à jouer avec le code. Le problème est qu'il n'y a aucun mouseExited qui est généré lorsque la boîte de dialogue est affichée de sorte que l'état du ButtonModel n'est jamais mis à jour.

Une autre option pourrait être de générer manuellement un MouseEvent pour l'événement mouseExited et d'envoyer l'événement au bouton avant que la boîte de dialogue ne s'affiche. Bien que cette approche serait également considérée comme un hack.

comment pourrais-je utiliser une minuterie

Encore une fois, le problème est l'état du bouton. Même si vous utilisez une minuterie, vous devrez manuellement réinitialiser l'état du modèle.

Votre solution actuelle semble raisonnable puisque toute la logique est située dans une classe qui personnalise le comportement.

+0

Merci pour votre réponse mais, pour l'instant, ça ne marche toujours pas (j'ai édité ma question). – Ansharja

+0

@Ansharja votre solution actuelle semble le meilleur pour moi, voir modifier. – camickr

+0

Ok, merci, ça me suffit;) – Ansharja