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'.
- In AWT: lista mostra un certo numero di righe ed ha
barra di scorrimento per accedere alle eventuali ulteriori righe
presenti
- In Swing: lista non ha barre di scorrimento di per se',
solitamente inserita in uno scrolled pane.
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:
- boolean isEmptySelection() se e' selezionato qualcosa o no
- int getSelectedIndex() l'indice dell'
elemento correntemente selezionato (se uno)
- int getMinSelectedIndex(), int getMaxSelectedIndex()
l'indice minimo e massimo dell'intervallo selezionato
(se un intervallo, Swing)
- int[] getSelectedIndices un array contenente gli indici
di tutti gli elementi selezionati (anche non contigui)
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.
- setMajorThickSpacing(int) e idem con Minor
stabilisce l'intervallo numerico fra le tacche
- setPaintThick(true/false) stabilisce se disegnare le tacche
(per default no)
- setPaintLabels(true/false) stabilisce se disegnare le
etichette numeriche
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
- TextField, JTextField: una linea di testo
- TextArea, JTextArea: testo su piu' linee,
tutto nella stessa font
- JEditorPane: testo su piu' linee,
le varie parti possono essere in font diverse, posso
inglobare immagini ed anche componenti Java,
complicato da programmare.
Anche possibile caricare documenti preformattati (HTML,RTF)
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:
- int getRows(), int getColumns() ritornano numero di
righe e colonne
- setText(String), String getText() per assegnare e leggere
il testo contenuto
- append(String) aggiunge testo in coda
- insert(String, int) inserisce testo alla posizione
indicata
- void replaceRange(String, int, int) sostituisce testo
tra le due posizioni indicate
- setFont(Font) stabilisce la font di caratteri usata
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:
- un contenitore di classe DesktopPane che simula
uno schermo all'interno della finestra principale
- per ciascun documento un contenitore di classe
JInternalFrame che realizza la finestra interna
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
- In AWT ho una speciale finestra di dialogo: FileDialog
- In Swing ho componente JFileChooser che puo' essere
collocata in qualsiasi contenitore
Consente di navigare nel file system e scegliere o scrivere il nome di
un file.
In AWT:
-
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
-
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
-
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:
-
Creare il file chooser
fc = new JFileChooser();
-
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.
-
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".
-
AWT e' piu' limitato: posso dare al componente un'etichetta
testuale ma non tracciare un'immagine grafica
-
Swing e' piu' versatile: posso dare etichetta testuale o iconica,
posso mettere bordi attorno ai contenitori
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:
- void repaint() che mette in coda una richiesta
di redisegnamento per il componente
- void paint(Graphics g) che ridisegna effettivamente
il componente
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:
- paintComponent disegna il componente
- paintBorder disegna i bordi
- paintChildren disegna ricorsivamente i figli del componente
(se e' un contenitore, i figli sono i componenti che contiene)
L'ultimo dei tre metodi e' reso necessario dal fatto che componenti Swing
sono lightweight.
- un componente heavyweight (come quelli AWT) ha la sua propria finestra,
percio' ciascun componente disegna la sua finestra
- un componente lightweight non ha finestra propria, ma e' ospitato in
una sotto-area di un suo antenato heavyweight (es. il frame che sta
alla radice della gerarchia di contenimento),
percio' e' questo antenato che si incarica di disegnare
ricorsivamente tutti i suoi discendenti
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
- implementa metodo di ridisegmanento a piacere
- cattura classi di eventi a piacere
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:
-
Color getColor() e setColor(Color c):
ritorna e assegna colore corrente
-
Font getFont() e setFont(Font):
ritorna e assegna font corrente
Classe Graphics fornisce anche primitive per disegnare.
Elenchiamo le principali, ce ne sono numerose altre.
I parametri sono interi esprimenti numero di pixel.
-
clearRect(x,y, width,height):
pulisce il rettangolo indicato riempiendolo col colore di sfondo.
-
void drawRect(x,y, width,height),
e analogo fillRect: Disegna rettangolo vuoto o pieno.
-
void drawRoundRect(x,y, width,height, arcWidth,arcHeight),
e analogo fillRoundRect():
Disegna rettangolo con angoli smussati, vuoto o pieno.
-
drawLine(x1,y1, x2,y2):
Disegna segmento tra (x1,y1) e (x2,y2).
-
drawArc(x,y, width,height, startAngle,arcAngle)
e analogo fillArc:
Disegna arco circolare o ellittico, vuoto o pieno.
Gli angoli sono in gradi,
angolo positivo significa rotazione in senso antiorario,
negativo in senso orario.
Centro dell'arco e' il centro del rettangolo di origine (x,y) e
dimensioni date da width, height.
-
drawOval(x,y, width,height) e analogo fillOval:
Disegna ellisse, vuoto o pieno.
-
drawPolygon(arrayX, arrayY, n) e analogo
fillPolygon:
Disegna poligono con n vertici definiti dai due array
di x e y.
-
drawPolyline(arrayX, arrayY, n):
Disegna spezzata poligonale con n vertici definiti
dai due array di x e y.
-
drawString(stringa,x,y):
Disegna stringa iniziando alle coordinate (x,y).
-
drawImage(Image, x,y, ImageObserver):
Disegna immagine alla posizione (x,y).
Es. per disegnare immagine che occupa tutta la componente:
Dimension d = getSize();
g.drawImage (image, 0,0, d.width,d.height, this);
-
drawImage(Image, x,y, width,height, ImageObserver):
disegna immagine alla posizione (x,y), scalata alla larghezza ed altezza
indicate.
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