Aplikace s grafickým uživatelským rozhraním, případně jinak užívající grafiku,
se v Javě tvoří s lehkostí a elegancí. Určitou daní za to jsou poněkud vyšší
nároky na procesor a hlavně na paměť - ty však lze vhodným návrhem a implementací
podstatně omezit. Právě proto máme dobrý důvod tvořit a používat grafické
aplikace v Javě, mají nám totiž skutečně co nabídnout.
24.4.2006 06:00 | Lukáš Jelínek | přečteno 79208×
Když Java vznikala, její tvůrci ušili grafickou část velice horkou jehlou. Původní návrh (jak API, tak "střev" grafických balíků) byl hodně nedokonalý, neumožňoval snadné a systematické využití. Proto se již s JDK 1.0 vytvořila u vývojářů jistá averze vůči javovské grafice. V dalších verzích však docházelo k neustálému zlepšování, takže se Java brzy stala vhodnou platformou pro tvorbu grafických aplikací.
Grafiku jako takovou a GUI nelze v Javě dost dobře oddělovat. Existují prostě pohromadě, využívají stejné třídy. Proto je v tomto i dalších dílech seriálu budu chápat jako jediný celek. Na odlišnosti včas upozorním.
První implementací grafiky byla knihovna zvaná Abstract Windowing Toolkit (AWT). Objevila se hned na začátku (JDK 1.0) a její nepříjemné a nesystematické API (byť s označením deprecated) přetrvává v Javě dodnes. Něco se stále používá, ale jsou to spíš věci související s obecnou grafikou, a nikoli s GUI. V dalších verzích bylo sice API přepracováno a podstatně rozšířeno, ale jiné nevýhodné vlastnosti přetrvaly.
Filosofie AWT byla taková, že každá komponenta v Javě měla svůj nativní protějšek v systému. Byl to tedy podobný model, jako dnes používají některé GUI platformy v C/C++. I když se jednalo o abstraktní (platformově nezávislé) rozhraní, přenositelnost byla problematická, vzhledem k rozdílným vlastnostem nativní úrovně GUI.
Od verze 1.2 máme v Javě novou grafickou knihovnu zvanou Java Foundation Classes (JFC), známou spíše pod názvem Swing (dále již budu používat jen toto označení). Původní koncept byl zavržen, Swing byl vytvořen prakticky kompletně na zelené louce (i když v sobě obsahuje AWT API). Základní vlastnosti lze shrnout do těchto bodů:
Uvedené vlastnosti, pro programátory velice příjemné, mají druhou stránku věci - relativně vysoké nároky na rychlost procesoru a hlavně na paměť. V reakci na to vznikly grafické knihovny, které se snaží tento problém odstraňovat. Nejvýznamější z nich je SWT.
SWT (the Standard Widget Toolkit) je GUI knihovna s podobnými vlastnostmi, jako má Swing, ovšem s nativní implementací grafických komponent. Kdo používá IDE Eclipse nebo nějakou aplikaci na této platformě postavenou, s SWT se už setkal. Proto též zajisté zjistil, že původní cíl vzniku, tedy snížení procesorové a paměťové náročnosti, příliš naplněn nebyl a srovnatelné aplikace postavené na SWT jsou zhruba stejně náročné jako ty používající Swing.
Navrhovat a tvořit GUI pro javovské programy lze samozřejmě i bez nějakých speciálních programů - návrh se připraví na papíře a příslušný kód se vytváří přímo, bez pomocných nástrojů. Není to sice nijak zvlášť těžké a mnoho vývojářů tak běžně pracuje (já občas také), ale pohodlná cesta to není. Pomocnou ruku nám podají různá vývojová prostředí (IDE), která obsahují kromě jiného i prostředky pro návrh GUI.
Po dřívějších neslavných výsledcích (z NB 3.x jsme leckdy získali nepříliš fungující GUI) se návrh GUI v tomto IDE velice zlepšil. Zejména s poslední verzí se pracuje výborně. V grafickém prostředí si lze vše snadno sestavit, kostra kódu se vygeneruje automaticky a implementaci dopíšeme. Generované části kódu jsou chráněny proti úpravám - to je současně výhoda i nevýhoda, ostatně na to každý brzy přijde.
Nejnovější verze (2006) umí vynechat fázi generování kódu z návrhu GUI a vytvořit místo toho přímo hotové třídy. To je výhodné pro rutinní práci, pro cvičné účely je ovšem mnohem lepší nechat si kód vygenerovat. Práce s JBuilderem je o něco snazší než s NetBeans (více podpůrných funkcí pro tvorbu GUI), ale podle mého názoru je tento program (právě z tohoto důvodu) pro začátečníky méně vhodný.
Ať už si zvolíte ten či onen program (existuje ještě řada dalších), vždy mějte v počítači dostatek fyzické paměti. Rychlost procesoru není tak podstatná, ale na paměti opravdu záleží.
V celém následujícím výkladu (v této i dalších kapitolách) budu vše vztahovat zásadně k technologii Swing. Jsou k tomu dobré důvody:
Samozřejmě, kdo se dobře naučí Swing, nebude mít problém přejít na SWT, případně na některou jinou implementaci GUI - hlavní vlastnosti se příliš neliší. Taktéž se zde hodí znalosti s programováním GUI v jiném jazyce (např. C++ a knihovny Qt).
Jak už je v moderních GUI zvykem, fungování aplikací je řízené událostmi. Na programátorovi je, aby vyřešil reakce na tyto události. Ve Swingu to funguje tak, že je zde smyčka obsluhující frontu asynchronních událostí (ty přicházejí z okenního systému, např. X11), a odtud se volá obsluha těchto událostí. Obslužné rutiny většinou volají další reakční metody vyšší úrovně, až se nakonec obsluha události dostane do aplikace ke konečnému zpracování.
Pokud někdo např. najede myší na tlačítko, do fronty událostí se přidá událost o pohybu myši. V obslužné smyčce se událost odebere a zavolá se metoda pro její obsluhu. Tam se zjistí, že se kurzor myši dostal nad tlačítko, proto se vytvoří nové události (pro pohyb myši a pro najetí do prostoru) a ty se "pošlou" příslušnému tlačítku. Tlačítko každou z událostí zpracuje a nějak na ně zareaguje - a potom vrátí řízení zpět, a tak se vše postupně dostane zase až do obslužné smyčky. Detaily teď neuvádím, dostaneme se k nim později.
Co nás na tom hlavně zajímá? Obsluha každé události funguje tak, že máme rozhraní pro reakci na událost, a toto rozhraní se nastavuje ve zdroji příslušné události. Většinou může být více implementací rozhraní (a tedy více reakcí) současně na stejnou událost, metody se zavolají postupně.
Nebyla by to grafika, kdybychom nemohli nic nakreslit. Základní principy opět nejsou nic neobvyklého. Ke kreslení používáme tzv. grafický kontext, což je nějaká implementace abstraktního kreslicího rozhraní. O jakou konkrétní implementaci se jedná, nás často vůbec nezajímá - proto lze naprosto stejně kreslit na obrazovku, do paměti nebo na tiskárnu.
Při kreslení můžeme využívat širokou škálu nástrojů, které jsou ve standardní knihovně k dispozici. Je dobré je používat do té míry, jak je to jen možné, protože ve vztahu ke grafickému kontextu většinou poskytují maximální možnou optimalizaci.
Než přejdeme k praxi, dovolte mi ještě připomenout několik důležitých zásad, bez kterých nelze rozumně programovat GUI v Javě. Opomenutí pak často vede k právě takovým programům, kvůli kterým mnozí lidé Javu zavrhují.
invokeLater()
a invokeAndWait()
, ještě o nich bude řeč. Je to proto, že mnohé grafické
třídy neobsahují (z výkonnostních důvodů) žádnou synchronizaci.
toString()
, která poskytne textovou reprezentaci nějakého objektu a umožní
pracovat s referencemi na data. Potom je např. položka v seznamu spjata
s konkrétní instancí datového objektu a nemusí se nikam nic kopírovat.
Tak to byla šedá a nudná teorie, proto se pusťme do první aplikace s GUI.
Bude obsahovat pouze jediné okno pevné velikosti, a na něm bude tlačítko Konec. Po stisknutí
tlačítka se - pochopitelně - aplikace ukončí. Vytvoříme si třídu MyDlg
, která
bude odvozena od třídy javax.swing.JDialog
(třída pro dialogová okna).
public class MyDlg extends JDialog implements ActionListener { private JButton but = new JButton("Konec");
Deklarujeme tlačítko pro umístění na panel, hned ho též inicializujeme. Šlo by použít i jinou cestu, nedeklarovat členskou proměnnou a tlačítko vytvořit až později. To by byl ovšem tento objekt později obtížně přístupný, proto je zvykem ve většině případů grafické objekty deklarovat v rámci třídy. Všimněte si implementovaného rozhraní - umožňuje zpracovávat události z tlačítka.
public MyDlg() { super(); setSize(200, 200); setLocation(100, 100); setResizable(false); setModal(true); setTitle("První GUI"); but.setSize(80, 30); but.setLocation(60, 85); getContentPane().setLayout(null); getContentPane().add(but); but.addActionListener(this); setDefaultCloseOperation(DISPOSE_ON_CLOSE); }
V konstruktoru zavoláme konstruktor rodičovské třídy - u tříd tohoto druhu to děláme vždy, pokud nemáme dobrý důvod tak neučinit. Pak se nastaví rozměry dialogu a jeho pozice na obrazovce, rozměry a relativní pozice tlačítka. Zakážeme změnu velikosti a určíme, že dialog má být modální (tedy že dokud je otevřen, komunikuje se s uživatelem pouze přes něj; zde je to nutnost, protože jinak by program hned skončil, nemáme žádné hlavní aplikační okno). Další dva řádky vypnou správce rozložení (layout manager) a vloží tlačítko na plochu dialogu.
Tím je hotovo sestavení dialogu, nyní je potřeba nastavit zpracování událostí od
tlačítka. Než se k tomu ale dostaneme, upozorním ještě na poslední řádek.
Dialog má totiž zavírací tlačítko okna - a to ve výchozím stavu nedělá vůbec nic.
Protože uživatel logicky očekává, že se mu po stisku okno zavře, musíme nastavit
výchozí zavírací operaci, a tou bude samozřejmě zrušení okna (konstanta
DISPOSE_ON_CLOSE
).
public void actionPerformed(ActionEvent e) { dispose(); }
Protože události od tlačíka budeme zpracovávat přímo ve třídě MyDlg
, je nezbytné
implementovat rozhraní java.awt.event.ActionListener
a tedy vytvořit příslušnou
metodu actionPerformed
. Obsahuje jediné volání - zrušení dialogu.
Máme tedy hotovo vše, co je potřeba k funkci aplikace. Tedy až na hlavní metodu. V ní se pouze vytvoří instance naší třídy a dialog se zobrazí. V metodě vidíte, že manipulace s GUI je přenechána event-dispatching threadu.
public static void main(String args[]) { SwingUtilities.invokeLater(new Runnable() { public void run() { MyDlg dlg = new MyDlg(); dlg.setVisible(true); } }); }
Potřebné importy (a také uzavírací závorku třídy) jsem neuvedl, pro fungování
programu je samozřejmě nezbytné je doplnit, případně pracovat s úplnou
specifikací tříd. Po kompilaci hotový program obvyklým způsobem spustíme:
java MyDlg
. Měl by se zobrazit dialog s tlačítkem. Po stisku tlačítka (nebo
uzavíracího tlačítka) okna se dialog zavře a program skončí.
I když z uvedeného příkladu jednoduchého GUI programu je zcela zřejmé, že nejde o nic těžkého, přesto se vyplatí dobře poznat některé třídy a rozhraní. Příští díl bych chtěl věnovat těmto základním "stavebním kamenů" a jejich použití. O praktické příklady nebude nouze.