Coordinate cursore in griglia

di il
16 risposte

Coordinate cursore in griglia

Ciao a tutti,
sto cercando di sviluppare una griglia in cui, dopo aver cliccato su una cella, si debba iniziare a memorizzare il movimento del cursore.

Finora riesco a ricavare le coordinate del cursore una volta che clicco su una cella. Questo è il codice:

	public class Griglia extends JPanel {
	   private class Cella extends JPanel {
	      private boolean colorata;
	      private Color stColore;
	      public Cella() {
	         setOpaque( true );
	         stColore = getBackground();
	         setBorder( BorderFactory.createLineBorder(Color.BLACK, 1));
	      }
	      public void switchColore() {
	         setBackground(colorata?stColore : Color.RED);
	         colorata=!colorata;
	      }
	   }


	   private class Clicker extends MouseAdapter {

	       @Override
	       public void mouseClicked(MouseEvent me) {
	           Cella obj = (Cella) me.getSource();
	           colora(obj);
	           Point p;
	           p=MouseInfo.getPointerInfo().getLocation();
	           System.out.println("colonna: " + p.getX());
	           System.out.println("righe: " + p.getY());
	       }
	   }
	   

	   private Clicker clicker;
	   private Cella[] celle;
	   private boolean[] colora;

	   public Griglia(int righe, int colonne) {
	      setLayout(new GridLayout(righe, colonne));
	      celle = new Cella[righe*colonne];
	      colora = new boolean[righe*colonne];
	      clicker = new Clicker();

	      for(int i=0; i<righe*colonne; i++) {
	         Cella c = new Cella();
	         c.addMouseListener(clicker);
	         add(c);
	      }
	   }
	   
	   
	   private void colora(Cella cell) {
	      cell.switchColore();
	   }
	}

Adesso vorrei aggiungere che una volta che clicco su una cella deve iniziare a visualizzarmi le coordinate che assume il cursore, fino a quando non clicco in un'altra cella. Ho provato ad aggiungere il mouse listener ma non funziona, sbaglio sempre qualcosa.
Sapreste aiutarmi?

Grazie in anticipo

