Chiarimento su gestione eventi

di il
9 risposte

Chiarimento su gestione eventi

Mi scuso per continuare a battere su questa tematica, ma non riesco a dissolvere la nebbia che pervade questo argomento.
nello screenshot una sintesi del problema che cerco di riproporre in mnaiera più chiara.

la JFrame coniete 3 controlli e precisamente:
1 JButton
2 jTextField

Sul bottobe è attivo l'evento click che fa incrementare e visualizzare il valore nella txt01

il codice è il seguente:


package progetto1;

import java.awt.BorderLayout;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JButton;
import javax.swing.JTextField;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;

public class TestEventi extends JFrame {

	private JPanel contentPane;
	private JTextField txt01;

	int  num=0;
	private JTextField txt02;
	
	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					TestEventi frame = new TestEventi();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public TestEventi() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 450, 300);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(null);
		
		JPanel panel = new JPanel();
		panel.setBounds(0, 0, 434, 261);
		contentPane.add(panel);
		panel.setLayout(null);
		
		txt02 = new JTextField();
		txt02.setBounds(87, 142, 288, 20);
		panel.add(txt02);
		txt02.setColumns(10);
		
		txt01 = new JTextField();
		txt01.addPropertyChangeListener(new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent arg0) {
				try {
		            if (Integer.parseInt(txt01.getText()) > 0) {
		                //auto update field
		                int t1 = Integer.parseInt(txt01.getText());
		                int result = t1 + 1;

		                txt02.setText(String.valueOf(result));
		            }
		        } catch (Exception e) {

		        }


			}
		});
		txt01.setBounds(172, 6, 86, 20);
		panel.add(txt01);
		txt01.setColumns(10);
		
		
		
		JButton btnNewButton = new JButton("New button");
		btnNewButton.setBounds(263, 5, 89, 23);
		btnNewButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
			
				num++;
				txt01.setText(String.valueOf(num));
				
			}
		});
		panel.add(btnNewButton);
	}
}



Ho attivato sulla txt01 un evento "PropertyChanged" nel quale ho impostato il codice di aggiornamento della txt02 al variare del valore della txt01.
NON viene eseguito il codice legato al propertyChanged della txt01.

Non capisco perchè e cosa devo fare per poter aggiornare la txt02 al variare della txt01.

Spero di aver spiegato al meglio il problema perchè vorrei capire bene il meccanismo degli eventi.

Grazie

Moreno (sono sempre più zuccone, ma sempre + determinato a spazzarvia questa nebbia che mi avvolge)
Allegati:
19656_1eec065022a04432bae9f7557e1268de.jpg
19656_1eec065022a04432bae9f7557e1268de.jpg

