LINUXSOFT.cz Přeskoč levou lištu
Uživatel: Heslo:  
   CZUKPL

> Java (30) - Look and Feel

Ne vždy se tvůrci nebo uživateli grafické aplikace v Javě zamlouvá výchozí vzhled GUI. Není ovšem problém vybrat si vzhled jiný, nebo si dokonce vytvořit svůj vlastní.

13.12.2006 10:00 | Lukáš Jelínek | Články autora | přečteno 24562×

Princip ovládání vzhledu

Původní grafické rozhraní javovských aplikací, tedy AWT, využívalo k vykreslování nativní grafické objekty. Možnosti měnit jejich vzhled byly tedy jednak dost omezené, ale hlavně silně platformově závislé. S grafickým frameworkem Swing nastala podstatná změna. Vzhled komponent GUI je zcela samostatný, není svázán s jednotlivými objekty a není determinován platformou, na které aplikace běží (až na malé výjimky, které později zmíním).

Lze si to představit tak, že instance grafického objektu obsahuje všechny potřebné parametry k vykreslení dané komponenty, ale místo aby sama kreslila, deleguje tuto činnost na objekt nějakého grafického motivu. A tento "delegát" použije zase své parametry pro kreslení a složením dohromady vznikne výsledný vzhled.

Mohu uvést příklad. Máme instanci třídy JButton, tedy tlačítko. Tento objekt obsahuje údaje o tom, jak je objekt velký, kde je umístěn a zda je tlačítko stisknuté či nikoli (a také zda je/není viditelné, aktivní, má fokus apod.). Neobsahuje už ale žádné informace, jakou barvu má mít pozadí, jakou text, jaké písmo (font, řez, velikost) použít, jak nápis umístit, jak indikovat deaktivaci nebo fokus atd. Přesněji řečeno tam takové informace uloženy jsou, ale hlavně z důvodů zpětné kompatibility. Toto vše je totiž záležitost objektu konkrétního grafického motivu, tedy "look&feel". Každý vzhled může vnitřně podporovat i různá témata, jak si za chvíli ukážeme. Celý systém se nazývá Pluggable Look And Feel (PLAF).

Výchozí vzhled, změna vzhledu uživatelem

Pokud neurčíme jinak, bude spuštěná swingovská aplikace používat výchozí vzhled, tedy ten, který určili vývojáři Swingu. Většinou to bude vzhled označený jako Metal, který má v Javě 5.0 o něco lepším základní téma Ocean, kdežto to původní se nyní jmenuje Steel. Tento výchozí vzhled je k dispozici všude, kde jsou třídy frameworku Swing, na všech platformách.

Kromě základního vzhledového motivu existují ještě další. Nabídka se liší podle platforem. Na GNU/Linuxu je k dispozici vzhled GTK a CDE/Motif, na Windows pak vzhled Windows a podobně. Není problém přiinstalovat si další a používat je pro všechny nebo jen pro některé aplikace.

Výchozí vzhled může uživatel změnit dvěma způsoby (nepočítáme-li možnost, že vývojář programu poskytne možnost volby v přímo rámci své aplikace, jako to umožňuje třeba jEdit). První možností je určení v příkazové řádce, druhou pak nastavení v preferencích. Uvažujme např. následující příkazy:

java Editor
java -Dswing.metalTheme=steel Editor
java -Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel Editor
java -Dswing.defaultlaf=com.sun.java.swing.plaf.motif.MotifLookAndFeel Editor

První příkaz spustí program Editor (z 26. dílu seriálu) s výchozím vzhledem. Příkaz na druhém řádku spouští program s tématem Steel vzhledu Metal (zde se samozřejmě předpokládá, že se opravdu použije Metal, že uživatel nenastavil v preferencích něco jiného). Třetí příkaz nastaví vzhled GTK a čtvrtý CDE/Motif. Je to jednoduché a snadno použitelné. Kdo by si chtěl změnit vzhled natrvalo, může to provést tak, že to souboru swing.properties (v adresáři $JDKHOME/lib/swing.properties) vloží něco podobného:

swing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel

Bohužel to nejde nastavit pro uživatele, jen pro celý systém. Snad se to v pozdějších verzích zlepší.

Změna vzhledu v programu

