Paola Magillo, Univestita' di Genova, Corso di Programmazione II per SMID, a.a. 2007-2008.

Lezione 09:

INTERFACCE GRAFICHE IN JAVA - III

Finestre di dialogo

Nella lezione 8 abbiamo menzionato l'esistenza delle finestre di dialogo...

Finestra di dialogo = finestra secondaria di un'applicazione, serve per mostrare informazioni e/o richiedere informazioni all'utente. E' intesa come finestra temporanea.
Viene mostrata in seguito a una azione dell'utente su una finestra chiamante. Esempio: se aziono il bottone per la stampa mi appare la finestra di dialogo che chiede quale stampante, quante pagine...
La finestra di dialogo contiene dispositivi (es. bottoni) con cui l'utente la chiude dopo aver ricevuto e/o dato le informazioni stabilite.

Un dialogo dipende funzionalmente dalla finestra chiamante. La finestra chiamante di solito e' la finestra principale (frame) dell'applicazione, ma puo' essere anche un'altra finestra di dialogo, che a sua volta dipende, direttamente o tramite altre finestre di dialogo, dalla finestra principale (dialoghi a cascata).

La dipendenza funzionale comporta che:

Questi comportamenti sono gestiti automaticamente dal sistema.

Classe Dialog

Finestra adatta ad essere usata come finestra di dialogo.
Viene costruita come dipendente da un'altra finestra (specificata in argomento al costruttore, ved. dopo).
Essendo intesa come finestra temporanea, il sistema puo' mostrarla con tutte le decorazioni o con un sotto-insieme (es. senza i dispositivi di controllo per redimensionamento).

Costruttori della classe Dialog:

Un dialogo viene creato vuoto. Poi bisogna riempirlo esattamente come si fa con i frame (aggiungere con "add" tutti i componenti e poi dimensionare con "pack") poiche' entrambi sono finestre.

Metodi interessanti della classe Dialog:

Esempio

La classe DialogExample.java realizza un'applicazione con due finestre:

Il dialogo e' modale, percio' l'utente non puo' interagire con il frame finche' non ha chiuso il dialogo.

import java.awt.*;
import java.awt.event.*;

class DialogExample extends Frame
{
  Button apriDialogo; // su questa finestra
  Dialog mioDialogo;
  Button chiudiDialogo; // su finestra di dialogo

  DialogExample()
  {
    /* costruzione del contenuto di questa finestra */
    apriDialogo = new Button("apri dialogo");
    add(apriDialogo);
    pack();
    /* costruzione della finestra di dialogo */
    mioDialogo = new Dialog(this,"Finestra di dialogo",true);
    mioDialogo.setLayout(new GridLayout(2,1));
    mioDialogo.add(new Label("ciao, sono una finestra di dialogo..."));
    mioDialogo.add(chiudiDialogo = new Button("chiudi dialogo"));
    mioDialogo.pack();
    /* comportamento del bottone sulla finestra principale */
    ActionListener apriListener = new ActionListener()
    {
       public void actionPerformed(ActionEvent e)
       {  mioDialogo.setVisible(true);  }
    };
    apriDialogo.addActionListener(apriListener);
    /* comportamento del bottone sulla finestra di dialogo */
    ActionListener chiudiListener = new ActionListener()
    {
       public void actionPerformed(ActionEvent e)
       {  mioDialogo.setVisible(false);  }
    };
    chiudiDialogo.addActionListener(chiudiListener);
  }

  public static void main(String[] args)
  {
    DialogExample ee = new DialogExample();
    ee.setVisible(true);
  }
}

La classe DialogExample realizza la finestra principale, sotto-classe di Frame. Il costruttore della classe DialogExample costruisce anche il dialogo e lo tiene nella variabile mioDialogo.
La callback del bottone apriDialogo presente sulla finestra principale mostra il dialogo; la callback del bottone chiudiDialogo presente sul dialogo lo nasconde.
La finestra principale invece viene mostrata direttamente dal main.

Disegno di immagini

Possiamo caricare un'immagine da file e visualizzarla in un'interfaccia grafica.

Caricare un'immagine