16 Risposte

  • Re: Coordinate cursore in griglia

    Enrichetto ha scritto:



    Adesso vorrei aggiungere che una volta che clicco su una cella deve iniziare a visualizzarmi le coordinate che assume il cursore, fino a quando non clicco in un'altra cella. Ho provato ad aggiungere il mouse listener ma non funziona, sbaglio sempre qualcosa.
    Sapreste aiutarmi?
    Con MouseListener potresti intercettare l'evento generato dall'entrare o uscire da una singola cella, ma non sei in grado di rilevare il movimento all'interno del singolo componente.
    Per quello hai bisogno di MouseMotionListener, che ha i metodi mouseMoved e mouseDragged.

    Nota che MouseAdapter implementa anche MouseMotionListener, quindi se vuoi potresti fare l'override di mouseMoved e mouseDragged anche all'interno di Clicker.
    Ma poi devi comunque richiamare addMouseMotionListener sul componente interessato, non ti basta addMouseListener, anche se la tua classe dovessere implementare entrambe le funzionalità.

    Per ricavare la coordinata del cursore ti basterà utilizzare getPoint () del MouseEvent, anche in mouseClicked tecnicamente quel :
    
    MouseInfo.getPointerInfo().getLocation()
    
    non dovrebbe servirti. In ogni caso è curioso voler memorizzare tutte le coordinate assunte dal cursore, mouseMoved ti lancerà uno sfracelo di eventi ...
  • Re: Coordinate cursore in griglia

    Quindi, non ho capito, cosa dovrei aggiungere/modificare nel mio codice per ottenere le coordinate del mouse tra un click di una cella ed un altro click?

    Grazie!
  • Re: Coordinate cursore in griglia

    Enrichetto ha scritto:


    Quindi, non ho capito, cosa dovrei aggiungere/modificare nel mio codice per ottenere le coordinate del mouse tra un click di una cella ed un altro click?

    Grazie!
    Ad esempio, scritto al volo :
    
    // ...
    for(int i=0; i<righe*colonne; i++) {
                Cella c = new Cella();
                c.addMouseListener(clicker);
                c.addMouseMotionListener (clicker); // Senza di questo ridefinire mouseMoved non avrebbe alcun effetto !
                add(c);
             }
    // ...    
    private class Clicker extends MouseAdapter {
    
              @Override
              public void mouseClicked(MouseEvent me) {
                  Cella obj = (Cella) me.getSource();
                  colora(obj);
                  Point p = me.getPoint ();
                  // non serve più ... p=MouseInfo.getPointerInfo().getLocation();
                  System.out.println("colonna: " + p.getX());
                  System.out.println("righe: " + p.getY());
              }
              @Override
              public void mouseMoved (MouseEvent e) {
              	Point p = e.getPoint ();
              	System.out.println ("Mouse spostato in " + p.x + ", " + p.y);
              }
          }
    
    Come dicevo MouseAdapter può ridefinire anche i metodi di MouseMotionListener, poi scegli tu se avere lo stesso listener o se mantenerli separati.

    Nota che quel getPoint () in mouseMoved si riferisce alla coordinata rispetto alla singola cella, se il mouseMotionListener è aggiunto ad ogni cella.
    Otterai quindi (0,0) ogni qualvolta sarai all'estremità in alto a sinistra di ogni cella, e così via.
    Se a te interessava invece la coordinata rispetto alla griglia, dovresti aggiungere il MouseMotionListener al pannello.

    Tutto però dipende da cosa vuoi fare, potresti addirittura non utilizzare alcun componente e preoccuparti tu di disegnare il pannello, fino a che devi fare rettangoli, bordi o immagini la cosa è più che fattibile, ad esempio io ho seguito questo approccio nella mia applicazione di scacchi...
  • Re: Coordinate cursore in griglia

    Ma in ogni caso gli verranno una sfracelo di coordinate...di cui c'è poca utilità.
  • Re: Coordinate cursore in griglia

    Grazie mille Ansharja, il tuo codice funziona perfettamente ed è esattamente ciò che cercavo!

    Ultimissima cosa, se invece volessi aggiungere il motion listener al pannello come dovrei fare? (Non ho ancora deciso quale strada scegliere per la definizione delle coordinate). Grazie ancora!
  • Re: Coordinate cursore in griglia

    Enrichetto ha scritto:


    Ultimissima cosa, se invece volessi aggiungere il motion listener al pannello come dovrei fare? (Non ho ancora deciso quale strada scegliere per la definizione delle coordinate). Grazie ancora!
    Semplicemente invece di aggiungere il listener ad ogni cella all'interno del ciclo con :
    
    c.addMouseMotionListener (clicker);
    
    lo aggiungi direttamente nel costruttore di griglia :
    
    // ...
    clicker = new Clicker();
    addMouseMotionListener (clicker);
    for(int i=0; i<righe*colonne; i++) {
    // ... (in questo caso rimuovi da dentro il ciclo la riga scritta sopra)
    
    Torno a ripetere, la cosa puoi farla in diversi modi , con lo stesso listener o separati, ma volendo puoi anche "dimenticarti" della classe Clicker e scrivere il listener come classe "anonima".

    Questo ti permetterebbe ad esempio di evitare di dover acquisire la cella cliccata con un getSource (), e potresti anche non passare per il metodo colora (Cella cell) di Griglia ma semplicemente richiamare il switchColore sulla cella cliccata (sempre che la griglia non debba comunque fare qualcosa di "ulteriore" oltre a colorare la cella, nel qual caso comunque dovresti notificarle il cambiamento).

    O ancora potresti solo settare una variabile boolean all'interno di Cella che memorizzi lo stato cliccato/non cliccato (per ora c'è una variabile "colorata" ma non te ne fai ancora nulla) e poi preoccuparti del colore in un eventuale ridefinizione di paintComponent () ...

    Come vedi ci sono tanti modi, ma capisco se per ora tu non voglia dover rivedere il tutto

    Però non hai mai risposto alla domanda (implicita) che ti abbiamo già fatto in due : mouseMoved ti lancerà uno sfracelo di eventi, a cosa ti serve memorizzare tutte le coordinate ?
    Magari, se ci dai un'idea di cosa intendevi fare, si riesce a trovare una via più semplice
  • Re: Coordinate cursore in griglia

    Intanto ti ringrazio per la risposta.
    Pure io avevo in precedenza aggiunto il listener nel costruttore della griglia però così facendo mi rileva le coordinate solo se il cursore è "fuori" da qualsiasi cella: la finestra ha un contorno privo di celle, se mi sposto lì mi rileva le coordinate, altrimenti non legge nulla.

    Ti spiego cosa mi servirebbe: la griglia al momento deve solo avere tutte quelle celle che si colorano al mio click (in futuro dovrò aggiungere altre funzioni ma per ora mi limito a questo) mentre io voglio che spostandomi in qualsiasi posto all'interno della finestra in cui si trova appunto questa griglia mi vengano mostrate le coordinate del cursore.
    Come posso risolverlo quindi?

    Grazie ancora!
  • Re: Coordinate cursore in griglia

    Enrichetto ha scritto:


    Intanto ti ringrazio per la risposta.
    Pure io avevo in precedenza aggiunto il listener nel costruttore della griglia però così facendo mi rileva le coordinate solo se il cursore è "fuori" da qualsiasi cella: la finestra ha un contorno privo di celle, se mi sposto lì mi rileva le coordinate, altrimenti non legge nulla.
    Errore mio, se aggiungi il listener sia al pannello sia alle celle all'interno, la cella che riceve l'evento "interecetta" quello del pannello.
    Puoi risolvere in almeno tre modi :

    - Mantieni il listener solo sulle celle e dalla singola cella ti preoccupi di calcolare la coordinata rispetto al pannello.
    - Mantieni il listener solo sul pannello e ti preoccupi di trovare la cella cliccata in base alla coordinata (questa soluzione funziona bene se la dimensione della griglia rimane costante, altrimenti fai fatica).
    - Usi un Glass Pane. Questa soluzione non l'ho mai provata, trovi qui il link ufficiale (qui invece per l'esempio).

    Enrichetto ha scritto:


    Ti spiego cosa mi servirebbe: la griglia al momento deve solo avere tutte quelle celle che si colorano al mio click (in futuro dovrò aggiungere altre funzioni ma per ora mi limito a questo) mentre io voglio che spostandomi in qualsiasi posto all'interno della finestra in cui si trova appunto questa griglia mi vengano mostrate le coordinate del cursore.
    Sì, questo si era capito, ti chiedevo lo scopo di memorizzare quelle coordinate, perché a seconda dei casi potrebbe non servire.

    Vuoi avere una label che mostri la posizione (es. il Paint che in basso a sinistra mostra ad esempio "179, 222 pixel" quanto sei sull'immagine) ?
    Vuoi memorizzare tutte le coordinate per poter in qualche modo "replicare" il movimento del cursore in modo programmatico (abbastanza complicato) ?
    Stai realizzando un gioco e devi impedire che chi gioca faccia un certo tipo di movimento o passi sopra a certe caselle ? Altro ?

    In ogni caso, se ti interessa che il tuo programma sia veloce a colorare le caselle (mi viene in mento il Campo minato dove la velocità è fondamentale), con mouseClicked vai poco avanti.
    Se provi a cliccare velocissimo nelle celle, mentre sei in movimento, l'evento non viene preso molto spesso.
    Ti converrebbe sostituire mouseClicked con mousePressed (se ti interessa che si colori la cella nella quale viene "premuto" (nel senso di abbassato) il cursore) o con mouseReleased (se ti interessa che si colori la cella nella quale viene "rilasciato" il cursore).
  • Re: Coordinate cursore in griglia

    Si mi serve una cosa simile a Paint ma voglio andar per gradi e implementare le varie funzioni man mano che risolvo quelle precedenti.

    Comunque si, hai perfettamente ragione, utilizzando il listener solo sul pannello ottengo il risultato desiderato. Non c'è alcun modo per utilizzare due listener giusto?

    Inoltre avrei una domanda: quando sviluppo la griglia ho un bordo e quindi le celle non occupano tutta la finestra. Come faccio ad avere celle in full screen (niente bordo)?
    Metto a schermo la griglia mediante:
    
    public static void main(String[] args) {
          Griglia grigl = new Griglia(19*2,34*2);
          JFrame window = new JFrame("Finestra Prova");
          window.setBounds(0,0,1360,760);
          window.setVisible(true);
          window.add(grigl);
    }
    
    Sbaglio qualcosa qui o precedentemente?
    Grazie!
  • Re: Coordinate cursore in griglia

    Enrichetto ha scritto:


    Comunque si, hai perfettamente ragione, utilizzando il listener solo sul pannello ottengo il risultato desiderato. Non c'è alcun modo per utilizzare due listener giusto?
    La terza soluzione che avevo proposto dovrebbe permetterti di mantenere entrambi.

    Ansharja ha scritto:


    - Usi un Glass Pane. Questa soluzione non l'ho mai provata, trovi qui il link ufficiale (qui invece per l'esempio).
    Come detto non l'ho testato, se hai voglia prova tu e al massimo se non funziona vediamo perché.

    Forse però ti vai a complicare un po' la vita, le altre due soluzioni sono quasi immediate, soprattutto la prima, ma anche la seconda nel caso in cui ti vada bene di mantenere la griglia di una dimensione fissa.
    Poi a livello di design non saprei cosa consigliarti adesso, non ho la visione completa di ciò che vuoi fare né un'esperienza tale da poterti dire molto di più

    Enrichetto ha scritto:


    Inoltre avrei una domanda: quando sviluppo la griglia ho un bordo e quindi le celle non occupano tutta la finestra. Come faccio ad avere celle in full screen (niente bordo)?
    Metto a schermo la griglia mediante:
    
    public static void main(String[] args) {
          Griglia grigl = new Griglia(19*2,34*2);
          JFrame window = new JFrame("Finestra Prova");
          window.setBounds(0,0,1360,760);
          window.setVisible(true);
          window.add(grigl);
    }
    
    Sbaglio qualcosa qui o precedentemente?
    Grazie!

    Non utilizzare setBounds (), in generale non è buono posizionare i componenti in modo "assoluto" o settare manualmente le dimensioni con setSize ().
    Meglio costruire la finestra, settare la griglia come content pane e poi chiudere il frame con il metodo pack (), che si preoccupa di dimensionare il tutto correttamente (per esigenze specifiche puoi comunque settare una dimensione "preferita" sui componenti, che viene rispettata dai principali layout manager).

    Io prima per fare qualche test avevo utilizzato questo codice per il main (ho solo aggiunto ora il numero di celle uguale) :
    
    public static void main (String [] a) {
    		SwingUtilities.invokeLater (new Runnable () {
    			public void run () {
    				JFrame frame = new JFrame ("Test");
    				frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
    				frame.setResizable (true);
    				frame.setContentPane (new Griglia (19*2, 34*2));
    				frame.pack ();
    				frame.setLocationRelativeTo (null);
    				frame.setVisible (true);
    			}
    		});
    	}
    
    Nota che all'inizio la finestra non ha spazio aggiuntivo oltre alla griglia.
    Se però provi a ridimensionarla, può succedere che appaia un bordo. Se non vado errato, questo dipende dal tipo di layout che stai utilizzando, il GridLayout.
    Nel GridLayout ogni singolo componente viene costretto ad avere la stessa dimensione (la dimensione del componente maggiore tra quelli nella griglia).
    Quindi se la dimensione della finestra, quando viene allargata, non è esattamente divisibile per il numero di celle, non è possibile "aggiungere dello spazio" solo ad alcune celle, e lo spazio in più deve essere occupato in qualche altro modo.
  • Re: Coordinate cursore in griglia

    Ansharja ha scritto:


    Nota che all'inizio la finestra non ha spazio aggiuntivo oltre alla griglia.
    Se però provi a ridimensionarla, può succedere che appaia un bordo. Se non vado errato, questo dipende dal tipo di layout che stai utilizzando, il GridLayout.
    Nel GridLayout ogni singolo componente viene costretto ad avere la stessa dimensione (la dimensione del componente maggiore tra quelli nella griglia).
    Quindi se la dimensione della finestra, quando viene allargata, non è esattamente divisibile per il numero di celle, non è possibile "aggiungere dello spazio" solo ad alcune celle, e lo spazio in più deve essere occupato in qualche altro modo.
    Hai perfettamente ragione! Ho fatto diverse prove ma ottengo sempre risultati non soddisfacenti.
    Come potrei mantenere una griglia di celle (di cui posso modificare il colore) ma a full-screen? Cambiando Layout perdo le proprietà della griglia quindi è un bel problema!

    Grazie ancora per l'aiuto
  • Re: Coordinate cursore in griglia

    Enrichetto ha scritto:


    Come potrei mantenere una griglia di celle (di cui posso modificare il colore) ma a full-screen? Cambiando Layout perdo le proprietà della griglia quindi è un bel problema!
    Allora, se vuoi rendere l'applicazione full-screen, puoi utilizzare questa riga di codice (non metterlo dopo il setVisible (), in genere quello deve essere l'ultimo metodo applicato al frame nella creazione) :
    
    frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
    
    Questo in realtà ti mostra ancora la cornice del frame, e a me non copre la barra degli strumenti. Puoi allargarlo ulteriormente se fai :
    
    frame.setUndecorated(true);
    
    Questo ti toglie la cornice e a me occupa interamente lo schermo, cosa che onestamente trovo molto irritante dato che devo chiudere l'applicazione con alt-tab e poi da console o con il tasto destro sull'icona in basso ...

    Ma, se vuoi mantenere il GridLayout, cosa più che sensata (sempre che non voglia essere tu a disegnare il pannello, cosa che io tra l'altro ti suggerirei di provare, almeno per farti un'idea), non puoi pretendere che la griglia occupi tutto lo spazio del frame e contemporaneamente lasciare che sia ridimensionabile.

    Su questo non si scappa, se si allarga il frame a una dimensione non multipla della lunghezza delle celle, apparirà sempre un bordo, per forza.
    Poi tu puoi scegliere come gestire questo bordo, ad esempio distribuirlo uniformemente tra destra-sinistra e alto-basso (che tra l'altro mi pare sia ciò che accada in automatico se fai solo il setExtendedState come scritto sopra).

    Se non vuoi assolutamente che appaia un bordo, e non sei tanto fortunato da non averne alcuno se la griglia si adatta perfettamente al tuo schermo massimizzato (ma sarebbe comunque una falsa soluzione), allora devi risolvere in altro modo.

    La soluzione migliore e più semplice, secondo me, è impedire che la griglia possa essere allargata a piacimento (settando setResizable (false) sul frame).
    Questo non vuol dire per forza limitarsi ad avere una singola dimensione per il frame, perché puoi comunque lasciare all'utente la possibilità di dimensionare la finestra in altro modo.
    Ad esempio una soluzione carina sarebbe avere una JMenuBar in alto (quelle barre che trovi nei programmi che di solito hanno le voci File, Modifica, Cerca, Configurazione, ecc.) in cui metti una voce Finestra, facendo un menù a cascata con le possibili dimensioni della finestra (es. 800x600, 400x300, etc.) oppure con le dimensioni della singola cella.

    In questo modo avresti una finestra con sempre e solo la griglia (ovviamente devi impostare dimensioni "furbe", senza bordi) e senza componenti aggiuntivi, solo una barra degli strumenti in alto (che comunque potrebbe farti molto comodo per fornire in seguito altre opzioni all'utente).

    Un'altra soluzione è cambiare LayoutManager, e sfruttarne uno che permetta a una o più celle della griglia di prendersi la "dimensione in eccesso".
    Io non seguirei questa via, non so neanche se sia facile ottenere questo risultato, forse con il GridBagLayout se dividi la percentuale di lunghezza tra le celle (io non uso questo layout perché è molto artificioso, preferisco combinare i layout più semplici).

    L'ultima soluzione che mi viene in mente ci riporta al disegno da parte tua del pannello.
    Qui hai la libertà di fare ciò che vuoi e i calcoli richiesti per distribuire la dimensione in più, in questo caso, non dovrebbero essere affatto complessi.
    Poi non ti so dire ora se il refresh del pannello (a seguito dell'allargamento della finestra) risulterebbe o no gradevole, per questo bisognerebbe provare.

    Dicci tu cosa pensi di fare
  • Re: Coordinate cursore in griglia

    Vanno benissimo quelle righe di codice che mi hai suggerito: è esattamente ciò che cercavo!

    Ultima cosa: avendo spostato il listener sul pannello ho "perso" la possibilità che lavorare sulle singole celle. Come faccio a selezionare una specifica cella del Grid Layout (mantenendo il listener nel pannello)?

    Grazie ancora!
  • Re: Coordinate cursore in griglia

    Enrichetto ha scritto:


    Ultima cosa: avendo spostato il listener sul pannello ho "perso" la possibilità che lavorare sulle singole celle. Come faccio a selezionare una specifica cella del Grid Layout (mantenendo il listener nel pannello)?
    Ad esempio sostituendo questo nel costruttore di Griglia (nella parte in cui aggiungi celle e listener) :
    
    for (int i=0; i<righe*colonne; i++) add (new Cella ());
    		addMouseListener (new MouseAdapter () {
    			@Override public void mouseClicked (MouseEvent e) {
    				Component component = getComponentAt (e.getPoint ());
    				if (component instanceof Cella) colora ((Cella) component);
    			}
    		});
    		addMouseMotionListener (new MouseAdapter () {
    			@Override public void mouseMoved (MouseEvent e) {
    				Point p = e.getPoint ();
    				System.out.println ("Mouse spostato in " + p.x + ", " + p.y);
    			}
    			
    		});
    
    Non penso sia il metodo migliore, ma dovrebbe andare.
    Peraltro non mi sembra che fossi particolarmente intenzionato a cambiare approccio, disegnando il pannello o fissando la dimensione della griglia.

    Comunque, quantomeno, prova a vedere cosa succede se al posto di mouseClicked utilizzi mousePressed e mouseReleased.

    Ci sarebbero inoltre alcune questioni su come hai disegnato le classi, con alcune cose non proprio belle, come ad esempio avere due vettori celle e colora (che non stai ancora usando) e contemporaneamente una variabile booleana all'interno di Cella.
Devi accedere o registrarti per scrivere nel forum
16 risposte