Accès rapide :
La vidéo
Pourquoi utiliser des actions Swing ?
Comment utiliser des actions Swing ?
Cacher le texte de l'action dans une barre d'outils
L'exemple de code complet
Une interface graphique propose des mécanismes standardisés pour déclencher vos traitements : barre de menu, barres d'outils, menus contextuels, ... Souvent une même action est disponible sur tous ces emplacements. Du coup, comment correctement factoriser la définitions des éléments rattachés à cette action (icône, mnémonique, accélérateur, ...) ? C'est à ce besoin que cherche à répondre le concept d'action Swing.
Dans les trois chapitres précédents (Mise en oeuvre d'une barre de menu, Mise en oeuvre d'une barre d'outils et Mise en oeuvre d'un menu contextuel) nous avons appris à utiliser les principaux éléments de menu.
Le problème est que souvent une même fonctionnalité (dit autrement, une même action) est accessible à partir de la barre de menu, mais aussi à partir de la barre d'outils et enfin à partir d'un menu contextuel. De la manière dont nous avons travaillé, il est nécessaire de respecifier les icons, les accélérateurs, ... pour chaque élément de menu.
Le concept d'action Swing permet, justement, de répondre à cette problématique en factorisant en un seul endroit la définition du traitement de l'action
(l'ActionListener
), de son icône, son texte, ... Ensuite, on injecte cette action dans la barre de menu, dans la barre d'outil et dans un
éventuel menu contextuel.
Le type de base, pour coder une action Swing, est l'interface javax.swing.Action
qui étend l'interface java.awt.event.ActionListener
:
il sera donc nécessaire de redéfinir la méthode actionPerformed
. Pour les autres méthodes abstraites de cette interface, ne vous inquiétez
pas car nous n'aurons à les redéfinir. Effectivement, nous utiliserons la classe abstraite javax.swing.AbstractAction
qui implémente
l'interface initialement présentée.
Grace à la méthode putValue
, vous pourrez associer à l'action une icône, un accélérateur, un mnémonique et son texte. Cela sera fait dans le
constructeur de chacune de nos action. Voici un exemple de définition d'action pour notre élément de menu « New File... ».
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private AbstractAction actNew = new AbstractAction() { { // Le constructeur de votre classe anonyme putValue( Action.NAME, "New File" ); putValue( Action.SMALL_ICON, new ImageIcon( "icons/new.png" ) ); putValue( Action.MNEMONIC_KEY, KeyEvent.VK_N ); putValue( Action.SHORT_DESCRIPTION, "New file... (CTRL+N)" ); // ToolTipText sur tool bar putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK ) ); } @Override public void actionPerformed( ActionEvent e ) { System.out.println( "New" ); } }; |
Ensuite, il ne reste plus qu'à utiliser cette action pour créer une élément de menu dans chaque conteneur considéré (barre de menu, barre d'outils ou menu contextuel).
1 2 3 |
JMenuItem mnuNew = mnuFile.add( actNew ); JButton btnNew = toolBar.add( actNew ); JMenuItem mnuNew2 = popupMenu.add( actNew ); |
Et voici le résultat qui sera produit par l'exemple proposé ci-dessous.
Il y a un petit subtil à gérer. Par défaut, une barre d'outils (JToolBar
) affiche le texte de chaque action, ce qui n'est pas habituelle.
Le texte de l'action doit être définit pour que l'on puisse l'afficher dans une barre de menu ou un menu contextuel, mail il faut demander à ne pas
l'afficher pour une barre d'outils. Cela se fait grâce à la méthode setHideActionText
. De style de codage peuvent être employés.
Voici un premier style de codage, que je qualifierais de décomposé, consiste à récupérer un pointeur sur le bouton de la barre de menu dans une variable,
puis d'utiliser ensuite cette variable pour invoquer la méthode setHideActionText
.
1 2 3 |
JButton btnNew = toolBar.add( actNew ); btnNew.setHideActionText( true ); /* ... do other calls on btnNew */ |
Cette première technique vous permettra d'invoquer d'autres méthodes sur le bouton, si cela est nécessaire. La seconde technique est pratique si vous n'avez rien d'autre à demander au bouton : dans ce cas, il est inutile de produire une déclaration d'une variable pour un unique appel sur votre bouton. Voila comment procéder.
1 |
toolBar.add( actNew ).setHideActionText( true );
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.JToolBar; import javax.swing.JTree; import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.plaf.nimbus.NimbusLookAndFeel; @SuppressWarnings( "serial" ) public class ActionSample extends JFrame { /* Construction de l'interface graphique */ public ActionSample() { super( "Swing Action sample" ); this.setSize(600,400); this.setLocationRelativeTo( null ); this.setDefaultCloseOperation( DISPOSE_ON_CLOSE ); // Construction et injection de la barre de menu this.setJMenuBar( this.createMenuBar() ); // Construction et injection de la barre d'outils JPanel contentPane = (JPanel) getContentPane(); contentPane.add( this.createToolBar(), BorderLayout.NORTH ); // The content of the window JScrollPane leftScrollPane = new JScrollPane( new JTree() ); leftScrollPane.setPreferredSize( new Dimension( 200, 0 ) ); JTextArea textArea = new JTextArea(); JScrollPane rightScrollPane = new JScrollPane( textArea ); JSplitPane splitPane = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, leftScrollPane, rightScrollPane ); contentPane.add( splitPane /*, BorderLayout.CENTER */ ); // Association d'un popup menu sur la zone d'édition de texte // Attention avant Java SE 8.0, il faut un final au début de la déclaration !!! JPopupMenu popupMenu = this.createPopupMenu(); textArea.addMouseListener( new MouseAdapter() { @Override public void mousePressed( MouseEvent event ) { if ( event.isPopupTrigger() ) { popupMenu.show( event.getComponent(), event.getX(), event.getY() ); } } } ); } /* Methode de construction de la barre de menu */ private JMenuBar createMenuBar() { // La barre de menu à proprement parler JMenuBar menuBar = new JMenuBar(); // Définition du menu déroulant "File" et de son contenu JMenu mnuFile = new JMenu( "File" ); mnuFile.setMnemonic( 'F' ); /*JMenuItem mnuNewFile =*/ mnuFile.add( actNew ); mnuFile.addSeparator(); mnuFile.add( actOpen ); mnuFile.add( actSave ); mnuFile.add( actSaveAs ); mnuFile.addSeparator(); mnuFile.add( actExit ); menuBar.add(mnuFile); // Définition du menu déroulant "Edit" et de son contenu JMenu mnuEdit = new JMenu( "Edit" ); mnuEdit.setMnemonic( 'E' ); mnuEdit.add( actUndo ); mnuEdit.add( actRedo ); mnuEdit.addSeparator(); mnuEdit.add( actCopy ); mnuEdit.add( actCut ); mnuEdit.add( actPaste ); menuBar.add(mnuEdit); // Définition du menu déroulant "Help" et de son contenu JMenu mnuHelp = new JMenu( "Help" ); mnuHelp.setMnemonic( 'H' ); menuBar.add( mnuHelp ); return menuBar; } /* Methode de construction de la barre d'outils */ private JToolBar createToolBar() { JToolBar toolBar = new JToolBar(); toolBar.add( actNew ).setHideActionText( true ); toolBar.addSeparator(); toolBar.add( actOpen ).setHideActionText( true ); toolBar.add( actSave ).setHideActionText( true ); toolBar.add( actSaveAs ).setHideActionText( true ); toolBar.addSeparator(); toolBar.add( actExit ).setHideActionText( true ); return toolBar; } /* Methode de construction du menu contextuel */ private JPopupMenu createPopupMenu() { JPopupMenu popupMenu = new JPopupMenu(); popupMenu.add( actUndo ); popupMenu.add( actRedo ); popupMenu.addSeparator(); popupMenu.add( actCopy ); popupMenu.add( actCut ); popupMenu.add( actPaste ); return popupMenu; } /* Nos diverses actions */ private AbstractAction actNew = new AbstractAction() { { putValue( Action.NAME, "New File..." ); putValue( Action.SMALL_ICON, new ImageIcon( "icons/new.png" ) ); putValue( Action.MNEMONIC_KEY, KeyEvent.VK_N ); putValue( Action.SHORT_DESCRIPTION, "New file (CTRL+N)" ); putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK ) ); } @Override public void actionPerformed( ActionEvent e ) { System.out.println( "New" ); } }; private AbstractAction actOpen = new AbstractAction() { { putValue( Action.NAME, "Open File..." ); putValue( Action.SMALL_ICON, new ImageIcon( "icons/open.png" ) ); putValue( Action.MNEMONIC_KEY, KeyEvent.VK_O ); putValue( Action.SHORT_DESCRIPTION, "Open file (CTRL+O)" ); putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_DOWN_MASK ) ); } @Override public void actionPerformed( ActionEvent e ) { System.out.println( "Open" ); } }; private AbstractAction actSave = new AbstractAction() { { putValue( Action.NAME, "Save File" ); putValue( Action.SMALL_ICON, new ImageIcon( "icons/save.png" ) ); putValue( Action.MNEMONIC_KEY, KeyEvent.VK_S ); putValue( Action.SHORT_DESCRIPTION, "Save file (CTRL+S)" ); putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_DOWN_MASK ) ); } @Override public void actionPerformed( ActionEvent e ) { System.out.println( "Save" ); } }; private AbstractAction actSaveAs = new AbstractAction() { { putValue( Action.NAME, "Save As..." ); putValue( Action.SMALL_ICON, new ImageIcon( "icons/save_as.png" ) ); putValue( Action.MNEMONIC_KEY, KeyEvent.VK_A ); putValue( Action.SHORT_DESCRIPTION, "Save file" ); } @Override public void actionPerformed( ActionEvent e ) { System.out.println( "Save as" ); } }; private AbstractAction actExit = new AbstractAction() { { putValue( Action.NAME, "Exit" ); putValue( Action.SMALL_ICON, new ImageIcon( "icons/exit.png" ) ); putValue( Action.MNEMONIC_KEY, KeyEvent.VK_X ); putValue( Action.SHORT_DESCRIPTION, "Exit (ALT+F4)" ); putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F4, KeyEvent.ALT_DOWN_MASK ) ); } @Override public void actionPerformed( ActionEvent e ) { System.out.println( "Exit" ); } }; private AbstractAction actUndo = new AbstractAction() { { putValue( Action.NAME, "Undo" ); putValue( Action.SMALL_ICON, new ImageIcon( "icons/undo.png" ) ); putValue( Action.MNEMONIC_KEY, KeyEvent.VK_U ); putValue( Action.SHORT_DESCRIPTION, "Undo (CTRL+Z)" ); putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Z, KeyEvent.CTRL_DOWN_MASK ) ); } @Override public void actionPerformed( ActionEvent e ) { System.out.println( "Undo" ); } }; private AbstractAction actRedo = new AbstractAction() { { putValue( Action.NAME, "Redo" ); putValue( Action.SMALL_ICON, new ImageIcon( "icons/redo.png" ) ); putValue( Action.MNEMONIC_KEY, KeyEvent.VK_R ); putValue( Action.SHORT_DESCRIPTION, "Redo (CTRL+U)" ); putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK ) ); } @Override public void actionPerformed( ActionEvent e ) { System.out.println( "Redo" ); } }; private AbstractAction actCopy = new AbstractAction() { { putValue( Action.NAME, "Copy" ); putValue( Action.SMALL_ICON, new ImageIcon( "icons/copy.png" ) ); putValue( Action.MNEMONIC_KEY, KeyEvent.VK_C ); putValue( Action.SHORT_DESCRIPTION, "Copy (CTRL+C)" ); putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK ) ); } @Override public void actionPerformed( ActionEvent e ) { System.out.println( "Copy" ); } }; private AbstractAction actCut = new AbstractAction() { { putValue( Action.NAME, "Cut" ); putValue( Action.SMALL_ICON, new ImageIcon( "icons/cut.png" ) ); putValue( Action.MNEMONIC_KEY, KeyEvent.VK_T ); putValue( Action.SHORT_DESCRIPTION, "Cut (CTRL+X)" ); putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.CTRL_DOWN_MASK ) ); } @Override public void actionPerformed( ActionEvent e ) { System.out.println( "Cut" ); } }; private AbstractAction actPaste = new AbstractAction() { { putValue( Action.NAME, "Paste" ); putValue( Action.SMALL_ICON, new ImageIcon( "icons/paste.png" ) ); putValue( Action.MNEMONIC_KEY, KeyEvent.VK_P ); putValue( Action.SHORT_DESCRIPTION, "Paste (CTRL+V)" ); putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK ) ); } @Override public void actionPerformed( ActionEvent e ) { System.out.println( "Paste" ); } }; public static void main(String[] args) throws Exception { UIManager.setLookAndFeel( new NimbusLookAndFeel() ); ActionSample frame = new ActionSample(); frame.setVisible(true); } } |
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :