Griglia di JPanel con MouseListener

di il
7 risposte

Griglia di JPanel con MouseListener

Salve a tutti sono nuovo del forum, sono uno studente universitario in crisi con il suo primo progetto. Sto lavorando ad un progetto che ha come fine, quello di creare e gestire un centro urbano.


Sono arivato al punto in cui devo gestire tramite il frame questo Centro urbano formato da edifici pubblici, privati strade e lotti liberi.
Ogni edificio o strada che sia, sono salvati in lotti che a loro volta vengono salvati in una griglia 3x4 in un settore, c'è un totale di 6 settori in questo centro urbano tutti salvati in un ArrayList<Settore>, con un totale di 72 lotti. Ora tramite una determinata finestra viene sviluppata la prima parte che mostra la griglia 2x3 contenente tutti i settori, dato che al passaggio del mouse devono cambiare colore ho utilizzato per ogni settore un JPanel, ciò che non riesco a fare è cliccare sul settore a me interessato capire di quale settore si tratta, e da li spostarmi in esso e accedere ai lotti presenti, aperti in un nuovo frame che li mostra tutti, permettendo poi la modifica tramite determinate condizioni del CentroUrbano.

JPanel panel = new JPanel();
		panel.setLayout(new GridLayout(2, 3));
		panel.setBounds(21, 68, 801, 384);
		
		for (i = 0; i < cu.getListaSettori().size(); i++) {
			
		
			JPanel panelSet = new JPanel();
			 panelSet.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
			 panelSet.setBackground(Color.GREEN);
			 panelSet.addMouseListener(new MouseListener() {
				
				@Override
				public void mouseReleased(MouseEvent e) {
				}
				
				@Override
				public void mousePressed(MouseEvent e) {}
				
				@Override
				public void mouseExited(MouseEvent e) {
					 panelSet.setBackground(Color.GREEN);
				}
				
				@Override
				public void mouseEntered(MouseEvent e) {
					 panelSet.setBackground(Color.RED);	
				}
				
				@Override
				public void mouseClicked(MouseEvent e) {}
			});
			
			 	panel.add(panelSet);
		}

Questo è il codice con cui accedo al prima frame con questa griglia 2x3 che al passaggio del mouse modifica il colore.
Ripeto il mio dubbio:

Come faccio ad accedere al settore tramite il click del frame? Avevo pensato ad assegnare ad ogni click un valore che è quello che porta alla posizione del settore richiesto nell'ArrayList.
Spero di essermi spiegato, grazie in anticipo