Někdy je vhodné měnit vzhled GUI uvnitř běžícího programu - ať už chceme poskytnout uživateli možnost snadného nastavení nebo mu prostě vnutit nějaký konkrétní vzhled. O správu vzhledů se ve Swingu stará statická třída UIManager. Zabývá se jak nastavováním vzhledu, tak použitím výchozích hodnot, získaných podle pravidel popsaných v přechozích odstavcích. Kromě toho má i různé další metody. Na tuto třídu se nyní podíváme.

Základem je dvojice metod setLookAndFeel(), které volí aktuální vzhled aplikace. Jedna přijímá přímo instanci třídy LookAndFeel (ještě o ní bude řeč), druhá pak název třídy. Již v tuto chvíli můžeme snadno změnit vzhled - stačí znát metody getSystemLookAndFeelClassName() a getCrossPlatformLookAndFeelClassName(). První vrací název třídy se systémovým vzhledem (tedy např. na GNU/Linuxu to může být GTK), druhá pak název třídy platformově přenositelného vzhledu (typicky Metal). Jak to celé vypadá, ukazuje následující příklad:

try {
  UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
  // reakce na výjimku
} catch (IllegalAccessException ex) {
  // reakce na výjimku
} catch (InstantiationException ex) {
  // reakce na výjimku
} catch (UnsupportedLookAndFeelException ex) {
  // reakce na výjimku
}

Všimněte si, že je potřeba pochytat řadu výjimek (lze to samozřejmě udělat souhrnně, v příkladu jsou rozepsány jen kvůli názornosti). To vyplývá z toho, že se třída získává pomocí metod reflexe (vzpomeňte na Class.forName()). Implementace totiž počítá s tím, že daná třída může být známa až v okamžiku, kdy ji někdo hodlá použít.

Dále jsou tu metody addAuxiliaryLookAndFeel() a removeAuxiliaryLookAndFeel(). Ty si necháme na později. Co však zmíním už nyní, je řada různých metod pro zjišťování vlastností podle klíčů. Sahají od primitivních hodnot (getInt(), getBoolean()) až ke složitým (getFont(), getBorder()) a jsou poskytují jednoduchý přístup k hodnotám aktuálního vzhledu. Při zjišťování hodnoty má největší prioritu uživatelské nastavení (metoda put()), následuje výchozí hodnota aktuálního vzhledu, a poslední je systémová hodnota.

Ještě bych připomněl dvě metody, a sice addPropertyChangeListener() a removePropertyChangeListener(). Přidávají a odstraňují odběratele událostí - na změny vlastností lze tak snadno reagovat, jsou zasílány v podobě událostí PropertyChangeEvent.

Jak to všechno funguje

Objekty grafických komponent, jak známo, kreslí v metodě paint(). Ta ve výchozí implementaci v zásadě volá metodu paintComponent() pro vykreslení vlastní komponenty a pak paintChildren() pro potomky (komponenty vložené uvnitř). Je to sice ve skutečnosti trochu složitější, ale to teď není důležité. Nyní nás zajímá kreslení komponenty jako takové, u potomků to totiž bude stejné. A důležité právě je, že nyní dochází k oné delegaci kreslení.

Každá komponenta má totiž svého delegáta (potomka třídy ComponentUI, reálně třeba MetalButtonUI), který se stará o vlastní kreslení. Tomuto delegátovi se zavolá metoda update() a předá se mu grafický kontext - ale pozor, ne ten původní, nýbrž kopie. To proto, aby nějaká manipulace s kontextem neměla vliv na pozdější kreslení. Metoda update() ve výchozí implementaci vymaže pozadí (u neprůhledných komponent) a zavolá paint(), samozřejmě stále v rámci delegáta. V metodě paint() a odtud volaných metodách se pak již přímo kreslí. Je to velice jednoduché.

Zcela logicky nyní vyvstává otázka, odkud se berou ony objekty delegátů, které se starají o kreslení jednotlivých komponent. Poskytuje je UIManager, a to prostřednictvím metody getUI(). Pro danou komponentu vrátí delegáta, který se komponentě přiřadí na metodou setUI(). Obvykle je to tak, že přiřazení zajistí metoda updateUI(), která je určena k nastavení základního vzhledu komponenty - při inicializaci komponenty (proběhne automaticky) nebo po změně vzhledu (musí se zavolat explicitně).

