INTERFACCE GRAFICHE IN JAVA - IV

[Per corso su Java vedere http://java.sun.com/docs/books/tutorial/]

Altri contenitori

Pannelli a schedario

Permettono a piu' componenti (di solito pannelli) di condividere lo stesso spazio.
Ogni componente viene gestita come una scheda con linguetta sporgente. Selezionando la linguetta l'utente stabilisce quale scheda va mostrata davanti alle altre.

Creazione

mypanel = JTabbedPane();
mypanel = JTabbedPane(posizione);
La posizione specifica su che lato saranno visualizzate le "linguette" delle varie schede (di default su quello superiore). Valori possibili: JTabbedPane.TOP e stessa cosa con BOTTOM, LEFT, RIGHT.

Aggiunta di componenti

Per aggiungere una componente comp non si usa "add" ma si usa un metodo apposito:
mypanel.addTab(titolo,comp);
mypanel.addTab(titolo,icona,comp);
Il titolo e' la stringa da mostrare sulla linguetta. L'icona (se presente) viene anch'essa mostrata sulla linguetta.

Scelta della componente selezionata

La componente selezionata e' quella la cui scheda e' in questo momento davanti alle altre, e quindi quella che l'utente vede e con cui puo' interagire.
Per assegnare / ottenere la componente selezionata:
void setSelectedComponent(Component);
Component getSelectedComponent();

Gestione dello schedario per indici

Vari metodi per gestire lo schedario (ordinamento, selezione, abilitazione / disabilitazione di schede) identificando le componenti tramite indici interi.

Toolbar

Barra orizzontale o verticale contenente componenti (in genere bottoni con etichette grafiche).

Puo' essere trascinata e posizionata a piacere su uno dei quattro lati della finestra che la ospita o fuori dalla sua finestra e il sistema automaticamente la colloca in finestra indipendente apposita.


Devo avere un contenitore che usa BorderLayout. La toolbar deve stare in una delle quattro zone diverse da CENTER. La zona CENTER puo' ospitare (e di solito ospita) un'altra componente. Le altre tre zone devono essere vuote.

Creazione e riempimento di toolbar

mytoolbar = new JToolBar();
mytoolbar.add(new JButton("uno");
mytoolbar.add(new JButton("due");
mytoolbar.add(new JButton("tre");
mytoolbar.addSeparator();
mytoolbar.add(new JButton("ultimo");
Crea toolbar con tre bottoni, uno spazio (separatore) e poi un'altro bottone.

I bottoni sono disposti in fila nell'ordine in cui li ho inseriti. Posso controllarne l'allineamento specificando setAlignmentX/Y per ciascun bottone che aggiungo.

Posso aggiungere qualsiasi tipo di componenti ad una toolbar, di solito sono bottoni.

Per stabilire se la toolbar puo' essere trascinata per spostarla:

mytoolbar.setFloatable(true/false);

Altri dispositivi

Combo box

Una combo box puo' essere non editabile o editabile (di default non editabile).

Combo box non editabile si presenta come un bottone fino a che l'utente non interagisce con essa. Quando l'utente interagisce, appare una tendina con lista di scelte come in un menu'.

Combo box editabile appare come un campo di testo con un piccolo bottone accanto. L'utente puo' digitare nel campo di testo oppure agire sul bottone, facendo apparire una tendina di scelte.


Ciascuna scelta della tendina puo' essere stringa di testo o immagine.

Combo box non editabili

La combo box si crea inizializzandola con un array di stringhe (o di icone), e si assegna la stringa (icona) inizialmente selezionata. Esempio:
String[] scelte = {"una", "due", "tre", "quattro"};
JComboBox cbox = new JComboBox(scelte);
cbox.setSelectedItem(2);
Combo box sono sensibili a ActionEvent e ItemEvent. Di solito e' sufficiente un ActionListener per gestire una combo box.

Combo box editabili

Una combo box viene resa editabile chiamando:
cbox.setEditable(true);
Combo box editabile genera un ActionEvent quando l'utente sceglie un elemento dalla tendina e anche quando l'utente preme tasto "enter" nel campo di testo.

Liste

Una lista mostra una serie di elementi disposti in colonna da cui l'utente puo' sceglierne uno o piu'. Ammette selezione singola o multipla. Swing distingue anche fra selezione multipla limitatamente ad un intervallo contiguo oppure multipla con piu' intervalli.


Contenuto di una lista puo' essere cambiato dinamicamente.

Creazione

In AWT:
List list = new List(4);
list.add("una");
list.add("due");
list.add("tre");
list.add("quattro");
crea lista con 4 linee visibili e vi aggiunge le quattro stringhe indicate.

In Swing:

String[] scelte = {"una", "due", "tre", "quattro"};
JList list = new JList(scelte);
crea lista con le 4 scelte contenute nell'array.

Modo di selezione

In AWT stabilisce se lista permette di selezionare una linea singola o piu' linee (anche non contigue):
list.setMultipleMode(true/false);

In Swing stabilisce se lista permette di selezionare una sola linea, piu' linee contigue, oppure piu' linee anche non contigue:

list.setSelectionMode(modo);
dove il modo e' uno fra JList.SINGLE_SELECTION, JList.SINGLE_INTERVAL_SELECTION, JList.MULTIPLE_INTERVAL_SELECTION.

Eventi

Lista e' sensibile a eventi di classe ListSelectionEvent. Il corrispondente ListSelectionListener prevede il metodo
valueChanged
che scatta quando utente esegue una selezione.

Dall'evento di selezione posso ottenere:

Potenziometro (slider)

Consente di introdurre un valore numerico compreso fra un minimo ed un massimo. Barra con cursore mobile. La barra corrisponde ad un intervallo di valori, il cursore ad un valore corrente nell'intervallo.

Creazione

slider = new JSlider(direzione,minimo,massimo,corrente);
La direzione puo' essere JSlider.HORIZONTAL o JSlider.VERTICAL, i valori minimo, massimo e corrente sono interi.

Righello

Slider puo' visualizzare righello con due ordini di tacche: maggiore e minore. Ciascun ordine di tacche puo' essere numerato oppure no.

Eventi

Slider e' sensibile a ChangeEvent. Il corrispondente ChangeListener prevede il metodo
stateChanged
che scatta quando utente muove il cursore dello slider.

Per leggere i valore corrente dello slider usare getValue(). Per sapere se utente sta ancora muovendo il cursore o ha finito, chiamare getValueIsAdjusting().
Esempio che reagisce solo se utente ha terminato l'interazione:

public void stateChanged(ChangeEvent e)
{
  JSlider source = (JSlider)e.getSource();
  if (!source.getValueIsAdjusting())
    ... fare qualcosa con source.getValue() ...
}

Campi ed aree di testo

4 campi di testo:area di testo:

Campo di testo

Mostra una sola linea di testo editabile. Genera ActionEvent quando utente preme il tasto "return".

Creare un campo di testo:

TextField(String);
TextField(int);
TextField(String);
TextField(String,int);
La stringa e' il contenuto iniziale, l'intero e' la larghezza in numero di caratteri del campo.

Leggere e assegnare il testo contenuto:

void setText(String)
String getText()

Possibilita' di controllare la validita' secondo regole sintattiche del testo inserito.

In Swing, esiste sottoclasse JPasswordField di JTextField che funziona in modo identico ma la scritta inserita e' nascosta (al posto delle lettere sono mostrati caratteri di "echo").

In AWT e' possibile specificare che la stringa mostrata in un campo di testo deve essere nascosta.

Aree di testo

Area di testo su piu' linee, editabile dall'utente tramite tastiera e mouse.

In AWT puo' avere barre di scorrimento laterali. In Swing non sono previste, ma posso inserire l'area di testo dentro uno scrolled pane.

Costruzione di area di testo:

tarea = new TextArea();
tarea = new TextArea(numerorighe,numerocolonne);
tarea = new TextArea(testoiniziale);
tarea = new TextArea(testoiniziale,numerorighe,numerocolonne);
Similmente in Swing.

Metodi utili per area di testo:

Esempio:
JTextArea textArea = new JTextArea("Questo e' un esempio ");
textArea.setFont(new Font("Serif", Font.ITALIC, 16));
Solo in Swing:

Per default, l'area di testo non va a capo, ma mostra tutto su linea unica e, se inserita in scroll pane, permette di scorrere orizzontalmente. Si puo' abilitare l'andata a capo con setLineWrap e setWrapStyleWord per indicare che le linee devono essere spezzate solo a fine parola.
Esempio:

textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);

Finestre interne

Per implementare finestre di documento all'interno della finestra principale di un'applicazione.

Devo avere:

Le finestre all'interno del finto schermo hanno decorazioni che forniscono i controlli tipici delle finestre a top-level (chiusura, iconificazione...), ma sono aggiunte e gestite da Swing invece che dal window manager. Per questo, possono differire dalle decorazioni delle finestre top-level (in particolare da quelle della finestra che le contiene).

Creazione

Esempio che crea una finestra (sottoclasse di JFrame) con una finestra interna:
class MyFrame extends JFrame
{
  JDesktopPane mydesktop = null;
  JInternalFrame f1 = null;

  MyFrame()
  {
    mydesktop = new JDesktopPane();
    setContentPane(mydesktop);
    f1 = new JInternalFrame("titolo 1",true,true,true,true);
    f1.setSize(new Dimension(200,50));
    f1.setVisible(true);
    mydesktop.add(f1);
    setSize(new Dimension(500,500));
    setVisible(true);
  }
}
Il costruttore della classe MyFrame stabilisce che il pannello di contenuto del mio frame e' un pannello desktop.
Crea f1 finestra interna al mio frame, con possibilita' di essere redimensionata, chiusa, massimizzata ed iconificata (i quattro true nel costruttore), di dimensione 200x50 pixel ed inizialmente mappata (visibile) sul desktop del mio frame.
Stabilisce che le dimensioni del mio frame sono 500x500 pixel cioe' piu' grandi della finestra interna.

In generale f1 invece di essere un JInternalFrame sara' di una sottoclasse di JInternalFrame che definisce anche un contenuto per la finestra interna.

Differenze e similarita' tra frame ed internal frame

La gerarchia di componenti sotto di un JInternalFrame ricalca quella che ho sotto un JFrame. Anche un JInternalFrame ha pannello di contenuto e devo aggiungere componenti al pannello di contenuto.
Anche JInternalFrame ha metodo pack per stabilirne le dimensioni ottimali in base al contenuto. Bisogna sempre assegnare le dimensioni di un internal frame prima di visualizzarlo.

Internal frame non ricevono eventi di finestra (WindowEvent) come i frame, invece ricevono eventi di classe InternalFrameEvent, che sono analoghi.

Supporto predefinito per operazioni particolari

Scelta di un file

Consente di navigare nel file system e scegliere o scrivere il nome di un file.

In AWT:

  1. creare la finestra di dialogo e stabilire se e' finestra per caricamento o per salvataggio di file
    chiediFile = new FileDialog(finestra, "titolo");
    chiediFile.setMode(FileDialog.LOAD
    oppure FileDialog.SAVE);
    dove finestra e' la finestra da cui il dialogo dipende
  2. mostrare il dialogo
    chiediFile.setVisible(true);
    l'interazione con l'utente e' gestita automaticamente dal sistema, finche' utente non chiude il dialogo premendo bottone OK o CANCEL
  3. ottenere il nome del file e, se il nome esiste, prendere il file
    String fileName = chiediFile.getFile();
    String dirName = chiediFile.getDirectory();
    if ( (fileName != null) && (fileName.length()>0) )
    {
    ...aprire file dirName + fileName...
    }
In Swing:
  1. Creare il file chooser
    fc = new JFileChooser();
  2. Chiamare uno dei seguenti metodi che mostrano un dialogo modale contenente il file chooser
    int scelta = fc.showOpenDialog(finestra);
    int scelta = fc.showSaveDialog(finestra);
    int scelta = fc.showDialog(finestra,"titolo");
    dove finestra e' la componente da cui il dialogo deve dipendere.
  3. Leggere il valore di ritorno, che puo' essere JFileChooser.APPROVE_OPTION o JFileChooser.CANCEL_OPTION e, in caso di approvazione, prendere il file
    if (scelta==JFileChooser.APPROVE_OPTION)
    {
    ... apertura del file ...
    }
Altri metodi consentono di assegnare directory corrente, definire filtri, personalizzare.

Scelta di un colore (solo Swing)

JColorChooser consente selezione di un colore o da una tavolozza, o mediante terna RGB (rosso, verde, blu) o mediante terna HSB (tinta, saturazione, luminosita').

Non lo vediamo.

Grafica in Java

Un componente Java copre un'area rettangolare della finestra nella quale e' collocato. La API permette entro certi limiti di personalizzare l'aspetto grafico del componente cioe' "che cosa viene disegnato su quest'area rettangolare". Nel caso questo non fosse sufficiente, e' possibile associare ad un componente Java del codice per "disegnarlo" nel modo desiderato.

In AWT, questo e' l'unico modo per es. per inserire immagini in un'interfaccia. In Swing il disegnamento diretto e' piu' raramente necessario.

Un componente "disegna se stesso" per mezzo di un oggetto di classe Graphics, che rappresenta il contesto grafico corrente e che fornisce funzioni di disegnamento (tracciare linee, poligoni, cerchi, mappare immagini ecc.).

Meccanismo di ridisegnamento

Ogni componente Java prevede i metodi: Il metodo paint e' chiamato dal sistema, attraverso canali suoi, ogni volta che il sistema ritiene che il componente vada ridisegnato (es. quando la finestra che lo contiene e' mappata o torna visibile dopo essere stata oscurata da altre, quando viene ridimensionata...).

Il metodo repaint puo' essere invocato da programma per ridisegnare il componente in altri casi, non previsti dal sistema, in cui occorra ridisegnarlo. Non chiamare direttamente paint!

Siccome la gestione della coda e' asincrona, puo' capitare che piu' chiamate a repaint vengano collassate in una sola chiamata a paint.

IN AWT, il metodo paint e' "monolitico".

In Swing, il metodo paint e' implementato invocando nell'ordine i seguenti tre metodi:

L'ultimo dei tre metodi e' reso necessario dal fatto che componenti Swing sono lightweight.

Personalizzare il redisegnamento

Il componente personalizzato va implementato come sottoclasse di una classe esistente.

Scegliere una classe di componente specializzata (es Button / JButton), in modo da ereditare tutti i comportamenti previsti da tale classe (es. per bottone la capacita' di catturare ActionEvent).
Se non sono necessari comportamenti particolari si puo' usare un Panel / JPanel.

Ridefinire il metodo paint in AWT o paintComponent in Swing inserendovi il codice per compiere il disegnamento voluto. Chiamare sempre super.paint o super.paintComponent all'inizio in modo da effettuare anche tutto il redisegmanento previsto di default (es. pulitura dello sfondo).

Invocare repaint ogni qual volta si ritiene che il componente vada ridisegnato.

Nota su AWT

AWT fornisce classe di componente Canvas con scopo apposito di permettere la definizione di classi di componenti personalizzati.

Programmatore puo' definire sottoclasse di canvas che

In questo modo puo' realizzare tipi di componenti non presenti in AWT.

Metodi per disegnare

Ogni componente ha un sistema di coordinate intere (in pixel) con origine (0,0) in alto a sinistra e punto in basso a destra di coordinate (d.width-1,d.height-1) dove Dimension d = getSize(). Le coordinate x crescono da sinistra a destra, le coordinate y crescono dall'alto verso il basso.

In Swing devo tenere conto che parte del rettangolo della componente puo' essere occupata dal bordo. Insets i = getInsets() ritorna informazioni sullo spessore del bordo.
La parte di rettangolo libera dal bordo ha angolo in alto a sinistra (i.left,i.top) e in basso a destra (d.width-i.right-1,d.height-i.bottom-1).

Metodo paint ha come argomento oggetto di classe Graphics che viene passato automaticamente dal sistema.

Tale oggetto contiene informazioni di stato tra cui per es. colore e font correnti.

Si puo' agire sullo stato con:

Classe Graphics fornisce anche primitive per disegnare. Elenchiamo le principali, ce ne sono numerose altre. I parametri sono interi esprimenti numero di pixel.

Esempi

Esempio di bottone con disegnamento personalizzato

La funzione paint traccia le due diagonali del rettangolo

Esempio di pannello con disegnamento personalizzato

Traccia un'immagine caricata da file e una stringa