9 Risposte

  • Re: Chiarimento su gestione eventi

    misonsan ha scritto:


    Ho attivato sulla txt01 un evento "PropertyChanged"
    PropertyChangeListener/propertyChange()/PropertyChangeEvent .... NON ti servono.

    Per ricevere notifica puntuale di qualunque modifica (anche il più piccolo cambiamento di 1 carattere in più o in meno) su un campo di testo si prende l'oggetto Document associato ad un campo di testo e su quel Document si registra un DocumentListener (che ha 3 metodi)
  • Re: Chiarimento su gestione eventi

    Ciao Andrea

    Grazie innnazi tutto per la cortese risposta.

    Mi dici
    PropertyChangeListener/propertyChange()/PropertyChangeEvent .... NON ti servono.


    Allora vorrei capire il meccanismo di gestione Eventi.

    L'IDE mi permette, selezionato un controllo (JTextFiled ad esempio) di impostare gli eventi.
    Mi genera quindi, in funzione di tale scelta, il codice
    
    	txt01.addPropertyChangeListener(new PropertyChangeListener() {
    			public void propertyChange(PropertyChangeEvent arg0) {
    				try {
    		            if (Integer.parseInt(txt01.getText()) > 0) {
    		                //auto update field
    		                int t1 = Integer.parseInt(txt01.getText());
    		                int result = t1 + 1;
    
    		                txt02.setText(String.valueOf(result));
    		                JOptionPane.showMessageDialog(null, "Il valore è : " + result );
    		            }
    		        } catch (Exception e) {
    
    		        }
    
    
    			}
    		});
    
    Domanda Importante: se il codice di questo evento NON verrà maia eseguito, perchè l'IDE me lo crea ?
    perchè non mi crea il codice legato al "DocumentListener" per permettermi di eseguire quello che gli chiedo ?

    Dal punto di vista logico non trovo una spiegazione plausibile e vorrei capirlo in maniera chiara per potermi rendere autonomo.
    Spero di aver esposto il mio dubbio in mnaiera compiuta.

    Grazie

    Moreno
  • Re: Chiarimento su gestione eventi

    misonsan ha scritto:


    Allora vorrei capire il meccanismo di gestione Eventi.
    Ma il concetto non è così difficile. In AWT/Swing esistono svariati "listener", ciascuno per un compito ben preciso e specifico. Ci sono listener di "basso" livello (es. MouseListener) e listener a più alto livello (es. ActionListener).

    Un listener è una INTERFACE Java. Le interfacce si usano sovente/solitamente per definire un "contratto" tra due/più parti. Chi emette gli eventi (un componente GUI o altra entità) si aspetta di ricevere la registrazione di un oggetto che implementa una certa interfaccia con quei metodi ben precisi. E chi vuole ricevere la notifica, implementando quella interfaccia dichiara di rispettare esattamente quel contratto mettendo nella implementazione del listener esattamente quei metodi che sono richiesti dalla interfaccia.

    Ciascun componente AWT o Swing può gestire SVARIATI tipi di listener.


    JTextField ad esempio possiede:
    - addActionListener
    - addCaretListener — ereditato da JTextComponent
    - addInputMethodListener — ereditato da JTextComponent
    - addAncestorListener — ereditato da JComponent
    - addVetoableChangeListener — ereditato da JComponent
    - addContainerListener — ereditato da Container
    - addPropertyChangeListener — ereditato da Container
    - addComponentListener — ereditato da Component
    - addFocusListener — ereditato da Component
    - addHierarchyBoundsListener — ereditato da Component
    - addHierarchyListener — ereditato da Component
    - addKeyListener — ereditato da Component
    - addMouseListener — ereditato da Component
    - addMouseMotionListener — ereditato da Component
    - addMouseWheelListener — ereditato da Component

    Quindi JTextField gestisce (se non ne ho dimenticato qualcuno) 15 listener differenti. Di cui la maggior parte sono "ereditati" dalle super-classi.

    addFocusListener ad esempio è in java.awt.Component che è la classe BASE di TUTTI i componenti AWT e Swing. Ma è logico: le notifiche riguardanti il focus c'entrano con tutti i componenti, quindi è ovvio che è stato messo nella classe base.
    Idem es. addKeyListener e addMouseListener. Sono generali, validi per TUTTI i componenti. Quindi sono lì nella classe "base".

    addCaretListener (il "caret" è quella barrettina lampeggiante che indica il punto dove si inserisce il testo) riguarda solo i componenti "di testo". E quindi è stato messo in JTextComponent che è la classe base dei componenti appunto di testo.

    Il DocumentListener che dicevo prima NON è di un componente GUI ma di un "modello", il Document che è il model interno ai componenti di testo.

    Sulla mia macchina che uso ora avevo messo il Eclipse Photon con il WindowBuilder. NON lo uso normalmente il WindowBuilder ma ho fatto una verifica veloce. Se nella vista "Design" fai tasto destro mouse su un JTextField ti compare un menù di popup con diverse voci tra cui:

    Add event handler >
    e "sotto" quel menù ci sono voci
    action >
    ancestor >
    .....
    propertyChange >
    vetoableChange >

    Che corrispondono appunto ai 15 addXXXListener che ho detto prima.

    Se tu continui a cliccare su propertyChange (che NON ti serve), non si può dare la colpa al IDE o al WindowBuilder. Non è quello che devi scegliere!

    Il punto però l'ho detto prima: DocumentListener NON è di JTextField ma del Document interno. E NON compare in quella lista.
    E nella vista Properties NON vedo una proprietà "document".

    Quindi, pur non conoscendo bene il WindowBuilder ho una mia impressione/sensazione: che il DocumentListener NON lo si possa registrare "graficamente" con il WindowBuilder. E quindi vada registrato e implementato A MANO scrivendo del codice.

    Tutto qui. E se così fosse, allora sì, sarebbe un LIMITE del WindowBuilder.
  • Re: Chiarimento su gestione eventi

    Ciao Andrea

    Ti ringrazio per la risposta molto dettagliata.

    Vediamo se sto capendo qualcosa.
    Ho modificato il codice inserendo un listner sul JtestField txt01.

    ribadisco quello che dovrebbe fare la form.
    sulla txt01 viene visualizzato il valore di un contatore che viene aggiornato cliccando sul bottone.

    st txt01 ci dovrebbe essere un evento che ribalta sul txt02 lo stesso valore.
    Ho pertanto aggiunto un actionListener sul txt01.
    di default viene createo il metodo "actionPerformed"
    io ho inserito a manao altru due metodi e precisamente "changedUpdate" e "propertyChanged".

    penso che già da qui continuo a navigare in una nebbia un pò meno densa, ma sempre nebbia.
    Procediamo.

    una curiosità: per la minutazione dei due metodi non esiste un aiuto da parte di Eclipse ?
    devo digitare a mano con il pericolo di scrivere boiate ?

    procediamo

    se fosse corretto al modificarsi del valore nella txt01 dovrei scatenare l'evento (io penserei) propertyChanged e fare eseguire il codice del metodo.
    così non è perchè txt02 NON viene visualizzato.

    Fin quì è corretto ?
    Come posso procedere per ottenere la visualizzazione su txt02 del valore di txt01 ?

    ti allego il codice
    
    
    package progetto1;
    
    import java.awt.BorderLayout;
    import java.awt.EventQueue;
    
    import javax.swing.JFrame;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;
    import javax.swing.border.EmptyBorder;
    import javax.swing.JButton;
    import javax.swing.JTextField;
    import java.awt.event.ActionListener;
    import java.awt.event.ActionEvent;
    import java.beans.PropertyChangeListener;
    import java.beans.PropertyChangeEvent;
    
    public class TestEventi extends JFrame   {
    
    	private JPanel contentPane;
    	private JTextField txt01;
    
    	int  num=0;
    	private JTextField txt02;
    	
    	/**
    	 * Launch the application.
    	 */
    	public static void main(String[] args) {
    		EventQueue.invokeLater(new Runnable() {
    			public void run() {
    				try {
    					TestEventi frame = new TestEventi();
    					frame.setVisible(true);
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    			}
    		});
    	}
    
    	/**
    	 * Create the frame.
    	 */
    	public TestEventi() {
    		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		setBounds(100, 100, 450, 300);
    		contentPane = new JPanel();
    		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    		setContentPane(contentPane);
    		contentPane.setLayout(null);
    		
    		JPanel panel = new JPanel();
    		panel.setBounds(0, 0, 434, 261);
    		contentPane.add(panel);
    		panel.setLayout(null);
    		
    		txt02 = new JTextField();
    		txt02.setBounds(87, 142, 288, 20);
    		panel.add(txt02);
    		txt02.setColumns(10);
    		
    		txt01 = new JTextField();
    		// -------------------------------------------------------------------   inizio nuovo codice per gestire ActionListener
    		txt01.addActionListener(new ActionListener()
    				{
    					public void actionPerformed(ActionEvent arg0) {
    						txt02.setText("su txt01 ho il valore: " + txt01.getText());
    						
    					}
    					
    					public void changedUpdate(ActionEvent arg0) 
    		            {
    						txt02.setText("su txt01 ho il valore -------> " + txt01.getText());
    		            }
    					
    					public void propertyChanged(ActionEvent arg0)
    					{
    						txt02.setText("su txt01 ho il valore .......: " + txt01.getText());
    					}
    				});
    			// -------------------------------------------------------------------   Fine  nuovo codice per gestire ActionListener
    		txt01.setBounds(172, 6, 86, 20);
    		panel.add(txt01);
    		txt01.setColumns(10);
    		
    		//  vecchio codice che dici non serve
    		
    		/*
    		txt01.addPropertyChangeListener(new PropertyChangeListener() {
    			public void propertyChange(PropertyChangeEvent arg0) {
    				try {
    		            if (Integer.parseInt(txt01.getText()) > 0) {
    		                //auto update field
    		                int t1 = Integer.parseInt(txt01.getText());
    		                int result = t1 + 1;
    
    		                txt02.setText(String.valueOf(result));
    		                JOptionPane.showMessageDialog(null, "Il valore è : " + result );
    		            }
    		        } catch (Exception e) {
    
    		        }
    
    
    			}
    		});
    		*/
    
    		
    
    		
    		JButton btnNewButton = new JButton("New button");
    		btnNewButton.setBounds(263, 5, 89, 23);
    		btnNewButton.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent arg0) {
    			
    				num++;
    				txt01.setText(String.valueOf(num));
    
    				
    			}
    		});
    		panel.add(btnNewButton);
    
    	}
    	
    
    
    
    
    grazie

    Moreno
  • Re: Chiarimento su gestione eventi

    misonsan ha scritto:


    st txt01 ci dovrebbe essere un evento che ribalta sul txt02 lo stesso valore.
    Ho pertanto aggiunto un actionListener sul txt01.
    di default viene createo il metodo "actionPerformed"
    io ho inserito a manao altru due metodi e precisamente "changedUpdate" e "propertyChanged".
    Se stai implementando (con una anonymous inner class) e registrando un ActionListener, allora l'UNICO metodo che conta è il

    public void actionPerformed(ActionEvent e)

    Gli altri due metodi che hai messo (quei changedUpdate e propertyChanged) sono totalmente inutili. Non sono di ActionListener, NESSUNO invocherà mai questi due metodi. Non c'entrano niente.

    Quindi, ascolta: hai due JTextField txt01 e txt02.

    Se a) vuoi che premendo <invio> mentre sei dentro txt01 venga copiato il contenuto di txt01in txt02, allora registri un ActionListener su txt01 e nel actionPerformed copi il testo da txt01 a txt02.

    Se invece b) vuoi che a fronte di ciascuna singola modifica in txt01, cioè mentre stai scrivendo in txt01 venga replicato pari-pari al volo il testo in txt02, allora registri un DocumentListener sul Document di txt01 e per qualunque dei 3 metodi del listener copi il testo da txt01 a txt02.
  • Re: Chiarimento su gestione eventi

    Vediamo di chiarire meglio.
    
    		
    		JButton btnNewButton = new JButton("New button");
    		btnNewButton.setBounds(263, 5, 89, 23);
    		btnNewButton.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent arg0) {
    			
    				num++;
    				txt01.setText(String.valueOf(num));
    
    				
    			}
    		});
    		panel.add(btnNewButton);
    
    
    jButton implementa unActionListener nel quale incremento un contatore e aggiorno su txt01 il valore del contatore increementato.
    Qui è correttamente eseguito.

    Ora guardiamo txt01.
    Voglio che automaticamente alla modifica del valore su txt01 (che avviene regolarmente) txt02 venga aggiornato con una dicitura e il valore di txt01.

    il codice che penseri corretto a questo punto dovrebbe essere :
    
    		txt01 = new JTextField();
    		txt01.addActionListener(new ActionListener()
    				{
    					public void actionPerformed(ActionEvent arg0) {
    						//txt02.setText("su txt01 ho il valore: " + txt01.getText());
    						try {
    				            if (Integer.parseInt(txt01.getText()) > 0) {
    				                //auto update field
    				                int t1 = Integer.parseInt(txt01.getText());
    				           
    				                txt02.setText("il valore su txt1 è: " + String.valueOf(t1));
    				                JOptionPane.showMessageDialog(null, "Il valore è : " + t1 );
    				            }
    				        } catch (Exception e) {
    
    				        }
    					}
    
    				});
    		
    		txt01.setBounds(172, 6, 86, 20);
    		panel.add(txt01);
    		txt01.setColumns(10);
    
    
    se non interpreto male il codice, dopo aver creato l'oggetto txt01 gli associo un ActionListener al quale è associato il metodo "actionPerformed"
    Il metodo "actionPerformed" se txt01 è maggiore di zero imposta su txt02 la stringa e visualizza un messageDialog.

    E' corretta l'interpretazione del codice ?

    quello che voglio avvenga è esattamente questo.
    Se non va bene il codice, e non va bene dato che txt02 NON viene aggiornato, puoi correggermi queste due righe di cidice commentando dove sbaglio ?

    Vorrei cdercare di non romperti troppo i sentimenti.
    Ti ringrazio
    ciao

    Moreno
  • Re: Chiarimento su gestione eventi

    misonsan ha scritto:


    
    		JButton btnNewButton = new JButton("New button");
    		btnNewButton.setBounds(263, 5, 89, 23);
    		btnNewButton.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent arg0) {
    				num++;
    				txt01.setText(String.valueOf(num));
    			}
    		});
    		panel.add(btnNewButton);
    
    jButton implementa unActionListener nel quale incremento un contatore e aggiorno su txt01 il valore del contatore increementato.
    Detto non benissimo: non è JButton che "implementa" ActionListener. Se tu che "implementi" ActionListener (con una anonymous inner class) e poi passi un oggetto di tale implementazione al addActionListener del JButton.

    Comunque: per un pulsante il actionPerformed() viene invocato alla "azione" sul pulsante tramite mouse/tastiera. Quindi ad ogni azione sul pulsante, in txt01c'è un numero che si incrementa.
    Stop. Se è questo che volevi, è corretto.

    misonsan ha scritto:


    Voglio che automaticamente alla modifica del valore su txt01 (che avviene regolarmente) txt02 venga aggiornato con una dicitura e il valore di txt01.
    
    		txt01 = new JTextField();
    		txt01.addActionListener(new ActionListener()
    				{
    					public void actionPerformed(ActionEvent arg0) {
    
    Alt! "alla modifica" cosa intendi?
    L'ho sicuramente già detto prima: su un JTextField il actionPerformed() viene invocato solo (e ripeto SOLO) quando sei nel campo e premi <INVIO>.
    Il actionPerformed NON viene invocato quando il testo nel textfield "cambia" (per qualunque motivo, dall'utente o programmaticamente).

    Se vuoi ricevere notifica di QUALUNQUE cambiamento sul JTextField e intendo qualunque modifica: tolti 1+ caratteri, inseriti 1+ caratteri, rimpiazzati n caratteri, tutto questo eseguito a mano dall'utente o programmaticamente con setText che sia, allora l'ho già detto prima:

    DocumentListener sul Document del JTextField
  • Re: Chiarimento su gestione eventi

    Ciao Andrea

    Ho risolto.

    il codice che mi fa eseguire la valorizzazione su txt02 del valore presente su txt01 è:
    
    		txt01 = new JTextField();
    		// funziona
    		txt01.getDocument().addDocumentListener(new DocumentListener() {
    		  	  public void changedUpdate(DocumentEvent e) {
    	    	    warn();
    	    	  }
    	    	  public void removeUpdate(DocumentEvent e) {
    	    	    warn();
    	    	  }
    	    	  public void insertUpdate(DocumentEvent e) {
    	    	    warn();
    	    	  }
    
    	    	  public void warn() {
    	    		  txt02.setText(txt01.getText());
    	    		/*
    	    		  JOptionPane.showMessageDialog(null,
    	        	          "IL Valore su txt01 è: " + txt01.getText(), "Error Massage",
    	        	          JOptionPane.ERROR_MESSAGE);  */
    
    	    	  }
    	    	});
    
    mi resta ancora molto sconosciuto e francamente non so capire il perchè l'IDE mi da la possibilità di associare a un controllo degli eventi che poi con quel codice non riesce a dar seguito all'evento.
    In allegato ho messo il menu eventi e vorrei capire, se puoi aiutarmi a cosa serve se per attivare realmente l'evento devo utilizzare il DocumentListener sul document del jtextfield.

    Li usi mai questi eventi ?
    a cosa servono ?

    come diceva qualche vecchio "Obbedisco e mi adeguo" io invece vorrei "Obbedire perchè ho ragionevolmente capito la logica che sovraintende a un'operazione".
    L'uso delle opzioni del menu eventi mi è assolutamente sconosciuto e vorrei capire quando usarli.
    Grazie

    Buona giornata

    Moreno
    Allegati:
    19656_51eb00d6ca6ea7b2897c5b4b4e3cb9d8.jpg
    19656_51eb00d6ca6ea7b2897c5b4b4e3cb9d8.jpg
  • Re: Chiarimento su gestione eventi

    misonsan ha scritto:


    il codice che mi fa eseguire la valorizzazione su txt02 del valore presente su txt01 è:
    
    		txt01.getDocument().addDocumentListener(new DocumentListener() {
    		  	  public void changedUpdate(DocumentEvent e) {
    	    	    warn();
    	    	  }
    	    	  public void removeUpdate(DocumentEvent e) {
    	    	    warn();
    	    	  }
    	    	  public void insertUpdate(DocumentEvent e) {
    	    	    warn();
    	    	  }
    
    	    	  public void warn() {
    	    		  txt02.setText(txt01.getText());
    	    	  }
    	    	});
    
    Sì, questo è sensato e corretto.

    misonsan ha scritto:


    Li usi mai questi eventi ?
    Ci sono listener particolari che si usano molto raramente, tipo HierarchyListener o (specifici di Swing) TreeExpansionListener o CaretListener.
    Altri sono più comuni e li uso frequentemente anche io, tipo ActionListener, MouseListener e altri.

    misonsan ha scritto:


    a cosa servono ?
    Basta leggere la documentazione javadoc del framework. Ciascun listener è per uno o pochi compiti ben precisi. Che va capito ... altrimenti non si combina nulla e non si va avanti nelle GUI ...

    misonsan ha scritto:


    L'uso delle opzioni del menu eventi mi è assolutamente sconosciuto e vorrei capire quando usarli.
    Quel menù "Add event handler" che mostra il WindowBuilder serve per generare/imbastire la gestione di uno dei listener/eventi mostrati sotto in cascata. Il WindowBuilder insomma ti genera il codice (che poi deve completare il programmatore).
    Quelli che sono mostrati per JTextField sono SOLO gli eventi "diretti" dal componente stesso. NON c'è il DocumentListener, altrimenti ci sarebbe una voce "document" con sotto "changedUpdate", "insertUpdate" e "removeUpdate".

    Non ci sono, tutto qui. Come ho già detto prima, presumo sia un LIMITE del WindowBuilder. Gli editor "visuali" delle GUI non sono la panacea, non sono "oro colato", non risolvono tutti i problemi (anzi, secondo me ne pongono diversi in più).
    Il WindowBuilder ti facilita la creazione del codice per i listener dei componenti, mentre per altri come quelli dei "model" interni ci si deve arrangiare. Lo si accetta oppure no. Tutto qui.
Devi accedere o registrarti per scrivere nel forum
9 risposte