Centralizzare la gestione di certi eventi

di il
12 risposte

Centralizzare la gestione di certi eventi

Buongiorno a tutti,

Situazione:
In una Form JFrame vorrei centralizzare la gestione di certi eventi, come per esempio la chiusura della Form tramite la pressione di un tasto, oppure l'avvio di una ricerca su un db alla pressione del tasto INVIO quando il fuoco è su uno qualsiasi dei campi di immissione dati.
Attualmente sto usando un KeyEventDispatcher definito nel costruttore della javax.swing.JFrame

    this.MioControlloTasti = new KeyEventDispatcher() {
      @Override
      public boolean dispatchKeyEvent(KeyEvent e) {
        clsLog.ScriviLog(this, MesLevel.INFO.Val(), "dispatchKeyEvent: Tasto pigiato = " + e.getKeyCode());
        if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
          Uscita();
          return true;
        }
        if (e.getID() == KeyEvent.KEY_RELEASED) {
          if (e.getKeyCode() == KeyEvent.VK_F11 ) {
            Tbl1.transferFocus();
            return true;
          }
        }
        return false; // Con false intercetto i tasti dagli altri eventi della form.
      }
    }; 
    KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this.MioControlloTasti);
Problema:
Ho una Form A che chiama la Form B la quale chiama la Form C.
Tutte e tre hanno un KeyEventDispatcher nel loro costruttore e alla pressione di qualsiasi tasto vengono attivati tutti i 3 KeyEventDispatcher.

Come faccio invece a fare in modo che quando il fuoco ce l'ha, per esempio la Form B venga attivato solo il suo KeyEventDispatcher?

Oppure: bisogna usare qualche altro modo (alternativo al KeyEventDispatcher) per ottenere questo risultato?

12 Risposte

  • Re: Centralizzare la gestione di certi eventi

    ZioCrick ha scritto:


    oppure l'avvio di una ricerca su un db alla pressione del tasto INVIO quando il fuoco è su uno qualsiasi dei campi di immissione dati.
    Se nel frame hai già un pulsante es. "OK", "Conferma" (o come si chiama...) e vuoi che premendo "invio", qualunque sia il componente che ha il "focus", venga eseguita l'azione che farebbe quel pulsante, allora non c'è da fare nulla di molto strano, perché questo è già gestito dal JRootPane! Basta usare il suo setDefaultButton.

    Prova questo:
    import java.awt.FlowLayout;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JSlider;
    import javax.swing.JTextField;
    import javax.swing.SwingUtilities;
    
    public class FrameProva extends JFrame {
        public FrameProva() {
            super("Prova");
            setDefaultCloseOperation(EXIT_ON_CLOSE);
            setSize(300, 200);
            setLocationRelativeTo(null);
            setLayout(new FlowLayout());
    
            add(new JTextField(10));
            add(new JTextField(10));
            add(new JSlider());
    
            JButton defButton = new JButton("OK");
            add(defButton);
    
            defButton.addActionListener(event -> System.out.println("Azione di OK"));
    
            getRootPane().setDefaultButton(defButton);    // <------ Questo
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> new FrameProva().setVisible(true));
        }
    }
    Stando su qualunque dei componenti, "invio" va a triggerare esattamente la stessa azione che si può fare con il pulsante OK.
  • Re: Centralizzare la gestione di certi eventi

    Grazie per l'esempio, che costituisce un altro tassello nella mia conoscenza di Java.
    Il mio problema però è più esteso e riguarda Form senza bottoni (li evito come la peste ). Normalmente uso i menù.

    Il mio dilemma fondamentale è quindi:
    è corretto usare il KeyEventDispatcher per centralizzare la gestione di alcuni eventi (di vario tipo) in una JFrame, oppure esistono modi alternativi che evitino di essere attivati anche da altre form chiamanti e-o chiamate?
  • Re: Centralizzare la gestione di certi eventi

    ZioCrick ha scritto:


    Grazie per l'esempio, che costituisce un altro tassello nella mia conoscenza di Java.
    Prego.

    ZioCrick ha scritto:


    Il mio problema però è più esteso e riguarda Form senza bottoni (li evito come la peste ). Normalmente uso i menù.
    Se usi molto i menù, puoi usare gli accelerator sui JMenuItem. Un accelerator è quella combinazione di tasti che di norma compare a fianco del menù, tipo negli editor di testo, per intenderci, File --> Salva Ctrl-S
    Anche con il menù chiuso, l'accelerator risponde alla azione, indipendentemente dal componente che ha il focus nella finestra.

    ZioCrick ha scritto:


    è corretto usare il KeyEventDispatcher per centralizzare la gestione di alcuni eventi (di vario tipo) in una JFrame, oppure esistono modi alternativi che evitino di essere attivati anche da altre form chiamanti e-o chiamate?
    No, KeyEventDispatcher (ovvero KeyboardFocusManager) va meglio per cose più "trasversali", azioni più globali nella applicazione (e su più finestre).

    In alternativa agli accelerator c'è il meccanismo del Key Binding, che è ancora un'altra cosa ....
  • Re: Centralizzare la gestione di certi eventi

    andbin ha scritto:


    Se usi molto i menù, puoi usare gli accelerator sui JMenuItem. Un accelerator è quella combinazione di tasti che di norma compare a fianco del menù, tipo negli editor di testo, per intenderci, File --> Salva Ctrl-S
    Anche con il menù chiuso, l'accelerator risponde alla azione, indipendentemente dal componente che ha il focus nella finestra.
    Amico carissimo, pensa che l'uso degli "accelerator" che io ho chiamo "tasti di scelta rapida", è sempre stata la mia filosofia di programmazione anche quando sviluppavo professionalmente in VB.
    Le mie applicazioni sono completamente utilizzabili tramite tastiera, senza l'uso dell'odiatissimo "topo". E' per questo che le mie finestre hanno quasi tutte un menù.

    andbin ha scritto:


    No, KeyEventDispatcher (ovvero KeyboardFocusManager) va meglio per cose più "trasversali", azioni più globali nella applicazione (e su più finestre).
    Già, infatti avevo iniziato ad usarlo per gestire il passaggio da una finestra ad una altra con i tasti <ALT><TAB>.
    In alternativa agli accelerator c'è il meccanismo del Key Binding, che è ancora un'altra cosa ....
    Grazie mille, allora adesso mi metto a cercare informazioni su questo meccanismo.
  • Re: Centralizzare la gestione di certi eventi

    ZioCrick ha scritto:


    Già, infatti avevo iniziato ad usarlo per gestire il passaggio da una finestra ad una altra con i tasti <ALT><TAB>.
    Ma ALT+TAB non viene già "consumato" prima dal S.O. per lo switch tra finestre??

    ZioCrick ha scritto:


    Grazie mille, allora adesso mi metto a cercare informazioni su questo meccanismo.
    Se vuoi gestire una combinazione che non è tra gli accelerator (e non deve esserlo) ma "extra", allora sì, il Key Binding è una opzione: How to Use Key Bindings
    Per dubbi, chiedi.
  • Re: Centralizzare la gestione di certi eventi

    andbin ha scritto:


    Ma ALT+TAB non viene già "consumato" prima dal S.O. per lo switch tra finestre??
    SI scusa! Ho scritto di fretta e ho ciccato.
    In realtà utilizzo la sequenza <Ctrl><Tab> e la <Ctrl><Shift><Tab> per tornare indietro.

    andbin ha scritto:


    Se vuoi gestire una combinazione che non è tra gli accelerator (e non deve esserlo) ma "extra", allora sì, il Key Binding è una opzione: How to Use Key Bindings
    Per dubbi, chiedi.
    Grazie mille, sei un amico.
  • Re: Centralizzare la gestione di certi eventi

    Ciao Andrea,
    sono riuscito finalmente a fare qualche prova con il Key Binding...
    e ho visto come può essere usato in abbinamento ad un componente.
    Quello che non riesco a capire è come usarlo in abbinamento ad un JFrame perché, da quel che capisco, il metodo getInputMap() non esiste per il JFrame.

    Esempio, ho una classe:
    
    public class ProveFrm extends javax.swing.JFrame {
      public ProveFrm() { 
        initComponents();
    //	faccio varie cose.
      }
    }
    
    ma se nel costruttore aggiungo:
    
        ProveFrm.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "check");
    
    oppure:
    
        this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "check");
    
    mi da l'errore che non trova il simbolo getInputMap.

    Io ho solo bisogno di centralizzare a livello di JFrame la gestione di qualche tasto, indipendentemente da quale componente abbia il fuoco in un certo momento.
    All'inizio avevo provato ad usare gli eventi formKeyPressed e formKeyTyped del JFrame, ma non vengono mai attivati.

    Ci sono altri metodi per ottenere questo?
  • Re: Centralizzare la gestione di certi eventi

    ZioCrick ha scritto:


    Quello che non riesco a capire è come usarlo in abbinamento ad un JFrame perché, da quel che capisco, il metodo getInputMap() non esiste per il JFrame.
    getInputMap() è di JComponent. JFrame chiaramente non deriva da JComponent (non è un "componente").
    Però JRootPane sì, è il primo "figlio" strutturale contenuto in JFrame. E generalmente si agisce su questo.
  • Re: Centralizzare la gestione di certi eventi

    Aggiornamento:
    Dopo le solite ricerche e prove varie, ero riuscito a far funzionare la gestione dei tasti di scelta rapida attraverso il Key Binding sul JRootPane e già esultavo...
    ma poi mi sono accorto che se nella JFrame faccio una qualsiasi semplice operazione come pigiare il tasto <TAB> o fare un click su di un campo di testo, il meccanismo non funziona più.

    Riporto il codice di una semplice Form in cui ho inserito il minimo indispensabile per fare la prova.
    Io uso NetBeans come ambiente di sviluppo, quindi le dichiarazioni degli oggetti grafici sono opera sua.
    In questo esempio intercetto il tasto <Escape> e la combinazione <Ctrl><Enter>
    
    import java.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;
    import javax.swing.AbstractAction;
    import javax.swing.JRootPane;
    import javax.swing.KeyStroke;
    
    public class ProvaRP extends javax.swing.JFrame {
    //                                                                    Costruttore.
      public ProvaRP() {
        initComponents();
    
        JRootPane myRP = this.getRootPane();
        myRP.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Esc");
        myRP.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_DOWN_MASK), "CtrlEnt");
    
        myRP.getActionMap().put("Esc", new AbstractAction() {
          @Override
          public void actionPerformed(ActionEvent ee) {
            System.out.println(this.getClass().getName() + "actionPerformed: Pigiato <ESC>");
            dispose();
          }
        });
    
        myRP.getActionMap().put("CtrlEnt", new AbstractAction() {
          @Override
          public void actionPerformed(ActionEvent ee) {
            System.out.println(this.getClass().getName() + "actionPerformed: Pigiato <CtrlEnt>");
          }
        });
      }
    
      @SuppressWarnings("unchecked")
      // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
      private void initComponents() {
    
        TxtTesto = new javax.swing.JTextField();
        BtnShow = new javax.swing.JButton();
    
        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
    
        TxtTesto.setHorizontalAlignment(javax.swing.JTextField.LEFT);
    
        BtnShow.setMnemonic('m');
        BtnShow.setText("Mostra il testo");
        BtnShow.addActionListener(new java.awt.event.ActionListener() {
          public void actionPerformed(java.awt.event.ActionEvent evt) {
            BtnShowActionPerformed(evt);
          }
        });
    
        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
          layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addGroup(layout.createSequentialGroup()
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addGroup(layout.createSequentialGroup()
                .addGap(38, 38, 38)
                .addComponent(TxtTesto, javax.swing.GroupLayout.PREFERRED_SIZE, 243, javax.swing.GroupLayout.PREFERRED_SIZE))
              .addGroup(layout.createSequentialGroup()
                .addGap(81, 81, 81)
                .addComponent(BtnShow)))
            .addContainerGap(34, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
          layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addGroup(layout.createSequentialGroup()
            .addGap(24, 24, 24)
            .addComponent(TxtTesto, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addGap(18, 18, 18)
            .addComponent(BtnShow)
            .addContainerGap(28, Short.MAX_VALUE))
        );
    
        pack();
      }// </editor-fold>                        
    
      private void BtnShowActionPerformed(java.awt.event.ActionEvent evt) {                                        
        System.out.println(this.getClass().getName() + "Testo inserito: " + TxtTesto.getText());
      }                                       
      // Variables declaration - do not modify                     
      private javax.swing.JButton BtnShow;
      private javax.swing.JTextField TxtTesto;
      // End of variables declaration                   
    }
    

    La attivo dalla form principale con il comando:
    new ProvaRP().setVisible(true);
    Come mai succede questo?
    Per caso sto usando il Key Binding o il JRootPane in modo improprio?
  • Re: Centralizzare la gestione di certi eventi

    ZioCrick ha scritto:


    mi sono accorto che se nella JFrame faccio una qualsiasi semplice operazione come pigiare il tasto <TAB> o fare un click su di un campo di testo, il meccanismo non funziona più.

    Riporto il codice di una semplice Form in cui ho inserito il minimo indispensabile per fare la prova.
    Sì, il tuo codice mostra chiaramente che il Key Binding così come l'hai usato non fa quello che ti aspetti. Ma .... il punto è che hai usato getInputMap(), quello senza parametri.

    Quello che forse non sai è che invece devi usare l'altro: getInputMap(int condition)

    Questo condition può essere:
    - JComponent.WHEN_IN_FOCUSED_WINDOW
    - JComponent.WHEN_FOCUSED (è quello usato dal getInputMap() senza parametri)
    - JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT

    Ciascuno di questi fa tirare fuori una InputMap differente, il cui ambito d'uso cambia in base appunto a ciascuno dei tre concetti.
    E a te è sufficiente il WHEN_IN_FOCUSED_WINDOW che da javadoc:

    means that the command should be invoked when the receiving component is in the window that has the focus or is itself the focused component

    Ovvero, la azione mappata sul JRootPane viene considerata se la finestra che contiene quel JRootPane ha il focus (JRootPane non riceve mai il focus, quindi non può essere il caso "is itself the focused component"). A meno che ci sia un listener o un altro binding che consuma prima l'evento. Nel tutorial Oracle è spiegata la logica di ricerca dei binding.

    Nota: i famosi mnemonics e gli accelerator sono implementati "dietro le quinte" proprio tramite il Key Binding!

    Se fai es.
    JMenuItem apriItem = new JMenuItem("Apri");
    apriItem.setAccelerator(KeyStroke.getKeyStroke("ctrl A"));
    nel InputMap(WHEN_IN_FOCUSED_WINDOW) di apriItem viene registrato il binding "ctrl pressed A" --> "doClick"

    Se nessun'altro consuma Ctrl+A, la InputMap(WHEN_IN_FOCUSED_WINDOW) di apriItem verrà consultata qualunque sia il componente che ha il focus nella finestra (es. un JTextField).

    Quindi se usi WHEN_IN_FOCUSED_WINDOW, puoi gestire una azione che ha la stessa "ampiezza" d'uso degli accelerator, ma senza dover appunto applicare un accelerator ad una voce di menu (che magari non hai o non vuoi dover creare).
  • Re: Centralizzare la gestione di certi eventi

    andbin ha scritto:


    Quello che forse non sai è che invece devi usare l'altro: getInputMap(int condition)

    Questo condition può essere:
    - JComponent.WHEN_IN_FOCUSED_WINDOW
    - JComponent.WHEN_FOCUSED (è quello usato dal getInputMap() senza parametri)
    - JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
    Mitico Andrea!!! FUNZIONA!!!

    andbin ha scritto:


    ...che da javadoc:

    means that the command should be invoked when the receiving component is in the window that has the focus or is itself the focused component
    Ecco e qui ritorno a sentirmi un asino.
    Perché non ho mai capito come mai il mio NetBeans non ha inglobato la javadoc... e non sono ancora riuscito a capire come aggiungerla!
    O ti riferisci alla documentazione on line?
  • Re: Centralizzare la gestione di certi eventi

    ZioCrick ha scritto:


    O ti riferisci alla documentazione on line?
    Il javadoc della API di JavaSE si intende questo (16): https://docs.oracle.com/en/java/javase/16/docs/api
    Che su Java SE Downloads c'è anche lo zip scaricabile per averlo offline.

    Poi come avere in un IDE il javadoc del framework standard o di una qualsiasi libreria per fruirlo online e/o offline, è tutto un altro discorso.
Devi accedere o registrarti per scrivere nel forum
12 risposte