LINUXSOFT.cz
Nazwa użytkownika: Hasło:     
    CZ UK PL

> Java (24) - úvod do grafiky a GUI

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 | czytane 79192×

RELATED ARTICLES KOMENTARZE   

Krátký výlet do historie

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.

AWT

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.

Swing

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ů:

  • Kompletní implementace v Javě. GUI není napasováno na nativní komponenty, vše je zcela platformově nezávislé.
  • Intenzivní využití dědičnosti a kompozice. Komponenty GUI využívají objektové vlastnosti Javy, složitější součásti jsou složeny či zděděny z jednodušších.
  • Výrazné využití rozhraní. Různé operace v komponentách GUI (kreslení, editace apod.) se provádějí přes rozhraní. Standardní implementace lze nahrazovat vlastními a měnit tak chování komponent.
  • Důsledné oddělení funkcionality od vzhledu GUI (look & feel). Témat vzhledu je několik k dispozici přímo ve standardní knihovně, další si lze vytvořit a použít.
  • Oddělení dat od objektů GUI u složitějších komponent (strom, tabulka) pomocí tzv. modelů. To opět zlepšuje možnosti přizpůsobení a také opakovanou použitelnost komponent.

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

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.

V čem tvořit?

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.

NetBeans

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.

JBuilder

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ží.

Principy javovské grafiky a GUI

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:

  • Swing je součástí standardní instalace Javy.
  • Velká platformová nezávislost a přenositelnost.
  • Kvalitně navržené a systematické API.
  • Větší počet existujících aplikací - více možností k učení se ze zdrojových kódů.

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).

Principy fungování GUI

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ě.

Jak se kreslí

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.

Zásady pro tvorbu GUI

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í.

  • Nevytvářet zbytečně nové instance, vytvořené používat opakovaně. Zejména se to týká polí. Vytváření a rušení objektů je jednak dost náročné, a hlavně se rychle zaplňuje paměť, kterou garbage collector uvolňuje jen zvolna.
  • Nic by se nemělo zbytečně překreslovat. Využívejte optimalizační možnosti a nekreslete to, co určitě není vidět. Mnoho věcí si lze také připravit předem a pak najednou zobrazit, namísto ustavičného překreslování.
  • Do objektů grafiky a GUI se smí sahat jen z jednoho jediného vlákna, zvaného event-dispatching thread. Je to to vlákno, které obsluhuje frontu událostí. Pokud potřebujete něco provést z jiného vlákna, jsou na to metody 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.
  • Neblokujte event-dispatching thread výpočetními operacemi nebo třeba čekáním na data ze sítě, výkonný kód umístěte do jiných vláken (i když to znamená dost práce navíc). Nemělo by se stát, že aplikace kvůli nějaké zdlouhavé operaci zatuhne na dobu delší než stovky milisekund - uživatelé bývají velice netrpěliví.
  • Pokud to jde, používejte pro obsluhu událostí normální (opakovaně používané) pojmenované třídy namísto "chrlení" tříd anonymních, k čemuž někdy svádí vývojová prostředí.
  • Důsledně se vyhýbejte zavrženým (deprecated) metodám, i když se chovají úplně stejně jako metody modernější. Používání zastaralých metod znepřehledňuje kód (důvodem k zavržení je často právě nevhodné pojmenování, vzniklé v dobách JDK 1.0) a hlavně může v budoucnu způsobit nefunkčnost programu, když budou příslušné metody odstraněny.
  • Ošetřujte výjimky co nejblíže místu vzniku, ale ne ve větším rozsahu, než je nutné. Nezachycené asynchronní výjimky totiž nezpůsobí ukončení programu, budou odchyceny v hlavní smyčce fronty událostí. Proto také při podezřelém chování programu s GUI vždy nahlédněte do standardního chybového výstupu, zda tam není informace o výjimce (včetně výpisu zásobníku volání).
  • Snažte se maximálně oddělovat data od GUI, ale současně zachovávejte logickou konzistenci. Tam, kde se používají modely, je to snadné. V ostatních případech (seznamy apod.) se s výhodou používá metoda 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.
  • U složitějších grafických struktur (stromy) zobrazujících velké množství dat zvažte, zda je v daném případě lepší vybudovat celou strukturu na začátku, anebo ji budovat podle činnosti uživatele.
  • Mějte na paměti, že zavřené okno se nerovná zrušené okno. Pokud okno nezrušíte, bude existovat dál, se všemi výhodami a nevýhodami. Bude-li nějaký program tvrdošíjně odolávat pokusům o ukončení, možná někde zůstává zavřené nezrušené okno. Zruší-li se všechna okna a program už nemá nic jiného na práci, skončí.

První grafická aplikace

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čí.

Stavební kameny

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.


KOMENTARZE
GUI frameworky 24.4.2006 11:35 Lukáš Zapletal
L Re: GUI frameworky 24.4.2006 12:20 Petr Zajíc
  L Re: GUI frameworky 24.4.2006 14:23 Lukáš Jelínek
Rada s vyvojem projektu 26.4.2006 08:13 Aleš Dostál
|- Re: Rada s vyvojem projektu 29.4.2006 12:34 Lukáš Jelínek
L Re: Rada s vyvojem projektu 2.5.2006 16:13 by default
  L Re: Rada s vyvojem projektu 3.5.2006 14:40 Aleš Dostál
styska se mi... 4.6.2006 21:32 Jirka Hrbek
Tylko zarejestrowani użytkownicy mogą dopisywać komentarze.
> Szukanie oprogramowania
1. Pacman linux
Download: 4850x
2. FreeBSD
Download: 9044x
3. PCLinuxOS-2010
Download: 8541x
4. alcolix
Download: 10915x
5. Onebase Linux
Download: 9631x
6. Novell Linux Desktop
Download: 0x
7. KateOS
Download: 6219x

1. xinetd
Download: 2382x
2. RDGS
Download: 937x
3. spkg
Download: 4692x
4. LinPacker
Download: 9918x
5. VFU File Manager
Download: 3173x
6. LeftHand Mała Księgowość
Download: 7171x
7. MISU pyFotoResize
Download: 2775x
8. Lefthand CRM
Download: 3540x
9. MetadataExtractor
Download: 0x
10. RCP100
Download: 3087x
11. Predaj softveru
Download: 0x
12. MSH Free Autoresponder
Download: 0x
©Pavel Kysilka - 2003-2024 | mailatlinuxsoft.cz | Design: www.megadesign.cz