MokaByte 63 - Maggio 2002 
Il mondo java embedded
III parte: Grafica e gestione delle interfacce utente nel
Mobile Information Device Profile (MIDP)
di
Marco Tomà
Continuiamo, in questo articolo, ad occuparci del profilo MIDP (Mobile Information Device Profile), prendendo in esame il package javax.microedition.lcdui al quale è stata delegata la gestione della grafica e dell'interazione con l'utente.

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

 
MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it