Ještě by se někdo mohl ptát, kde delegáty vezme UIManager. On samotný je nemá, za to má ale mechanismus, jak je získávat. Toto činí třída UIDefaults. Ta si podle potřeby načítá třídy delegátů (druh potřebné třídy se dozví od komponenty pomocí metody getUIClassId()), vytváří jejich instance a poskytuje je třídě UIManager.

Multiplexovaný vzhled

Za normálních okolností má každá grafická komponenta právě jednoho delegáta. Někdy je ale potřeba, aby jich bylo víc - například kromě klasického zobrazení též zvukový výstup. K tomu slouží Multiplexing Look & Feel, zastupovaný třídou MultiLookAndFeel a souvisejícími delegáty.

Vnitřní fungování je velice jednoduché. Každý delegát tohoto vzhledu obsahuje kolekci jiných delegátů a jejich metody (např. update()) volá jednu po druhé. Vždy je určen výchozí vzhled, který je první v pořadí a je důležitý například pro dotazování na velikost komponenty.

Nyní se vrátíme k odloženým metodám addAuxiliaryLookAndFeel() a removeAuxiliaryLookAndFeel(). Právě ty totiž slouží přidávání a odebírání vzhledů, které používá multiplexovaný vzhled. Lze tak ale učinit i uživatelsky, a to přidáním takovéhoto řádku do souboru swing.properties:

swing.auxiliarylaf=com.sun.java.swing.plaf.motif.MotifLookAndFeel,
    mypackage.MySpecialLookAndFeel
    

Aktualizace GUI po změně vzhledu

Změníme-li za běhu programu vzhled GUI, změna se neprojeví ihned. Projeví se pouze u nově vytvářených komponent, ne však u těch již existujících. Aby se komponenty překreslily s novým vzhledem, musí se každé z nich zavolat již zmíněná metoda updateUI().

Naštěstí se to nemusí provádět ručně pro každou komponentu zvlášť, protože máme statickou metodu SwingUtilities.updateComponentTreeUI(). Té se předá reference na komponentu od které se GUI aktualizuje. Když tedy předáme nejvyšší komponentu v hierarchii, aktualizují se i všechny pod ní.

Vytvoření vlastního vzhledu

Kdo si chce vytvořit vlastní vzhled, má v zásadě dvě možnosti - buď implementovat celý balík tříd, obsahující popis vzhledu a všechny potřebné delegáty, anebo se vydat cestou mnohem méně pracnou - použít vzhled Synth. Synth je plně implementovaný vzhled obsažený v Javě od verze 5.0, umožňující kromě jiného snadno uživatelsky definovat, jak bude GUI vypadat.

Vzhled Synth lze používat buď na základě uživatelských souborů s popisem vzhledu nebo se vzhledovými objekty definovanými v programu. První možnost je důležitější, nicméně si připomeneme i tu druhou.

Definice vzhledu pomocí XML souboru

Než se pustíme do vytváření souboru s popisem vzhledu, je nezbytné sáhnout do programu a připravit ho pro takovýto způsob práce. Můžeme si napsat třeba takovouto metodu:

public static void prepareGui() {
  try {
    SynthLookAndFeel slf = new SynthLookAndFeel();
    InputStream is = App.class.getResourceAsStream("skin.xml");
    if (is == null)
      throw new IOException("Nelze otevřít XML soubor.");
    try {
      slf.load(is, App.class);
      UIManager.setLookAndFeel(slf);
    } finally {
      is.close();   // musí se zavřít vždycky
    }
  } catch (Exception e) {
    String msg = "Nelze použít Synth:"
        + System.getProperty("line.separator") + e.getMessage();
    JOptionPane.showMessageDialog(null, msg, "Chyba",
        JOptionPane.ERROR_MESSAGE);
  }
}

Metoda vytvoří instanci vzhledu, otevře stream pro čtení z XML souboru se vzhledem (skinem), zpracuje soubor a nastaví nový vzhled v UIManageru. Pokud něco selže, zobrazí panel se zprávou o chybě a aplikace pracuje s původním vzhledem. Všimněte si několika věcí - jednak přístupu k prostředku (resource), a dále způsobu zacházení s výjimkami.

