Classi
grafiche e interfaccia utente
Le
API per la realizzazione di UI sono contenute, nella
versione standard di java, nell'Abstract Window Toolkit.
Purtroppo, il modello AWT é particolarmente indicato
per sistemi desktop ma, mal si adatta a dispositivi
wireless, non solo a causa delle risorse richieste,
solitamente troppo elevate per tali apparecchiature
ma anche per la diversità dei dispositivi di
input/output in gioco: tastiera, mouse e ampi display
da una parte, tastierini alfanumerici, touch-screen
e display di ridotte dimensioni dall'altra. La difficoltà
di estrapolare da AWT anche solo un sottoinsieme di
API da "adattare" a questo tipo di dispositivi,
ha indotto gli sviluppatori del MIDP a realizzare nuove
librerie grafiche appositamente "disegnate"
per tali apparati, che ritroviamo nel package javax.microedition.lcdui
(Limited Connection Device User Interface).
Cuore di tali librerie sono le classi Displayable e
Display: la prima rappresenta la "massima astrazione"
di un oggetto posizionabile e visualizzabile sullo schermo,
la seconda (Display) costituisce invece il vero e proprio
gestore dello schermo.
Displayable costituisce quindi la radice delle classi
grafiche del MIDP: da essa nascono due rami di sottoclassi,
Canvas e Screen, che consentono una gestione della grafica
rispettivamente a basso e alto livello. La figura 1
riporta in dettaglio la struttura gerarchica delle librerie
grafiche contenute nel package javax.microedition.lcdui.
Figura 1 - Gerarchia delle classi grafiche del
MIDP
La
classe (astratta) Displayable contiene quattro metodi
che permettono la gestione di qualunque oggetto grafico
si desideri realizzare:
addCommand(
Command c )
removeCommand( Command c )
permettono
l'aggiunta e la rimozione di un comando da associare
o associato ad un particolare componente grafico,
isShown()
ritorna
un valore booleano che indica se l'oggetto è
attualmente visibile sul display oppure no.
Il
metodo setCommandListener( CommandListener listener
), infine, permette di "agganciare" un listener
di eventi ad "alto livello". L'interfaccia
CommandListener contiene un solo metodo che deve essere
implementato: commandAction( Command c, Displayable
d ) il quale riceve come parametri rispettivamente il
comando e l'oggetto grafico che ha generato l'evento.
La
classe Display, come detto, costituisce il display-manager,
e si occupa della visualizzazione degli oggetti sullo
schermo. Ogni MIDlet può fare riferimento ad
un'unica istanza della classe Display attraverso il
metodo getDisplay(MIDlet midlet) che ritorna appunto
un oggetto Display.
I metodi getCurrent() e setCurrent(..) permettono di
ottenere e di settare l'oggetto visualizzato/da visualizzare
a display. Il metodo setCurrent ha le due firme differenti:
setCurrent(Displayable
nextDisplayable)
setCurrent(Alert alert, Displayable nextDisplayable)
nel
primo caso viene specificato solo l'oggetto da visualizzare,
nel secondo viene passato anche un oggetto Alert, cioè
una struttura predefinita che può contenere immagini
e testo e che può essere utilizzata per segnare
messaggi per un periodo di tempo prestabilito (la classe
Alert ha un apposito metodo setTimeOut(int time)), al
termine del quale sul display verrà visualizzato
l'oggetto nextDisplayable. Questa seconda opzione può
essere utile in molti casi pratici, come ad esempio
la segnalazione di eventuali errori nell'inserimento
dei dati di login (login-password) da parte dell'utente.
Per ottenere informazioni sulla tipologia dello schermo
(o dell'eventuale touch-screen), la classe Display contiene
inoltre metodi isColor() e numColors(), che permettono
di verificare il supporto del colore.
Classi
grafiche e gestione degli eventi (alto livello)
Come
visibile in figura 1 la classe Screen è estesa
da Form e da alcune altre classi che costituiscono oggetti
grafici predefiniti (TextBox, List, Alert).
Form
La
classe Form rappresenta un oggetto grafico visualizzabile
su display al quale è possibile aggiungere un
insieme arbitrario di componenti, cioè oggetti
di tipo Item (estensioni della classe Item). La figura
2 riporta l'elenco completo delle estensioni della classe
Item.
Figura 2 - Estensioni della classe Item
Nelle
figure 3 e 4 è possibile osservare come appaiono
i vari componenti inseriti in una form e visualizzati
sullo schermo:
Figura 3 - Choice, DateField, Gauge
Figura 4 - ImageItem, StringItem, TextField
La
classe Form prevede due differenti costruttori:
Form
(String formName);
Form (String formName, Item[] items);
nel
primo dei quali viene passato solo la stringa che indica
il nome della form (è la label che verrà
visualizzata sul display), il secondo costruttore prevede
invece anche il passaggio di un array di componenti
(di tipo Item o una loro estensione) che devono essere
contenuti e visualizzati nella form stessa.
I metodi:
public
int append (Image img);
public int append (Item itm);
public int append(String str);
permettono
l'inserimento nella form, rispettivamente, di immagini,
item e stringhe, accodandoli all'ultimo elemento inserito
(l'intero ritornato rappresenta l'indice associato all'oggetto),
mentre il metodo:
public
void insert( int itemNumber, Item itm);
permette
di inserire un oggetto nella posizione desiderata indicata
da index (per posizione non si intende, naturalmente,
le coordinate x,y del display ma la posizione occupata
nell'array dagli oggetti contenuti nella form).
Gli oggetti inseriti possono essere, eventualmente,
eliminati con il metodo:
public
void delete(int itemIndex);
I
metodi:
public
Item get(itemNumber);
public void set(itemNumber, Item itm);
permettono di ottenere e settare gli oggetti presenti
in una form, mentre il metodo
public
int size()
ritorna
il numero di elementi contenuti nella form.
La classe Form ha inoltre la possibilità di rilevare
e gestire il "cambiamento di stato" di un
componente inserito:
setItemStateListener(
ItemStateListener listener);
permette
di "agganciare" alla form un listener (la
gestione non è in alcun modo implementata nel
MIDP) . La classe ItemStateListener contiene un unico
metodo
public
void itemStateChanged(Item itm);
che
viene richiamato in caso di particolari operazioni svolte
su determinati componenti (ChoiceGroup, Gauge, TextField,
DateField) presenti nella form. In particolare, il cambiamento
di stato del componente è associato a:
- selezione
di un determinato valore in una ChoiceGroup;
- modifica
del valore di un Gauge;
- inserimento
o la modifica di un valore in un TextField;
- inserimento
o la modifica di una data in un DateField;
TextBox
La
classe TextBox, permette l'inserimento e la modifica
di testo da parte dell'utente. Essa dispone di numerosi
metodi per l'inserimento, la manipolazione, la cancellazione
del testo inserito o di sue parti. Il costruttore ha
la seguente forma:
TextBox(
String title, String defaultValue, int maxSize, int
constraints );
dove
title rappresenta la label che verrà visualizzata,
defaultValue serve per settare un eventuale valore di
default, maxSize è il numero massimo di caratteri
che possono essere contenuti nella TextBox, constraints
è un valore numerico che serve a stabilire il
tipo di dati che possono essere inseriti nella TextBox
e/o una loro particolare rappresentazione ( ad esempio
visualizzando "*" in luogo dei caratteri digitati
se il dato inserito è una password). I constraints
previsti sono i medesimi di quelli utilizzabili per
i TextField, cioè:
ANY
- qualunque sequenza di caratteri;
EMAILADDR
- i dati inseriti rappresentano un indirizzo e-mail;
NUMERIC
- i dati inseriti devono essere di tipo numerico;
PASSWORD
- i caratteri inseriti rappresentano una password;
PHONENUMBER
- la sequenza rappresenta un numero telefonico;
URL
- l'utente può inserire una sequenza di caratteri
rappresentanti una URL;
Il
seguente codice genera la TextBox visibile in figura
5:
import
javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public
class LCDUIExample extends MIDlet implements CommandListener
{
........
........
/ /**Costruttore della TextBox. Dimensione massima 200
caratteri di qualunque tipo (ANY)*/
textBox = new TextBox("TEXT BOX", "Default
Text",200, TextField.ANY);
/ /**Collega un listner alla TextBox*/
textBox.setCommandListener(this);
........
........
}
Figura 5 - l'oggetto TextBox
List
Le
List sono strutture predefinite che implementano l'interfaccia
Choice, e possono essere create utilizzando i seguenti
costruttori:
List
( String name, int listType )
List ( String name, int listType, String[] elements,
Image[] icons )
Nel
primo costruttore vengono passati la label e il tipo
di lista da creare, nel secondo caso vengono passati
due ulteriori parametri: un array di stringhe che contiene
i valori da visualizzare nella lista (sono i vari elementi
selezionabili) ed un'array di immagini da associare
a ciascun elemento.
Il parametro intero listType permette di creare tre
tipi differenti di liste. Nell'interfaccia Choice vi
sono tre costanti intere, che corrispondono ad altrettanti
tipi di liste:
IMPLICIT
- in questo caso la selezione di uno degli elementi
della lista genera una chiamata al listener registrato
al quale viene passato un comando del tipo SELECT_COMMAND;
EXCLUSIVE
- prevede la selezione di un unico elemento della lista;
MULTIPLE
- rende possibile la selezione multipla;
La
seguente porzione di codice genera i tre tipi di liste
rappresentate in figura 6:
List
list;
...........
/**Costruttore.
E' possibile passare anche un array di Image da associare
ad ogni elemento, in questo caso non viene passato alcun
array*/
list = new List(" IMPLICIT LIST ", Choice.IMPLICIT,
new String [] {"ONE", "TWO", "THREE"},
null);
/**Il listener associato*/
list.setCommandListener(this);
............
..............
/**Cattura
e gestione degli eventi generati. Se il comando è
una selezione sulla lista registra l'indice dell'elemento
selezionato*/
public void commandAction(Command c, Displayable d)
{
if(c == List.SELECT_COMMAND)
selectedIndex = list.getSelectedIndex();
if (c == exit) {
destroyApp(true);
}
}
Cambiando
il parametro listType nel costruttore e lasciando invariato
tutto il resto
list
= new List("EXCLUSIVE LIST ", Choice.EXCLUSIVE,
new String [] {"ONE", "TWO", "THREE"},
null);
list
= new List("MULTIPLE LIST ", Choice.MULTIPLE,
new String [] {"ONE", "TWO", "THREE"},
null);
otteniamo
liste di tipo esclusivo nel primo caso e, a selezione
multipla nel secondo.
Figura 6 - Tipi di liste : implicit, exclusive,
multiple.
Alert
Chiudiamo
la discussine sulle classi "eredi" di screen
occupandoci degli oggetti Alert.
Come già facilmente intuibile dal nome, tale
classe rappresenta un oggetto dedicato alla segnalazione
di un determinato evento all'utente.
Per creare un oggetto di tipo Alert è possibile
utilizzare uno dei seguenti costruttori:
Alert
(String alertName);
Alert (String alertName, String alertText, Image alertImage,
AlertType alertType);
nel
primo caso viene creata una Alert "vuota"
nel secondo vengono passati anche il testo, l'immagine
da visualizzare ed, infine, il tipo che può assumere
uno dei seguenti valori:
ALARM
- serve a notificare all'utente una particolare condizione(es.
il sopraggiungere di una determinata data);
CONFIRMATION
- conferma all'utente l'esecuzione di un'operazione
(es. la validità di una password inserita dall'utente);
ERROR
- segnala all'utente una condizione d'errore (es. la
non validità di una password inserita dall'utente);
INFO
- fornisce informazioni all'utente (es. la password
scadrà tra 15 giorni);
WARNING
- serve a segnalare la potenziale pericolosità
di una operazione richiesta dall'utente (es. la richiesta
di cancellazione dei dati contenuti in un database);
E
possibile ottenere e modificare il tipo di una Alert
con i metodi:
public
AlertType getType ();
public void setType (int alertType);
Il
tempo di visualizzazione di una Alert sul display può
essere impostato con il metodo:
public
void setTimeout (int timeout);
dove
timeout rappresenta il tempo di visualizzazione in millisecondi.
L'utilizzo del timeout serve a far si che dopo aver
visualizzato una Alert venga visualizzata una nuova
schermata. Ad esempio si potrebbe utilizzare una Alert
di conferma (CONFIRMATION) per indicare all'utente che
la password inserita è valida. Tale Alert potrebbe
essere visualizzata per alcuni secondi, dopodiché,
in modo automatico, verrebbe visualizzata una nuova
pagina (ad esempio i nominativi di una rubrica).
Nel caso venga passato come timeout il valore (variabile
statica) FOREVER, la dismissione dell'Alert sarà
decisa dall'utente.
Il passaggio da una Alert a una nuova schermata, può
essere ottenuto, come già detto, con il metodo
della classe Display:
public
void setCurrent(Alert alert, Displayable nextDisplayable);
dove
nextDisplayable rappresenta ciò che deve essere
visualizzato non appena l'Alert viene dismessa (per
la fine del timeout o per opera dell'utente).
Il codice seguente crea una Alert che resta "in
vita" per 5 secondi dopodiché sul display
verrà visualizzata una nuova pagina (una form
in questo caso):
.......
.......
form.setCommandListener(this);
.......
.......
Image img = null;
/** Crea un'immagine da inserire nell'Alert*/
try {
img = Image.createImage("/icons/alert.png");
} catch (Exception e) {e.printStackTrace();}
/** Costruttore dell'Alert*/
alert = new Alert(" Alert ", " ALERT
MESSAGE ", img, AlertType.WARNING);
/** Setta il timeout a 5 secondi*/
alert.setTimeout(5000);
/** Trascorso il timeout (5 secondi) verrà visualizzata
la Form form*/
Display.getDisplay(this).setCurrent(alert, form);
........
........
Il
risultato ottenuto è riportato in figura 7:
Figure 7 - Alert
I
comandi
Abbiamo
visto che ogni oggetto visualizzabile è una estensione
della classe Displayable alla quale è possibile
aggiungere e rimuovere comandi (con i metodi add /removeCommand(Command
cmd) ) e collegare listener (setCommandListener( CommadListener
lsr)).
Secondo il modello previsto dal MIDP, un evento generato
da una sorgente viene inviato al listener che provvede
a processarlo e, a gestirlo opportunamente. Per ottenere
tutto ciò è necessario che:
- la
classe implementi l'interfaccia del listener XYZ_Listener;
- il
listener sia collegato all'oggetto sorgente di eventi
( tipicamente quello visualizzato a display) con il
metodo addXYZListener( XYZ_Listener);
A
questo punto:
- il
listener XYZ_Listener è in ascolto sugli eventi
generati dalla sorgente (componente);
- il
componente può generare un evento;
- il
listener XYZ_Listener riceve l'evento e lo gestisce;
Il
MIDP prevede due differenti tipi di eventi: quelli di
tipo ItemStateChanged e quelli di tipo Command. I primi,
di cui abbiamo già parlato, riguardano esclusivamente
le Form, mentre gli eventi di tipo Command possono essere
associati a qualunque oggetto grafico che estenda Displayable.
Quando un comando associato ad un oggetto Displayable
è attivato, viene generato un evento.
Questo viene passato al listener collegato alla sorgente,
cioè all'oggetto che ha generato l'evento, per
poter essere processato. L'oggetto Command prevede il
seguente costruttore:
Command
(String commandLabel, int commandType, int commandPriority);
dove commandLabel è una stringa associata al
comando, commandType è il tipo di comando commandPriority
indica la priorità (l'importanza) di un comando
rispetto ad altri contenuti nella stessa schermata (valori
bassi indicano una alta priorità). A questo proposito
occorre precisare che, il MIDP non dispone di un layout
manager per gestire il posizionamento dei comandi. Solitamente,
se le dimensioni del display non consentono la visualizzazione
di tutti i comandi inseriti, quelli a priorità
più bassa vengono inseriti in un menu (questo
però dipende dalle varie implementazioni, quindi
la disposizione dei comandi può variare da un
dispositivo ad un altro).
La classe Command prevede alcune costanti associate
a tipi predefiniti di comandi che rappresentano azioni
(eventi) molto comuni:
·
BACK
- comando di navigazione che permette di ritornare alla
schermata precedente;
· CANCEL
- comando di cancellazione;
· OK
- comando di conferma;
· HELP
- comando per il lancio di un eventuale on-line help;
· EXIT
- comando di uscita dall'applicazione;
· SCREEN
- indica un comando che appartiene alla screen corrente
(ad esempio "load", "save");
· STOP
- comando per la terminazione di un qualche processo;
Nessuno
di questi comandi è attualmente implementato,
ma dovrà essere gestito nel metodo commandAction
ad esempio:
public
void commandAction(Command command, Displayable displayable)
{
int commandType = command.getCommandType();
switch( commandType )
{
case Command.BACK:
/** Gestisce il comando BACK*/
break;
case Command.CANCEL:
/** Gestisce il comando CANCEL */
break;
case Command.EXIT:
/** Gestisce il comando EXIT */
break;
case Command.HELP:
/** Gestisce il comando HELP */
break;
case Command.OK:
/** Gestisce il comando OK */
break;
case Command.SCREEN:
/** Gestisce il comando SCREEN */
break;
case Command.STOP:
/** Gestisce il comando STOP */
break;
}
}
L'esempio
riportato sotto mostra l'utilizzo dei comandi e la gestione
degli eventi.
package
midpexamples;
import
javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public
class LCDUIExample extends MIDlet implements CommandListener{
Form form;
/**Elenco dei comandi*/
Command exit;
Command c1;
Command c2;
Command c3;
/**Costruttore*/
public LCDUIExample() {
form = new Form("Command Test");
/**Creazione dei comadi. Hanno tutti la medesima priorità
(1)*/
exit = new Command("EXIT",Command.EXIT, 1);
c1 = new Command("CMD 1",Command.SCREEN, 1);
c2 = new Command("CMD 2",Command.SCREEN, 1);
c3 = new Command("CMD 3",Command.SCREEN, 1);
/**I comandi vengono "agganciati" all'oggetto
Displayable da visualizzare (una form in questo caso*/
form.addCommand(exit);
form.addCommand(c1);
form.addCommand(c2);
form.addCommand(c3);
form.append(new StringItem("", "Select
a command"));
form.setCommandListener(this);
}
/**Il metodo main*/
public void startApp() {
Display.getDisplay(this).setCurrent(form);
}
/**Cattura lo stato "paused" del MIDlet*/
public void pauseApp() {
}
/**Cattura lo stato "destroyed" del MIDlet*/
public void destroyApp(boolean unconditional) {
notifyDestroyed();
}
/**Cattura e gestione degli eventi generati. In base
al comando selezionato viene stampata una opportuna
stringa*/
public void commandAction(Command c, Displayable d)
{
if (c == c1) {
form.set(0,new StringItem("Selected :\n",
"COMMAND 1"));
}
if (c == c2) {
form.set(0,new StringItem("Selected :\n",
"COMMAND 2"));
}
if (c == c3) {
form.set(0,new StringItem("Selected :\n",
"COMMAND 3"));
}
if (c == exit) {
destroyApp(true);
}
}
}
L'output
generato da questo semplice programma è riportato
in figura 8:
Figura 8 - Esempio di utilizzo di comandi.
Classi grafiche e gestione degli eventi (basso livello)
Canvas
La
classe (astratta) Canvas, come visibile in figura 1,
è un'estensione della classe Displayable e implementa
le API grafiche a basso livello. Rispetto alle API ad
"alto livello" che abbiamo appena trattato,
quelle a basso livello consentono un maggiore controllo
dei dispositivi di input/output, questo, può
però tradursi in una diminuzione del "grado
di portabilità" delle applicazioni. Ogni
classe che estende Canvas deve ridefinire il metodo
astratto paint (Graphics g) al quale viene passato il
contesto grafico (Graphics g). Oltre a paint, Canvas
contiene altri metodi per il controllo del display :
public
void repaint();
public void repaint(int x, int y, int width, int height);
public void serviceRepaint();
I
primi due eseguono, rispettivamente il "refresh"
di tutta la Canvas e di una sua porzione ( a partire
dal punto x-y per un'area larga width e alta height),
mentre il terzo metodo (serviceRepaint()) impone l'immediata
esecuzione di eventuali processi di repaint rimasti
in attesa.
Anche per quel che riguarda la gestione degli eventi
la classe Canvas consente un controllo raffinato dei
dispositivi di input.
I metodi:
public
void keyPressed (int keyCode);
public void keyReleased (int keyCode);
public void keyRepeated (int keyCode);
permettono
di gestire gli eventi riguardanti rispettivamente la
pressione, il rilascio, e la pressione continuata di
un tasto. Il tasto che genera l'evento è individuato
dal valore intero keyCode. Poichè non tutti i
dispositivi generano un evento associato alla pressione
continuata di un tasto, la classe Canvas possiede il
metodo
public
boolean hasRepeatEvents();
che
permette di testare tale funzionalità.
Alcuni
dispositivi (PDA, Smart-Phone, cellulari) sono dotati
di display sensibili alla pressione (touch-screen),
Canavs dispone di un metodo per verificare la presenza
di un touch-screen:
public
boolean hasPointerEvents ();
mentre
con il metodo:
public
boolean hasPointerMotionEvents ();
è
possibile verificare se il dispositivo (touch-screen)
è in grado di rilevare lo spostamento da un punto
ad un'altro sul display.
Se
tali funzionalità sono presenti nel dispositivo,
Canvas ne permette la gestione con i metodi:
public
void pointerPressed ( int x, int y );
public void pointerReleased ( int x, int y );
public void pointerDragged ( int x, int y);
che
vengono richiamati ogniqualvolta il touch-screen viene
premuto, rilasciato o ci si sposta su di esso(i valori
interi passati come parametri indicano le coordinate
x,y del punto sul display).
Graphics
Al
metodo paint della classe Canvas viene passato come
parametro il contesto grafico, cioè un oggetto
di tipo Graphics. La classe Graphics del MIDP fornisce
semplici funzionalità grafiche in 2D, in particolare
essa permette:
-
il disegno di testo e immagini:
public
void drawChar (char c, int x, int y, int anchor);
public void drawChars (char[] c, int offset, int length,
int x, int y, int anchor);
public void drawString (String s, int x, int y, int
anchor);
public void drawChars (String s, int offset, int length,
int x, int y, int anchor);
public void drawImage(Image img, int x, int y, int
anchor);
la
variabile intera anchor indica gli anchor-point, utilizzati
per il posizionamento ottimale di testo e immagini
senza ricorrere a chiamate di metodi come stringWidth(),
charWidth(), getWidth(), ecc. Il valore che gli anchor-point
possono assumere è il risultato dell'operazione
logica OR tra le costanti orizzontali (LEFT, HCENTER,
RIGHT) e quelle verticali (TOP, BASELINE, BOTTOM).
Un esempio può essere:
g.drawString("text",10,10, Graphics.TOP
| Graphics.LEFT);
- il
disegno di linee, rettangoli e archi;
public
void drawLine (int x1, int y1, int x2, int y2);
public void drawRect (int x, int y, int width, int
height);
public void drawRoundRect (int x, int y, int width,
int height, int arcW, arcH);
public void drawArc(int x, int y, int width, int height,
int startAngle, int arcAngle);
-
il riempimento di rettangoli e archi;
in
questo caso i metodi hanno la stessa firma dei precedenti
( fillRect(..), fillRoundRect(..), fillArc(..) );
- la
gestione del colore;
con
i metodi isColor() e numColors() della classe Display
si può verificare la presenza o meno di un display
a colori e il numero dei colori o dei livelli di grigio
( nel caso non sia supportato il colore, isColor() è
"false") supportati. Il MIDP supporta una
gestione del colore a 24 bit (8 bit per ciascuna componente
RGB), il colore e le sue singole componenti sono accessibili
e modificabili con i metodi:
public
int getColor();
public int getRedComponent();
public int getGreenComponent();
public int getBlueComponent();
il
primo metodo ritorna il valore (nel formato 0xRRGGBB)
del colore corrente, i restanti ritornano un intero
che può variare tra 0 e 255 e rappresenta il
valore di ogni singola componente.
public
void setColor(int RGB);
public void setColor(int red, int green, int blue);
permettono
invece il settaggio del colore, al primo viene passato
il valore RGB nel formato 0xRRGGBB, il secondo accetta
come parametri le tre componenti separate ( i valori
interi delle componenti variano tra 0-255).
Per
quanto riguarda, invece, la "scala del grigio",
il MIDP supporta una gray-scale a 255 livelli:
public
void setGrayScale(int val);
public int getGrayScale();
permettono
di settare e ottenere il valore del livello corrente
di grigio (valore che può variare tra 0 e 255).
Un'ultima
considerazione riguarda il posizionamento degli oggetti
all'interno della Canvas. A questo proposito, occorre
tenere presente che le coordinate x, y dello schermo
variano, secondo quanto previsto dal MIDP, come riportato
in figura 9:
Figure
9 - Le coordinate X-Y di Graphics
La
classe Graphics permette la traslazione del punto di
origine, di coordinate (0,0), in un punto (x,y) utilizzando
l'apposito metodo:
public
void translate (int xTranslation, int yTranslation);
il
valore di tale traslazione rispetto all'origine è
sempre accessibile con i metodi:
public void getTranslateX();
public void getTranslateY();
che
ritornano, rispettivamente, la traslazione rispetto
all'asse x e y ( distanza dall'origine 0,0 ).
Tutte le operazioni eseguite sul contesto grafico dopo
un'operazione di traslazione diventano relative al nuovo
sistema di coordinate. L'esempio seguente mostra un
semplice utilizzo delle traslazioni:
public
class TranslationExample extends Canvas {
......
......
public void paint(Graphics g) {
/** Disegna un rettangolo di colore nero su tutta l'area
visualizzabile*/
g.fillRect(0,0, this.getWidth(), this.getHeight());
/**Inposta il colore (rosso)*/
g.setColor(255,0,0);
/**Dispone due linee ad angolo retto in posizione 0,0*/
g.drawLine(0,0,50,0);
g.drawLine(0,0,0,50);
/**Trasla il contesto grafico, portando l'origine nel
punto di coordinate (30,30)*/
g.translate(30, 30);
/**Dispone due linee ad angolo retto in posizione (0,0),
(relativo al nuovo punto di origine)*/
g.drawLine(0,0,50,0);
g.drawLine(0,0,0,50);
}
......
.....
}
L'output
generato è riportato in figura 10:
Figura 10 - Esempio di traslazione del sistema
di coordinate
In
questo caso i metodi getTranslateX e getTranslateY tornerebbero
entrambi 30 (valore della traslazione rispetto ad x
e a y).
Le
immagini
Più
volte, nella trattazione delle varie classi e dei componenti
per UI, abbiamo incontrato l'oggetto Image. Abbiamo
visto che può essere passato come parametro in
una List o in un'Alert, che può essere aggiunto
ad una Form e che la classe Graphics possiede il metodo
drawImage per il rendering di un'immagine. Vediamo ora,
più nel dettaglio, il supporto e la gestione
delle immagini nel MIDP.
Il formato attualmente supportato è il PNG (
Portable Network Graphics) nella versione 1.0 ed è
prevista la possibilità di creare due differenti
tipi di immagini:
- immutable
image: una volta create non possono essere modificate.
Si tratta di immagini create a partire da un file,
o scaricate da una rete.
-
mutable image : possono essere modificate dopo la
loro creazione, è quindi possibile disegnare
oggetti al loro interno.
Per
creare un'immagine, indipendentemente dal tipo, occorre
richiamare il metodo statico createImage, che prevede
le seguenti firme:
static
Image createImage( byte data[], int imageOffset, int
imageLength );
static Image createImage(Image imageSource);
static Image createImage(String name);
Questi
tre metodi ritornano un'immagine di tipo immutable.
Il primo metodo crea un'immagine a partire dai dati
(byte) contenuti in un array alla posizione individuata
da imageOffeset e di lunghezza imageLength. Il secondo
ritorna un'immagine a partire da un'altra immagine,
e viene utilizzata per creare una versione immutable
(quella ritornata) di un'immagine di tipo mutable (quella
passata come parametro, imageSource). Se l'immagine
passata è anch'essa "immutabile" non
viene creata nessuna nuova immagine ma viene semplicemente
ritornata quella passata come parametro. Nel terzo caso
invece l'immagine ritornata è quella individuata
dalla stringa name, che tipicamente indica il nome del
file di risorsa (file png), ad esempio:
Image
img = Image.createImage("/icons/myIcon.png");
crea
un'immagine (img) a partire dal file myIcon.png presente
nella directory icons. In quest'ultimo caso se il file
specificato non esiste o, comunque, non può essere
letto viene lanciata una IOException.
Per
creare un'immagine di tipo mutable si può utilizzare
il metodo createImage nel seguente formato:
static
Image createImage(int width, int height);
che
crea un'immagine di dimensioni width x height.
Richiamando
i metodi:
public
int getWidth();
public int getHeight();
public boolean isMutable();
è
possibile ottenere informazioni circa le dimensioni
e il tipo di un'immagine.
Il
metodo:
public
Graphics getGraphics();
infine,
crea un contesto grafico associato all'immagine, utilizzabile
per il rendering.
Questo metodo è richiamabile esclusivamente su
un'immagine di tipo mutable, mentre la chiamata su un'immagine
immutabile genera una IllegalStateException.
Riferimenti
MokaByte:
Il mondo java embedded
http://www.mokabyte.it/2002/01/j2me_1.htm
MokaByte:
La configurazione CLDC
http://www.mokabyte.it/2002/02/j2me_2.htm
Mokabyte:
Il profilo MIDP (prima parte)
http://www.mokabyte.it/2002/03/j2me_3.htm
http://developer.java.sun.com/developer/technicalArticles/wireless
Developing Wireless Applications using J2ME[tm] Technology
- Bill Day http://wireless.java.sun.com/getstart/articles/wirelessdev/wirelessdev.pdf
http://java.sun.com/products/midp/
Le
specifiche del formato png versione 1.0 (W3C)
http://www.w3.org/TR/REC-png
|