Perché il mio ChangeListener reagisce solo per JMenu e non per JMenuItem?
-
12-11-2019 - |
Domanda
Ora ho un JMenu e alcuni JMenuItem al suo interno.Voglio che il mio programma esegua alcune azioni quando lo stato di JMenu e JMenuItem viene modificato in "selezionato".Non utilizzo MouseOver di MouseLitener, perché voglio che l'utente possa navigare nel menu anche utilizzando le tastiere.Ora, ho scritto questo ascoltatore:
class MenuItemListener implements ChangeListener {
@Override
public void stateChanged(ChangeEvent arg0) {
JMenuItem item = (JMenuItem) arg0.getSource();
if(item.isSelected())
System.out.println(item.getText()+" pressed!");
}
}
Quando aggiungo questo ascoltatore a JMenu, funziona correttamente, ma quando lo aggiungo a JMenuItem, non succede nulla...Quando elimino l'istruzione if in modo che l'ascoltatore reagisca entrambi, quando il menu è selezionato e deselezionato, funziona bene sia per JMenu che per JMenuItem.Quindi, come vedo, JMenuItem non può "superare" il test isSelected()...Ma cosa può essere un problema?:S
Soluzione
Senza offesa, questa è solo una di quelle domande con una storia
- requisito iniziale:fare qualcosa quando il mouse si trova su JMenuItem
- iniziale, tutti cari:MouseListener
suggerimento iniziale deviante (complimenti a @mKorbel!):ChangeListener sul buttonModel, controllando la proprietà rollover
requisito raffinato:fai qualcosa quando JMenuItem è appena evidenziato, sia con la tastiera che con il passaggio del mouse.
- raffinato tesoro:ChangeListener sul buttonModel, proprietà non specificata
deviazione raffinata:ActionListener
requisito attuale:faiSomething quando la proprietà "selezionata" JMenu o JMenuItem è cambiata.
- tesoro attuale:non può essere fatto con un ascoltatore, override ...
- deviazioni attuali:Azione, MenuListener...
La risposta corretta e completa (col senno di poi, dato che la tastiera non era ancora stata menzionata) era già disponibile al primo turno:alcuni ascoltatori semantici che sono "abbastanza di basso livello" per catturare i cambiamenti di stato (i candidati vengono ribaltati, armati, selezionati, premuti a livello di buttonModel) che fanno cambiare il loro menuItems evidenziato stato.Sfortunatamente, la relazione esatta non è ben nota (almeno a me), non documentata (leggi:io che sono pigro non sono riuscito a trovare nulla con una rapida occhiata) e persino confuso (di nuovo, per me) poiché il rollover è sempre falso (?) per menuItems
La reazione dello sperimentalista è quella di...Tentativo:di seguito è riportato uno snippet di codice che ascolta e registra le modifiche di stato su alcuni menu ad albero (basta lanciarlo in una menuBar arbitraria e spostare il mouse e navigare tramite tastiera).
E il vincitore è:- usa un ChangeListener e controlla se la sorgente è selezionata o armata.
ChangeListener ch = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if (e.getSource() instanceof JMenuItem) {
JMenuItem item = (JMenuItem) e.getSource();
if (item.isSelected() || item.isArmed()) {
System.out.println("Highlighted: " + item.getActionCommand());
}
}
}
};
funziona sia per tastiera che per mouse, sia JMenu che JMenuItem
//----------- code snippet to track property changes in menuItem/buttonModel
// test menu
JMenu menu = new JMenu("Sample menu");
menu.setMnemonic('s');
installListeners(menu);
// first menuitem
JMenuItem other = menu.add("content1");
installListeners(other);
// second menuitem
other = menu.add("again + ");
installListeners(other);
// sub
JMenu sub = new JMenu("subMenu");
installListeners(sub);
menu.add(sub);
// menus in sub
other = sub.add("first in sub");
installListeners(other);
other = sub.add("second in sub");
installListeners(other);
getJMenuBar().add(menu);
private void installListeners(JMenuItem menu) {
menu.getModel().addChangeListener(getChangeListener());
menu.addChangeListener(getChangeListener());
}
private ChangeListener getChangeListener() {
ChangeListener ch = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if (e.getSource() instanceof ButtonModel) {
ButtonModel model = (ButtonModel) e.getSource();
System.out.println("from model: " + createStateText(model));
} else if (e.getSource() instanceof JMenuItem) {
JMenuItem item = (JMenuItem) e.getSource();
System.out.println(" from item: " + createStateText(item));
}
}
private String createStateText(ButtonModel model) {
String text = model.getActionCommand() + " armed: " + model.isArmed();
text += " selected: " + model.isSelected();
text += " rollover " + model.isRollover();
text += " pressed: " + model.isPressed();
return text;
}
private String createStateText(JMenuItem model) {
String text = model.getActionCommand() + " armed: " + model.isArmed();
text += " selected: " + model.isSelected();
// not supported on JMenuItem nor on AbstractButton
// text += " rollover " + model.isRollover();
// text += " pressed: " + model.isPressed();
return text;
}
};
return ch;
}