Načítání souboru jako prostředku se používá dost často (ještě se k tomu někdy později vrátíme), protože programátor nemusí řešit, jakým způsobem se aplikace spouští - zda jsou soubory jen tak na disku, načítají se ze sítě nebo jsou v JAR archivu. Co se týká zmíněných výjimek, tak pro výjimky vzniklé při parsování XML souboru je zvláštní blok try, a to proto, aby se v rámci bloku finally vždy hned uzavřel vstupní stream.

Nyní už můžeme přejít k vlastnímu XML souboru, zde tedy skin.xml. Může vypadat například takto (sice poněkud bláznivý styl, ale jeho fungování je dobře vidět):

<?xml version="1.0" encoding="iso-8859-1"?>

<synth>
  <style id="button">
    <opaque value="TRUE"/>
    <font name="Times New Roman" size="16"/>
    <state>
      <color value="green" type="FOREGROUND"/>
      <color value="#0000ff" type="BACKGROUND"/>
    </state>
    <state value="PRESSED">
      <color value="#ffff00" type="BACKGROUND"/>
    </state>    
  </style>
  <bind style="button" type="region" key="Button"/>
</synth>

Nebudu zde popisovat přesný formát, ten si každý může prostudovat. Řeknu tedy jen ve stručnosti, co výše uvedený text dělá. Bude se nastavovat styl tlačítek, proto si ho nazveme button (název není kritický). Dále řekneme, že se bude kreslit neprůhledně, a že se použije písmo Times New Roman o velikosti 16. Dále tu máme dvě stavová nastavení. První označuje všechny stavy (obecné nastavení), druhé pak konkrétní stav, tedy v tomto případě stisknuté tlačítko. Obecně nastavujeme zelené popředí a modré pozadí, pro stisknuté tlačítko pak bude pozadí žluté. Tím je styl vytvořen a je ho potřeba přiřadit klíči - v tomto případě Button (podle těchto klíčů se styly identifikují při nastavování komponentám). Pokud bychom chtěli styl přiřadit všem klíčům, stačí místo názvu použít .*.

Definice vzhledu v programu

I když bychom mohli i v programu vygenerovat XML data s popisem skinu, je to zbytečně složité a také výpočetně náročné. Místo toho lze použít jednodušší cestu, a to tvořicí třídu odvozenou od SynthStyleFactory. Tato třída bude generovat jednotlivé styly (instance SynthStyle) pro určité komponenty a jejich regiony (komponenta může být rozdělena do více různých regionů).

Třídy odvozené od SynthStyle se musí vytvořit ručně, žádná výchozí není k dispozici. Implementovat se musí přinejmenším metody getColorForState() a getFontForState() - stav komponenty se získá z kontextu (SynthContext) metodou getComponentState().

Obecně ale není moc dobrý nápad to řešit takto, prakticky vždy si vystačíme s XML souborem, případně doplněným pomocnými třídami - například potomky SynthPainter - v případech, kdy potřebujeme něco opravdu speciálního. K těmto třídám se vrátíme ještě někdy později, až se dostaneme k technologii JavaBeans.

Jak se tiskne?

V příští kapitole budu opět na základě čtenářských dotazů měnit plán. Původně jsem zamýšlel začít příště se základy technologie JavaBeans, ovšem nakonec bude všechno jinak. Ukazuje se, jak obrovský zájem je o problematiku tisku v Javě. A protože tisk hodně úzce souvisí s "běžnou" grafikou (používají se stejné mechanismy), je vhodná chvíle právě teď. Takže příští kapitola bude věnována právě oblasti tisku z javovských programů.

Verze pro tisk

pridej.cz

 

DISKUZE

