Naprogramujte si mobil

Část 3. Nízkoúrovňové uživatelské rozhraní V tomto dílu seriálu o programování v J2ME budeme pokračovat v popisu ...


Část 3. Nízkoúrovňové uživatelské rozhraní
V tomto dílu seriálu o programování v J2ME budeme pokračovat v popisu práce s
nízkoúrovňovým uživatelským rozhraním. Naučíme se tedy pracovat s displejem a
grafikou i namapovat klávesy, přičemž si vše ukážeme na konkrétních příkladech.
V minulém čísle jsme si popsali metody třídy Display, s jejichž pomocí daný
objekt získá odkaz na objekt typu Display a displej telefonu převezme. Třída
Displayable nabízí objektům svých potomků nejenom možnost kreslení na displej,
ale také možnost definovat sadu příkazů, které jsou instancemi třídy Command, a
reakci na ně.
Nyní už víte vše potřebné k tomu, abyste mohli definovat třídu DieCast_1, která
v konstruktoru nastaví svůj objekt jako vlastníka displeje MIDletu předaného v
parametru a sama sebe nastaví jako posluchače dvou příkazů: dalšího hodu
kostkou a ukončení MIDletu. Aby vyšla co nejjednodušší, je v následujícím
prográmku definována jako potomek třídy ShowDie_1, která má na starosti vlastní
vykreslení kostky a která je potomkem třídy Displayable. Celkovou strukturu
programu si můžete prohlédnout na obrázku.
import java.util.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
class DieCast_1
extends ShowDie_1
implements CommandListener {
//Abych mohl požádat MIDlet o ukončení
private static DieMIDlet_1 midlet;
private static Command cmdQuit;
private static Command cmdNext;
DieCast_1( DieMIDlet_1 midlet ) {
this.midlet = midlet;
cmdQuit = new Command( "Quit", Command.
EXIT, 1);
cmdNext = new Command( "Next", Command.OK, 0);
addCommand( cmdQuit );
addCommand( cmdNext );
setCommandListener( this );
Display.getDisplay( midlet ).setCurrent( this );
cast();
}//DieCast_1( MIDlet midlet )
public void commandAction(Command c, Displayable d){
if( c == cmdQuit ) {
//Výjimka nemůže nastat, ale překladač to neví
try{ midlet.destroyApp(true); }
catch( Exception e ) { e.printStackTrace(); }
}
else if( c == cmdNext ) {
cast();
}
}//protected void keypressed( int code )
private static final int MaxN = 6;
private static Random rnd = new Random();
int cast() {
n = (rnd.nextInt()&0X7fffffff) % MaxN + 1;
repaint();
return n;
}//int cast()
}//class DieCast_1 extends ShowDie_1

Grafika
Abyste si mohli prohlédnout program celý, podíváme se nyní na grafické možnosti
MIDletů. Jak jistě odhadnete, nejsou nijak závratné. Na druhou stranu vás jistě
potěší, že se princip jejich funkce nějak významně neliší od principů, které
znáte z knihovny AWT (Abstract Window Toolkit), proto budu stručný.
Chcete-li kreslit, potřebujete nějaké plátno. Tímto plátnem musí být instance
třídy, jež je potomkem abstraktní třídy Canvas (plátno), která je pak potomkem
třídy Displayable. Stejně jako v AWT musí vaše "plátno" překrýt její abstraktní
metodu protected abstract void paint(Graphics g),
kterou systém zavolá pokaždé, když se rozhodne překreslit obrazovku. Odkaz na
objekt typu Graphics, který tato funkce dostává jako parametr, můžete chápat
jako nástroj, jímž budete kreslit. Umožní vám kreslit plné a čárkované čáry,
vyplněné či nevyplněné obdélníky a eliptické výseče, vykreslovat texty a
obrázky ve formátu PNG, nastavovat i zjišťovat barvy a několik dalších funkcí,
které při tvorbě finálních obrazovek využijete. Nerozebírám je tu podrobněji,
protože jsou podobné funkcím z knihovny pro J2SE.
Dospějete-li k závěru, že máte připravenou novou podobu svého plátna, a chcete
ji vykreslit, zavoláte metodu public final void repaint().
Systém však (obdobně jako v AWT) nemusí zareagovat okamžitě vaše plátno
překreslí, až "bude mít náladu". Většinou si ale zdržení vůbec nepovšimnete.