Per caricare un'immagine bisogna interpellate la libreria per interfacce grafiche, che nel nostro caso e' la libreria Java AWT (Abstract Window Toolkit).
Il toolkit AWT e' esso stesso e' un oggetto di classe Toolkit, e si ottiene invocando il metodo getDefaultToolkit, che e' metodo di classe nella classe Toolkit, e ritorna un oggetto di classe Toolkit. Per esempio possiamo chiamare:

   Toolkit tool = Toolkit.getDefaultToolkit();

A questo oggetto di classe Toolkit, cosi' ottenuto, possiamo chiedere di caricare un'immagine da file. I formati di file accettati sono gli stessi che si possono inserire nella pagine web: GIF, JPEG. L'immagine caricata e' un oggetto della classe Java Image.
Per caricare un'immagine dal file "pippo.gif" scriviamo:

   Image img = tool.getImage("pippo.gif");

In realta' vi e' una complicazione. Caricare un'immagine puo' essere un'operazione lunga.
Se il metodo "getImage" fosse "sincrono" la sua esecuzione dovrebbe durare tutto il tempo del caricamento.
Invece il metodo "getImage" e' "asincrono", cioe' si esegue istantameamente, mentre l'immagine non e' ancora stata caricata (pero' Java si e' annotato che deve caricarla). E' come dire: "ho comperato questo" mentre ho solo spedito l'ordine, poi ci metteranno del tempo a consegnarmelo.

Java carichera' l'immagine per forza quando chiedero' di disegnarla (e' come mettere fretta al fornitore), non prima. Ma io prima di disegnare l'immagine ho bisogno di sapere per esempio quali sono le sue dimensioni. Sfortunatamente, siccome l'immagine non e' stata ancora caricata, risultano -1 x -1 pixel.

Allora: Devo forzare il caricamento per ottenere che l'immagine sia caricata subito dopo "getImage" senza aspettare il momento di disegnarla (che sarebbe troppo tardi).
Per forzare il caricamento uso un oggetto di classe MediaTracker.

MediaTracker track = new MediaTracker(this); crea oggetto di classe MediaTracker
track.addImage(img,0); vi aggiunge l'immagine di cui voglio forzare il caricamento. all'interno del tracker questa immagine e' identificata dal codice 0
track.waitForID(0); obbliga ad aspettare finche' l'immagine identificata dal codice 0 non viene caricata del tutto

Il tutto va messo dentro un blocco "try" perche' potrebbe sollevare eccezione (potrebbe interrompersi per problemi nel caricamento).

Disegnare un'immagine dentro a un componente Java

Ogni componente Java ha un metodo "paint" che Java usa per disegnare a schermo il componente. Da programma io non chiamo MAI tale metodo, lo chiama Java automaticamente quando e' necessario.
Per es. quando la finestra viene resa visibile, quando viene redimensionata ecc.
Il metodo paint e' nella superclasse Component ed e' ereditato da tutte le classi di componenti.

Posso ridefinire l'implementazione di questo metodo per disegnare un'immagine sullo sfondo del compomente. Suppomiano che l'immagine sia contenuta nella variabile image:

  public void paint(Graphics g)
  {
    super.paint(g);
    g.drawImage (image, 0, 0, width, height, this);
  }
Dove l'argomento di "paint" rappresenta il sistema grafico (e' passato automaticamente da Java quando chiama la funzione paint).
La funzione "drawImage" disegna un'immagine con punto di aggancio (0,0) e larghezza, altezza in pixel pari a width, height.
In width, height io devo aver memorizzato le dimensioni dell'immagine caricata. Per questa ragione ho bisogno di forzare Java a caricare l'immagine PRIMA di disegnarla.
L'ultimo argomento "this" e' il componente stesso su cui sto disegnando.

Esempio

La classe ExImage realizza un pannello (sotto-classe di Panel) che, invece di essere vuoto e a dimensione 0 x 0 pixel, e' pieno di un'immagine caricata da file e ha dimensioni pari all'immagine stessa.

La classe contiene:

E' dato anche un "main" di prova che crea un frame e ci mette dentro un pannello di classe ExImage con caricata dentro l'immagine presa dal file "pallini.gif" (va scaricato anch'esso).

Analogamente, posso realizzare un bottone, una label ecc. che ha un'immagine sullo sfondo.