| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Struktura u₧ivatelskΘho rozhranφ
V MIDP jsou k dispozici pro kreslenφ na displej komponenty dvou zßkladnφch typ∙:
-
Nφzko·rov≥ovΘ:
- Aplikace p°φmo kreslφ na displej.
- Komponenta dostßvß udßlosti typu stisknutφ klßvesy (nebo u displej∙ ovlßdan²ch dotykov∞ udßlosti stylusu).
- Pokud nebude kreslenφ dostateΦn∞ promyÜlenΘ, stane se aplikace nep°enositelnou.
-
Vysoko·rov≥ovΘ:
- Jsou nezßvislΘ na typu a velikosti displeje.
- VeÜkerΘ konkrΘtnφ kreslenφ a definice vlastnostφ, jako je nap°. velikost fontu nebo barva Φehokoli, °eÜφ implementace javy, programßtor na n∞ nemß ₧ßdn² vliv.
Skoro bezproblΘmovß p°enositelnost programu pou₧φvajφcφho pouze vysoko·rov≥ovΘ komponenty je jist∞ pozitivnφ vlastnostφ. U telefon∙ s mal²m displejem a pouze dv∞mi barvami nebylo p°i implementaci t∞chto komponent moc co zkazit. OvÜem t°eba u takovΘho telefonu Nokia 7650 nejspφÜ zjistφte, ₧e vzhled nap°. formulß°e, kter² pat°φ mezi vysoko·rov≥ovΘ komponenty, neuspokojuje dostateΦn∞ vaÜe estetickΘ cφt∞nφ, a budete nuceni ·rove≥ "snφ₧it".
Pozn.: T°φdy, u kter²ch v nßsledujφcφm textu neuvßdφm balφk, ze kterΘho jsou, hledejte v balφku javax.microedition.lcdui.
Displej
Zßkladnφ t°φdou, kterß spravuje displej, je (p°ekvapiv∞) t°φda Display. Tato t°φda je typick² jedinßΦek (singleton). V rßmci jednoho midletu existuje prßv∞ v jednΘ instanci, kterou lze zφskat pou₧itφm statickΘ metody Display.getDisplay().
Objekty, kterΘ se zobrazujφ na displeji, musφ b²t potomkem t°φdy Displayable. T°φda Display mß na nastavenφ aktußlnφho Displayable objektu metodu setCurrent() a na jeho zφskßnφ metodu getCurrent(). Pokud je aktußlnφ Displayable objekt zobrazen na displeji, b∞₧φ aplikace na pop°edφ a dostßvß vstupnφ udßlosti z klßvesnice Φi dotykovΘho displeje. Neb∞₧φ-li aplikace na pop°edφ (b∞₧φ na pozadφ), nemß p°φstup k ₧ßdn²m vstupnφm za°φzenφm. U v∞tÜiny telefon∙ (ne-li u vÜech) ovÜem nemß smysl zab²vat se b∞hem na pozadφ, proto₧e tato za°φzenφ neumo₧≥ujφ b∞h vφce aplikacφ najednou.
P°ehled komponent
Nßsledujφcφ obrßzek ukazuje hierarchii vÜech komponent, kterΘ lze zobrazit na displej:
Jak sami vidφte, nenφ jich mnoho. Potomci t°φdy Screen pat°φ mezi vysoko·rov≥ovΘ komponenty. Chcete-li pou₧φt n∞jakou nφzko·rov≥ovou komponentu, musφte si ji sami napsat jako potomka abstraktnφ t°φdy Canvas.
Ve zbytku Φlßnku se budu v∞novat p°φkladu pou₧itφ komponenty Canvas.
P°φkazy a udßlosti
T°φda Canvas mß s vysoko·rov≥ov²mi komponentami spoleΦnΘ ovlßdßnφ pomocφ abstraktnφch p°φkaz∙. Umφst∞nφ t∞chto p°φkaz∙ je zcela na libov∙li implementßtora J2ME. Programßtor pouze p°φkaz vytvo°φ, urΦφ jeho typ, na kterΘm m∙₧e zßviset, kde je tento p°φkaz umφst∞n na displeji, a nastavφ posluchaΦe na jeho udßlosti. Tento posluchaΦ se nastavuje metodou setCommandListener(CommandListener listener) a m∙₧e b²t (narozdφl od standardnφ Javy) pouze jeden.
Na odchytßvßnφ nφzko·rov≥ov²ch udßlostφ mßme k dispozici n∞kolik metod, kterΘ majφ vÜechny v t°φd∞ Canvas prßzdnou implementaci, tak₧e pokud nßs tyto udßlosti nezajφmajφ, nemusφme d∞lat v∙bec nic. Pro nßs asi nejzajφmav∞jÜφ (a pou₧itß v p°φkladu) je metoda keyPressed(int keyCode). Tuto metodu vyvolß stisk klßvesy, jejφ₧ k≤d dostane jako parametr, a obvykle bude jejφ implementace obsahovat p°φkaz switch, kter² podle k≤du klßvesy rozhodne, co dßl. VÜechny pou₧itelnΘ k≤dy klßves jsou statickΘ prom∞nnΘ t°φdy Canvas a zaΦφnajφ °et∞zcem "KEY".
Aby to nebylo na prvnφ pohled tak jednoduchΘ, obsahuje Canvas jeÜt∞ druhou ·rove≥ k≤d∙ klßves, naz²vanou hernφ akce (game actions). Tyto akce jsou namapovßny na klßvesy, kterΘ mß telefon k dispozici, tak₧e mohou b²t u ka₧dΘho telefonu namapovßny jinak v zßvislosti na jeho klßvesnici. Nap°φklad akce nahoru je u telefon∙ se Üipkou nahoru p°i°azena tΘto Üipce, u jin²ch telefon∙ klßvese s Φφslem 2. Odpovφdajφcφ hernφ akci zjistφme metodou getGameAction(int keyCode). Definovßny jsou hernφ akce UP, DOWN, LEFT, RIGHT, FIRE, GAME_A, GAME_B, GAME_C a GAME_D
KreslφÜ, kreslφm, kreslφme
R∙znΘ telefony jsou r∙zn∞ rychlΘ. ObzvlßÜt∞ v rychlosti kreslenφ na displej m∙₧e b²t u aplikacφ s velk²m mno₧stvφm grafick²ch operacφ kßmen ·razu, proto je d∙le₧itΘ tyto operace optimalizovat, aby jich bylo co nejmΘn∞.
V nßsledujφcφm p°φkladu jsem pou₧ila metodu dvojitΘho zßsobnφku (double buffering û pokud znßte n∞kdo n∞jak² vhodn² Φesk² termφn, kter² se pou₧φvß, rßda se nechßm pouΦit). V tomto p°φklad∞ to sice zrovna nenφ pot°eba, ale na p°ekreslovßnφ slo₧it∞jÜφch obrßzk∙ na n∞kter²ch telefonech by se bez kreslenφ nejprve do "zßlo₧nφho obrßzku" a pak teprve na displej nedalo dφvat.
Celß t°φda na zobrazenφ mince
import java.util.Random; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.CommandListener; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.Image; public class CoinCanvas extends Canvas implements CommandListener { /* * Nßzvy obrßzk∙ s rubem a lφcem mince. Tyto obrßzky * se musφ nachßzet v ko°eni JAR souboru, do kterΘho * aplikaci zabalφte. Obrßzky, kterΘ jsem pou₧ila jß, * jsou tak ÜpatnΘ kvality, ₧e jsem je rad∞ji ani * k Φlßnku nep°ilo₧ila. Jist∞ si namalujete * n∞jakΘ lepÜφ. */ private static final String panna = "/panna.png"; private static final String orel = "/orel.png"; CoinMIDlet midlet; Command cmdExit; Command cmdThrow; Image[] coin = new Image[2]; Random rand; int value = 0; Image image; Graphics buffer; /** * V konstruktoru se inicializujφ vÜechny prom∞nnΘ, * p°idajφ p°φkazy a nastavφ instance t°φdy typu * CommandListener, kterß zpracovßvß udßlosti p°φkaz∙. */ public CoinCanvas(CoinMIDlet midlet) { this.midlet = midlet; rand = new Random(); try { coin[0] = Image.createImage(panna); coin[1] = Image.createImage(orel); } catch (Exception e) { e.printStackTrace(); } image = Image.createImage(getWidth(), getHeight()); buffer = image.getGraphics(); buffer.setColor(0, 0, 0); buffer.fillRect(0, 0, getWidth(), getHeight()); buffer.setColor(255, 255, 255); cmdExit = new Command("Konec", Command.EXIT, 2); addCommand(cmdExit); cmdThrow = new Command("Hazej", Command.SCREEN, 1); addCommand(cmdThrow); setCommandListener(this); } /** * Nakreslφ do danΘ grafiky podle hodnoty parametru sign * bu∩ lφc nebo rub mince. */ private void drawCoin(Graphics g, int sign) { g.drawImage( coin[sign], getWidth() / 2, getHeight() / 2, Graphics.VCENTER | Graphics.HCENTER); } /** * Metoda, kterß vykresluje celou komponentu. */ protected void paint(Graphics g) { drawCoin(buffer, value); g.drawImage( image, 0, 0, Graphics.TOP | Graphics.LEFT); } /** * Metoda rozhranφ CommandListener. Zavolß se, kdy₧ * u₧ivatel aktivuje n∞jak² p°φkaz. */ public void commandAction(Command c, Displayable d) { if (c.equals(cmdThrow)) { value = Math.abs(rand.nextInt()) % 2; repaint(); } else if (c.equals(cmdExit)) { try { midlet.destroyApp(true); } catch (Exception e) { } midlet.notifyDestroyed(); } } /** * Tato metoda je zavolßna, stiskne-li u₧ivatel n∞jakou * klßvesu. K≤dy klßves najdeme jako konstanty t°φdy * Canvas. V naÜem p°φpad∞ namapujeme nov² hod * kostkou na klßvesu s Φφslem 5. */ protected void keyPressed(int key) { if (key == Canvas.KEY_NUM5) { commandAction(cmdThrow, this); } } }
Midlet
import javax.microedition.lcdui.Display; import javax.microedition.midlet.MIDlet; import javax.microedition.midlet.MIDletStateChangeException; public class CoinMIDlet extends MIDlet { private Display display = null; protected void startApp() throws MIDletStateChangeException { /* * Podle toho, ₧e je display==null, poznßme, * zda se aplikace spouÜtφ, nebo vracφ * z pasivnφho stavu. */ if (display == null) { display = Display.getDisplay(this); display.setCurrent(new CoinCanvas(this)); } } protected void pauseApp() { } protected void destroyApp(boolean arg0) throws MIDletStateChangeException { } public final Display getDisplay() { return display; } }
A jak to vypadß v emulßtoru?
Na nßsledujφcφch obrßzcφch m∙₧ete porovnat odliÜnost t°φdy Canvas u r∙zn²ch v²robc∙. Oba uvedenΘ telefony majφ stejnou v²Üku displeje, ale u Nokie ji nem∙₧e programßtor pou₧φt celou, proto₧e spodnφch 10 pixel∙ zabφrajφ p°φkazy. Ve specißlnφm API pro telefony firmy Nokia je sice t°φda FullCanvas, kterß mß k dispozici cel² displej, ale jejφm pou₧itφm je aplikace omezenß pouze na telefony jednΘ firmy.