thx 17.12.2006 02:57 Pavel `Goldenfish' Kysilka




Příspívat do diskuze mohou pouze registrovaní uživatelé.
> Vyhledávání software
> Vyhledávání článků

17.4.2018 0:46 /František Kučera
Dubnový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 19. 4. 2018 od 18:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tématem tohoto srazu bude OpenStreetMap (OSM) aneb svobodné mapy.
Přidat komentář

16.3.2018 22:01 /František Kučera
Kulatý OpenAlt sraz v Praze oslavíme klasicky: u limonády a piva! Přijďte si posedět, dát si dobré jídlo a vybrat z mnoha piv do restaurace Kulový blesk, který najdete v centru Prahy nedaleko metra I. P. Pavlova na adrese Sokolská 13, Praha 2. Sraz se koná ve čtvrtek 22. března a začínáme v 18:00. Heslo: OpenAlt. Vezměte s sebou svoje hračky! Uvítáme, když si s sebou na sraz vezmete svoje oblíbené hračky. Jestli máte nějaký drobný projekt postavený na Arduinu, nějakou zajímavou elektronickou součástku, či třeba i pěkný úlovek z crowdfundingové akce, neváhejte. Oslníte ostatní a o zábavu bude postaráno.
Přidat komentář

13.2.2018 0:41 /František Kučera
Únorový pražský sraz OpenAltu se koná 15. 2. 2018 a tentokrát se vydáme na návštěvu do jednoho pražského datacentra. Sejdeme se v 17:50 v severovýchodní části nástupiště tramvajové zastávky Koh-I-Noor. Po exkurzi se přesuneme do restaurace U Pštrosa (Moskevská 49), kde probereme tradiční témata (svobodný software a hardware, DIY, CNC, SDR, 3D tisk…) a tentokrát bude k vidění i IoT brána od The Things Network.
Přidat komentář

11.2.2018 23:11 /Petr Ježek
Hledáte lehký a rychlý prolížeč PDF souborů? Pokud vás již omrzelo čekat na načítání stránek či jiné nešvary, zkuste xreader.
Přidat komentář

11.2.2018 20:35 /Redakce Linuxsoft.cz
Třetí ročník odborné IT konference na téma Cloud computing v praxi proběhne ve čtvrtek 1. března 2018 v konferenčním centru Vavruška, v paláci Charitas, Karlovo náměstí 5, Praha 2 (u metra Karlovo náměstí) od 9:00 hod. dopoledne do cca 16 hod. odpoledne. Konference o trendech v oblasti cloud computingu nabídne i informace o konkrétních možnostech využívání cloudů a řešení vybraných otázek souvisejících s provozem IT infrastruktury.
Přidat komentář

15.1.2018 0:51 /František Kučera
První letošní pražský sraz se koná již tento čtvrtek 18. ledna od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Vítáni jsou všichni příznivci svobodného softwaru a hardwaru, ESP32, DIY, CNC, SDR nebo dobrého piva. Prvních deset účastníků srazu obdrží samolepku There Is No Cloud… just other people's computers. od Free Software Foundation.
Přidat komentář

14.11.2017 16:56 /František Kučera
Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě DIY, CNC, SDR nebo morseovka? Přijď na sraz spolku OpenAlt – tradičně první čtvrtek před třetím pátkem v měsíci: 16. listopadu od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).
Přidat komentář

12.11.2017 11:06 /Redakce Linuxsoft.cz
PR: 4. ročník odborné IT konference na téma Datová centra pro business proběhne již ve čtvrtek 23. listopadu 2017 v konferenčním centru Vavruška, v paláci Charitas, Karlovo náměstí 5, Praha 2 (u metra Karlovo náměstí) od 9:00. Konference o návrhu, budování, správě a efektivním využívání datových center nabídne odpovědi na aktuální a často řešené otázky, např Jaké jsou aktuální trendy v oblasti datových center a jak je využít pro vlastní prospěch? Jak zajistit pro firmu či jinou organizaci odpovídající služby datových center? Podle jakých kritérií vybrat dodavatele služeb? Jak volit součásti infrastruktury při budování či rozšiřování vlastního datového centra? Jak efektivně spravovat datové centrum? Jak eliminovat možná rizika? apod.
Přidat komentář

   Více ...   Přidat zprávičku

> Poslední diskuze

20.2.2018 18:48 / Ivan Majer
portal

20.2.2018 15:57 / Jan Havel
Jak využíváte služby cloudu v podnikání?

16.1.2018 1:08 / Ivan Pittner
verejna ip od o2 ubuntu

15.1.2018 17:26 / Mira Harvalik
Re: Jak udělat HTML/Javascript swiping gallery do mobilu?

30.12.2017 20:16 / Michal Knoll
odmocnina

Více ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2018) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze