9. díl - Kreslení na Graphics v Java Swing

Java Swing Kreslení na Graphics v Java Swing

V minulém dílu seriálu tutoriálů o tvorbě formulářových aplikací v Java Swing jsme doprogramovali upomínač narozenin. Vyzkoušeli jsme si na něm základní formulářové prvky a také modely a práci s chybovými stavy. Již umíme vytvořit poměrně sofistikované aplikace. Dnešní lekci budeme věnovat kreslení.

Kreslení na Graphics

Vytvoříme aplikaci, která má za úkol zastřešit prodej vstupenek do kina. Jak víme, v sálu je mnoho sedadel a pracovník kina by měl v aplikaci vidět, která sedadla jsou již obsazená. Možná by vás napadlo naklikat pro sedadla labely. Pokud by však kino mělo 15 řad a každá řada 30 sedadel, máme to 450 labelů. Asi tušíte, že existuje lepší cesta, než začít bušit JLabel1, JLabel2... A jak by se potom obsluhovaly? V případě, že potřebujeme vykreslit něco náročnějšího, než jen jeden nebo dva obrázky, využijeme Graphics. To spočívá v tom, že na formulář umístíme nějakou komponentu (nejčastěji JPanel nebo Canvas) a na její plátno budeme vykreslovat to, co potřebujeme.

Aplikaci značně zjednodušíme, není třeba aby byla složitá. Bude umět zobrazit jen jeden sál, který bude zpočátku prázdný. Uživatel nakliká myší obsazená sedadla a poté stiskne tlačítko Uložit, které do zvolené lokace uloží jednoduchý txt soubor s informací o obsazenosit sálu. Ukládání si zkusíme proto, abychom se naučili pracovat s dialogy.

Návrh formuláře

Vytvořte si novou Java Application bez hlavní třídy, přidáme do ní nový KinoJFrame, titulek mu nastavíme třeba na "Evidence kinosálu". Přes většinu formuláře natahněte JPanel (je v paletě v sekci Swing Containers), který pojmenujeme platnoJPanel. Pod JPanel přijde JButton se jménem ulozitJButton a textem "Uložit".

Formulář kinosálu v Java Swing

Logická vrstva

Asi vás nepřekvapí, že k aplikaci přidáme třídu Kinosal. Bude mít jeden privátní atribut, kterým bude dvourozměrné pole sedadel. Pokud jste s 2D polem ještě nepracovali, tak si ho můžete představit jako tabulku. Jednorozměrné (klasické) pole je vlastně jen jeden řádek. S 2D polem poté pracujeme úplně stejně, jako s jednorozměrným, jen musíme uvést dvě souřadnice (X a Y). Některé jazyky mají na 2D pole přímo datový typ, v Javě tomu tak není a pokud chceme 2D pole, uděláme jednoduše pole polí. Máme tedy pole (řádek) a v každé "přihrádce" tohoto pole je další pole (sloupec). Ve finále máme tedy tabulku.

class Kinosal
{
        private boolean[][] sedadla = new boolean[30][15];

}

Sedadla jsou typu boolean, protože nás zajímá jen jestli je volné nebo obsazené. 30 je šířka pole, 15 jeho výška.

Do třídy ještě přidáme 2 privátní konstanty, jedna udává velikost vykreslovaného sedadla v pixelech a druhá mezeru mezi sedadly v pixelech. Zvykněte si konstanty používat, až budete chtít sedadla zvětšit, stačí pouze přepsat jednu konstantu a nemusíte luštit vykreslovací kód.

private static final int VELIKOST = 16;
private static final int MEZERA = 2;

Můžeme přejít k metodám.

Vykreslení

Kinosál by se měl umět vykreslit. Již jsme si zmínili, že budeme kreslit na plátno. Toto plátno je typu Graphics a necháme si ho přijít v parametru metody vykresli(). Na plátno se potom kreslí pomocí jeho metod. Nás bude zatím zajímat jen metoda fillRect(), která vykreslí obdélník, vyplněný určitou barvou. Metod je tam obrovská spousta pro různé geometrické tvary, ať už vyplněné nebo nevyplněné. Můžete si je projet, některé si vyzkoušíme ještě v dalších lekcích.

Pomocí dvou vnořených cyklů projedeme všechna sedadla v poli a na plátno vykreslíme buď zelený nebo červený čtverec. Vnějším cyklem budeme projíždět řádky, vnitřním sloupce v aktuálním řádku. Barvu určíme podle toho, zda je sedadlo na dané souřadnici true nebo false. Kód metody bude následující:

public void vykresli(Graphics g)
{
        for (int j = 0; j < sedadla[0].length; j++)
        {
                for (int i = 0; i < sedadla.length; i++)
                {
                        if (sedadla[i][j])
                                g.setColor(Color.RED);
                        else
                                g.setColor(Color.GREEN);
                        g.fillRect(i * (VELIKOST + MEZERA), j * (VELIKOST + MEZERA), VELIKOST, VELIKOST);
                }
        }

}

Všimněte si, že v cyklech nepoužíváme hodnoty 30 a 15, ale používáme sedadla.length a sedadla[0].length. První výraz je šířka pole (počet sloupců), druhý je jeho výška (počet řádků). (Samozřejmě záleží na nás, kterou dimenzi si určíme jako výšku a kterou jako šířku). Pevnou velikost neuvádíme pochopitelně z důvodu, že v budoucnu můžeme pole zvětšit/zmenšit a museli bychom v kódu hledat kde všude jsme hodnoty 30 a 15 použili. Těmto problémům je vždy lepší se vyhnout a pracovat s délkou pole.

Za zmínku stojí i samotné vykreslení. Barvu nastavíme metodou setColor(), která bere v parametru jednu z konstant na třídě Color. Co se týče metody fillRect(), její parametry jsou souřadnice levého horního rohu obdélníku a dále jeho výška a šířka. Jelikož je každé sedadlo široké 16 pixelů + 2 pixely mezera, musíme jeho souřadnici touho hodnotou pronásobit. Pokud je v i např. hodnota 2 (kreslíme tedy 3. sloupec), kreslíme na X souřadnici 36, nikoli na 2 :) To samé platí pro souřadnici Y.

Později si můžete zkusit nahradit fillRect() metodou fillOval(). Funguje úplně stejně, ale vykreslní elipsu. Určitě si po dokončení aplikace zkuste vykreslit i další tvary.

Plátno

Jako plátno jsme v naší aplikaci použili JPanel. Ten se však samozřejmě vykreslí jako panel, nikoli jako sedadla kinosálu :) Abychom toto chování změnili, musíme JPanel podědit a přepsat jeho metodu paintComponent() tak, aby kreslila kinosál.

Do projektu tedy přidáme ještě třídu Platno. Její obsah bude následující:

public class Platno extends JPanel {

    private Kinosal kinosal;

    public Platno(Kinosal kinosal)
    {
        super();
        this.kinosal = kinosal;
    }

    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        kinosal.vykresli(g);
    }

}

Do třídy si v konstruktoru uložíme instanci kinosálu. Následně přepíšeme metodu paintComponent() tak, aby vykreslovala kinosál.

Pozn.: Kromě metody paintComponent() má komponenta i metodu paint(), kterou můžeme rovněž přepsat a dosahneme stejného výsledku. Je to minimálně trochu matoucí. Dle manuálů bychom měli správně přepisovat paintComponent(), jelikož je to metoda Swingu, paint() je z AWT.

Propojení formuláře s logickou vrstvou

Základ logiky máme hotový, pojďme ji propojit s formulářem. Přejdeme do kódu formuláře a ve třídě vytvoříme privátní instanci kinosálu:

private Kinosal kinosal = new Kinosal();

Nyní je třeba NetBeans sdělit, že ten JPanel, který jsme na formulář natáhli, nemá být typu JPanel, ale má to být naše upravené Platno. JPanel označíme a v Properties oknu se přesuneme na záložku Code. Do vlastnosti Custom Creation Code vložíme následující hodnotu:

new Platno(kinosal);
Java Swing Custom Creation Code v Netbeans

Tím upravíme vytvářecí kód pro tento panel, který NetBeans generuje a vypadal by jinak takto:

new JPanel();

Událost paintComponent() volá systém ve chvíli, kdy se má okno překreslit. To je samozřejmě v případě spuštění aplikace, ale také po obnovení z minimalizace, ve chvíli, kdy po okně aplikace přejedeme jiným oknem a podobně.

Když aplikaci spustíme, upravený panel se opravdu vykresluje jako sedadla v kině:

Kreslení tvarů na JPanel v Java Swing

Panel je stále typu JPanel, kdybychom na něm chtěli volat některé specifické metody, museli bychom ho přetypovat. V našem případě to potřebovat nebudeme.

V příštím dílu si ukážeme, jak kliknutím na určité sedadlo změnit jeho stav a zprovozníme také ukládání. Projekt máte jako vždy ke stažení v příloze pro případ, že se vám něco nepodařilo.


 

Stáhnout

Staženo 329x (25.6 kB)
Aplikace je včetně zdrojových kódů v jazyce java

 

  Aktivity (1)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (2 hlasů) :
55555


 



 

 

Komentáře

Avatar
martinkobelka
Redaktor
Avatar
martinkobelka:

Skvělý článek ! Moc děkuji. Poradil by mi někdo prosím jak provést přetypování JPanelu na Platno přímo v designeru ? Chtěl bych si vyzkušet volání specifických metod :)

 
Odpovědět 16.1.2015 15:36
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 1 zpráv z 1.