Klávesy
Třída Canvas je reprezentantem nízkoúrovňového vstupu a výstupu. Umožňuje
nejenom kreslit a zadávat reakce na příkazy (je přece potomkem Displayable),
ale dokonce i reakce na stisk či puštění jednotlivých kláves. K tomu slouží
funkce protected void keyPressed(int keyCode),
protected void keyRepeated(int keyCode),
protected void keyReleased(int keyCode),
které systém volá po stisku, přidržení (autorepeat) či puštění klávesy, jejíž
kód předá volané funkci jako parametr. Třída definuje kódy kláves "0" až "9",
"#" a "*" (udivilo mne, že autoři návrhu zapomněli na klávesu Delete). Kromě
kódů kláves zná třída také akce, kterých definuje celkem devět: UP, DOWN, LEFT,
RIGHT, FIRE a čtyři víceúčelové akce GAME_A až GAME_D. Jejich přiřazení záleží
na konkrétním zařízení. Některý mobil má pro ně vyhrazeny vlastní klávesy, jiný
je namapuje na číselné klávesy. Protože je může mít každý přístroj namapované
jinak, je vhodné je v programu nemíchat a používat buď klávesy, nebo akce, aby
se vám nepohádaly.
Jednoduchý mikroprográmek, kterým si můžete vyzkoušet namapování akcí na
jednotlivé klávesy, by mohl vypadat např. následovně:
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class KBD extends MIDlet {
public void startApp() {
new KW( this );
}//public void startApp()
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
}//public class KBD extends MIDlet
class KW extends Canvas {
KW( MIDlet m ) {
Display.getDisplay( m ).setCurrent(this);
}//KW( MIDlet m )
protected void keyPressed(int key) {
System.out.println("Key=("+key+") " + getKeyName(key) + " # Action=" +
getGameAction(key) );
}//protected void keyPressed(int key)
protected void paint (Graphics g) {};
}//class KW extends Canvas

Všimněte si v něm, že jsme museli převzít displej, i když jsme na něj nic
nekreslili (veškeré tisky šly na standardní výstup, který se na telefonu
nezobrazuje). Převzetím displeje totiž oznamujeme operačnímu systému, komu má
posílat příslušné události k zareagování.
Následující torzo programu ukazuje, jak bychom mohli přizpůsobit kostku, o níž
jsem hovořil v minulém dílu, tak, aby mohla házet čísla v různých rozsazích.
Zároveň umožníme házet stiskem některého z tlačítek, a zjednodušit tak ovládání
na telefonech, které nenabízejí možnost přímého zadání příkazů.

private static Random rnd = new Random();
static int MinN = 1; //Implicitní minimum
static int MaxN = 6; //Implicitní maximum
static int min = MinN; //Aktuální minimum
static int max = MaxN; //Aktuální maximum
private static int mmm = max min + 1; //Interval
int cast() {
n = (rnd.nextInt()&0X7fffffff) % mmm + min;
repaint();
return n;
}//int cast()
protected void keyPressed( int keyCode ) {
switch( getGameAction(keyCode) ) {
//Fire vyvolá další hod
case FIRE: cast(); break;
//Šipky nastavují házení malých, resp. velkých čísel
case UP: min = MaxN/2; max = MaxN; break;
case DOWN: min = MinN; max = MaxN/2; break;
default: //Klávesy neměly přiřazenu hlídanou akci
switch( keyCode ) {
case KEY_STAR: //Hvězdička resetuje nastavení
min=MinN=1; max=MaxN=9; break;
case KEY_NUM0: //Nula přepíná povolení hodu nuly
min = MinN ^= 1; break;
//Zbylá čísla nastavují hoditelné maximum
case KEY_NUM2: case KEY_NUM3: case KEY_NUM4:
case KEY_NUM5: case KEY_NUM6: case KEY_NUM7:
case KEY_NUM8: case KEY_NUM9:
min = MinN;
max = MaxN = (keyCode KEY_NUM0);
}//switch( keyCode )
}//switch action
mmm = max min + 1;
}//protected void keyPressed( int keyCode )
Všimněte si, že po každém hodu (cast) program požádá o překreslení displeje
(repaint).
Příště si ukážeme možnosti formulářového uživatelského rozhraní.
Autor pracuje jako EDU Expert ve firmě Amaio Technologies.

Zdrojové texty
Zdrojové kódy programů v tomto seriálu si můžete stáhnout na adrese http://www.
pecinovsky.cz/cw/pgm-mobil.zip









Komentáře
K tomuto článku není připojena žádná diskuze, nebo byla zakázána.