7 Risposte

  • Re: Griglia di JPanel con MouseListener

    Non sono sicuro di aver capito il problema, vuoi identificare la posizione del settore cliccato all'interno della lista?

    Se sì la soluzione più semplice è usare l'indice all'interno del ciclo, ma non puoi usare "i" all'interno del MouseListener, se è dichiarata come variabile locale, perché non è final (o "effectively final" da java 8 in poi).
    Puoi risolvere assegnando all'interno del ciclo il valore i ad una variabile final. Qualcosa di questo tipo:
    for (i = 0; i < cu.getListaSettori().size(); i++) {
    	final int index = i;	
    	JPanel panelSet = new JPanel();
    	// ...
    	panelSet.addMouseListener (new MouseListener() {
    		// ...
    		@Override public void mouseClicked(MouseEvent e) {
    			System.out.println ("Hai cliccato sul settore in posizione: " + index);
    		}
    	});
    	panel.add (panelSet);
    }
    Se non è importante avere l'indice del settore, ma solo il reference dell'oggetto, perché non te lo memorizzi semplicemente all'interno del ciclo?

    PS: Non so se usare solo mouseExited e mouseEntered sia abbastanza per assicurare che ci sia un solo pannello colorato, in alcuni casi potresti ad esempio cambiare finestra (es alt+tab su windows), o potresti perdere il focus per altri motivi, e magari l'evento del mouse exited potrebbe venire perso. Forse avresti bisogno di una variabile "logica" che memorizza quale indice o quale settore è attualmente selezionato, e usare quella per determinare il colore di background (ad esempio facendo l'override di getBackground).
  • Re: Griglia di JPanel con MouseListener

    Ansharja ha scritto:


    Non sono sicuro di aver capito il problema, vuoi identificare la posizione del settore cliccato all'interno della lista?

    Se sì la soluzione più semplice è usare l'indice all'interno del ciclo, ma non puoi usare "i" all'interno del MouseListener, se è dichiarata come variabile locale, perché non è final (o "effectively final" da java 8 in poi).
    Puoi risolvere assegnando all'interno del ciclo il valore i ad una variabile final. Qualcosa di questo tipo:
    for (i = 0; i < cu.getListaSettori().size(); i++) {
    	final int index = i;	
    	JPanel panelSet = new JPanel();
    	// ...
    	panelSet.addMouseListener (new MouseListener() {
    		// ...
    		@Override public void mouseClicked(MouseEvent e) {
    			System.out.println ("Hai cliccato sul settore in posizione: " + index);
    		}
    	});
    	panel.add (panelSet);
    }
    Ti ringrazio e hai risolto a pieno il mio problema, ora però se per te non è un problema, potresti spiegarmi la motivazione per cui hai usato questa variabile final? Perchè ad ogni ciclo viene settata con la i e rimane su quel valore, cioè assume 6 valori diversi?
    Scusami la domanda, ma sono un pò confuso.

    Grazie comunque.
  • Re: Griglia di JPanel con MouseListener

    Pasquale012 ha scritto:


    potresti spiegarmi la motivazione per cui hai usato questa variabile final?
    Rispondo io già che ci sono (ma @Ansharja ti direbbe la stessa cosa). Prima di Java 8, quella variabile index messa come final è un obbligo per poter essere usata dalla anonymous inner class. Perché il valore deve diventare sostanzialmente una "costante" per poter essere usata lì.

    Da Java 8 tutto questo è stato un po' "rilassato", nel senso che index può anche non essere final, purché sia concettualmente effectively final, ovvero solo inizializzata una volta e poi MAI toccata. Cosa che è così, perché in quel blocco { } del for, index viene assegnata UNA volta sola.

    La variabile i del for NON può essere usata direttamente dalla anonymous inner class .. nemmeno da Java 8 in poi. Perché NON è effectively final (in quanto c'è i++, quindi modificata) e né può essere ovviamente final (perché altrimenti non sarebbe usabile dal for).

    Ecco perché serve una variabile "temporanea" di passaggio, final (< Java 8 ) o effectively final (>= Java 8 )
  • Re: Griglia di JPanel con MouseListener

    Vi ringrazio per l'aiuto che mi state fornendo, ho un ulteriore domanda sempre riguardo al MouseListener.
    Una volta entrato nel rispettivo settore e quindi nel frame rispettivo con tutti i lotti. Se volessi prendere di striscio, cioè tenendo il mouse premuto, tre lotti di fila salvare gli indici dei lotti e quindi da li andare a creare una strada su tre lotti di fila. Vi metto un immagine per rendere meglio l'idea.
    Che tipo di metodo mi consigliate di usare? Sempre il MouseListener oppure il MouseMotionListener?

    Vi ringrazio in anticipo.
    Allegati:
    23000_300cc0c857c82d970d8a23eba4527142.png
    23000_300cc0c857c82d970d8a23eba4527142.png
  • Re: Griglia di JPanel con MouseListener

    Potresti usare tutti e due, anche con una singola classe che estenda MouseAdapter (che implementa già i metodi di entrambi, quindi puoi ridefinire solo quello che ti interessa) e aggiungere lo stesso listener con entrambe le funzioni, es:
    JPanel panelSet = ...
    
    MouseAdapter mouseAdapter = new MouseAdapter () {
    	@Override public void mouseEntered (MouseEvent e) {  }
    	// ...
    	@Override public void mouseDragged (MouseEvent e) { }
    	 
    };
    
    panelSet.addMouseListener (mouseAdapter);
    panelSet.addMouseMotionListener (mouseAdapter);
    Vado a memoria, ma dovrebbe funzionare.

    Questo in generale ti consente di intercettare ogni tipo di evento, e di agire di conseguenza.

    Il mouseDragged normalmente sarebbe il metodo giusto per eventi in cui serve la pressione continua del mouse.
    Ma qui potrebbe non servire, nel senso che non stai trascinando un immagine o altro, non devi insomma "rispondere" ad ogni evento generato dalla pressione mantenuta, ti interessa solo sapere in quali lotti sei transitato nel lasso di tempo in cui il mouse era premuto.

    Quindi io userei il resto dei metodi, seguendo ad esempio una logica di questo tipo:

    - Quando entri nel mousePressed (che a differenza del mouseClicked vene generato solo dalla pressione del tasto del mouse, non serve che sia stato rilasciato) metti un flag a true, che indica l'inizio della pressione del mouse. In questo momento potresti creare/svuotare una lista di lotti o di indici di lotti dove andrai a memorizzare tutti quelli che vengono attraversati dal mouse.
    - Quando entri nel mouseEntered controlli il valore di questa variabile: se è true aggiungi l'indice del lotto/il lotto alla lista che hai svuotato in precendenza.
    - Quando entri nel mouseReleased metti il flag a false, la pressione del mouse è terminata. In questo punto scegli cosa fare della lista che hai riempito.

    Ovviamente quando fai un singolo click del mouse (senza tenerlo premuto), il mousePressed e il mouseReleased saranno chiamati subito uno dopo l'altro, quindi avrai sempre una lista vuota e non dovrai fare nulla.
    Ma se invece tieni il mouse premuto a lungo ed entri nei vari lotti avrai l'insieme dei lotti che sono stati attraversati, e potrai farne quello che vuoi.

    Poi puoi inventarti quello che vuoi con tutti gli altri metodi, questa è una bozza di quello che farei io con le informazioni a disposizione.
    Purtroppo non riesco a buttare giù adesso un codice d'esempio, se intanto la soluzione ti piace puoi provare a scrivere tu qualcosa, per qualsiasi dubbio o altra proposta scrivi pure
  • Re: Griglia di JPanel con MouseListener

    Ansharja ha scritto:


    Quindi io userei il resto dei metodi, seguendo ad esempio una logica di questo tipo:

    - Quando entri nel mousePressed (che a differenza del mouseClicked vene generato solo dalla pressione del tasto del mouse, non serve che sia stato rilasciato) metti un flag a true, che indica l'inizio della pressione del mouse. In questo momento potresti creare/svuotare una lista di lotti o di indici di lotti dove andrai a memorizzare tutti quelli che vengono attraversati dal mouse.
    - Quando entri nel mouseEntered controlli il valore di questa variabile: se è true aggiungi l'indice del lotto/il lotto alla lista che hai svuotato in precendenza.
    - Quando entri nel mouseReleased metti il flag a false, la pressione del mouse è terminata. In questo punto scegli cosa fare della lista che hai riempito.

    Ovviamente quando fai un singolo click del mouse (senza tenerlo premuto), il mousePressed e il mouseReleased saranno chiamati subito uno dopo l'altro, quindi avrai sempre una lista vuota e non dovrai fare nulla.
    Ma se invece tieni il mouse premuto a lungo ed entri nei vari lotti avrai l'insieme dei lotti che sono stati attraversati, e potrai farne quello che vuoi.
    Ho provato a sviluppare il tutto come hai detto tu, però quando utilizzo il mouse Pressed e passo per ogni lotto,il lotto selezionato rimane sempre il primo cliccato, nel senso che se premo il terzo lotto, e passo sul quarto mi seleziona sempre il terzo. Fatto un click alla volta riesco a inerirlo in un ArrayList, ma ciò purtroppo, forse per mio errore, non risolve il problema.

    Se vedi pensi sia sbagliato il mio ragionamento, dimmi pure, ti prego.

    Ti ringrazio tanto per la risposta.
  • Re: Griglia di JPanel con MouseListener

    Pasquale012 ha scritto:


    Ho provato a sviluppare il tutto come hai detto tu, però quando utilizzo il mouse Pressed e passo per ogni lotto,il lotto selezionato rimane sempre il primo cliccato, nel senso che se premo il terzo lotto, e passo sul quarto mi seleziona sempre il terzo.
    Vedi questo:
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.GridLayout;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.awt.event.MouseMotionListener;
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class FramePrincipale extends JFrame {
        public FramePrincipale() {
            super("Prova settori");
    
            setLayout(new GridLayout(2, 3, 5, 5));
    
            GestoreSelezione gestoreSelezione = new GestoreSelezione();
    
            for (int r = 0; r < 2; r++) {
                for (int c = 0; c < 3; c++) {
                    PannelloSettore pannelloSettore = new PannelloSettore(r, c);
                    pannelloSettore.addMouseListener(gestoreSelezione);
                    pannelloSettore.addMouseMotionListener(gestoreSelezione);
    
                    add(pannelloSettore);
                }
            }
    
            setDefaultCloseOperation(EXIT_ON_CLOSE);
            pack();
            setLocationRelativeTo(null);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    new FramePrincipale().setVisible(true);
                }
            });
        }
    }
    
    
    class GestoreSelezione implements MouseListener, MouseMotionListener {
        private boolean selezioneAttiva;
        private Set<PannelloSettore> pannelliSelezionati;
    
        public GestoreSelezione() {
            pannelliSelezionati = new HashSet<PannelloSettore>();
        }
    
        @Override
        public void mouseClicked(MouseEvent e) {}
    
        @Override
        public void mousePressed(MouseEvent e) {
            // Inizio della selezione
            rimuoviSelezione();
            selezioneAttiva = true;
            seleziona((PannelloSettore) e.getSource());
        }
    
        @Override
        public void mouseReleased(MouseEvent e) {
            // Fine della selezione
            selezioneAttiva = false;
        }
    
        @Override
        public void mouseEntered(MouseEvent e) {
            if (selezioneAttiva) {
                // Estensione della selezione
                seleziona((PannelloSettore) e.getSource());
            }
        }
    
        @Override
        public void mouseExited(MouseEvent e) {}
    
        @Override
        public void mouseDragged(MouseEvent e) {}
    
        @Override
        public void mouseMoved(MouseEvent e) {}
    
    
        private void rimuoviSelezione() {
            for (PannelloSettore pannelloSettore : pannelliSelezionati) {
                pannelloSettore.setSelezionato(false);
            }
    
            pannelliSelezionati.clear();
        }
    
        private void seleziona(PannelloSettore pannelloSettore) {
            pannelloSettore.setSelezionato(true);
            pannelliSelezionati.add(pannelloSettore);
        }
    }
    
    
    class PannelloSettore extends JPanel {
        private final int riga;
        private final int colonna;
        private boolean selezionato;
    
        public PannelloSettore(int riga, int colonna) {
            this.riga = riga;
            this.colonna = colonna;
    
            setPreferredSize(new Dimension(80, 80));
            setBackground(Color.BLUE);
        }
    
        public int getRiga() {
            return riga;
        }
    
        public int getColonna() {
            return colonna;
        }
    
        public void setSelezionato(boolean selezionato) {
            if (this.selezionato != selezionato) {
                this.selezionato = selezionato;
                setBackground(selezionato ? Color.RED : Color.BLUE);
            }
        }
    
        public boolean equals(Object o) {
            if (o instanceof PannelloSettore) {
                PannelloSettore altroPannelloSettore = (PannelloSettore) o;
                return getRiga() == altroPannelloSettore.getRiga()
                    && getColonna() == altroPannelloSettore.getColonna();
            } else {
                return false;
            }
        }
    
        public int hashCode() {
            return getRiga() * 31 + getColonna();
        }
    
        public String toString() {
            return "PannelloSettore[riga=" + getRiga() + ",colonna=" + getColonna() + "]";
        }
    }
    Provalo, clicca, trascina. Poi ripeti, ecc...


    P.S. il concetto che ho applicato è simile (se non uguale) a quanto espresso da Ansharja. Ma il codice l'ho scritto io "di pugno" e senza farmi "condizionare" da quanto detto da lui prima.
Devi accedere o registrarti per scrivere nel forum
7 risposte