BLACK FRIDAY! Slevy až 80 % jsou všude. Tak je nepropásni a přejdi do rostoucího IT oboru!
BF Sales

Lekce 4 - Java GUI

V minulé lekci, Java GUI - událost, jsme naši kalkulačku zprovoznili a vysvětlili si, jak fungují události v Javě.

Dnes si upravíme naší primitivní kalkulačku. Naučíme se hlavně nový Layout a obsluhovat více tlačítek. Výsledná kalkulačka by měla vypadat takhle:

vysledek

Shrnutí

Myslím, že by neuškodilo lehké shrnutí:

Abychom vytvořili okno, vytvoříme si vlastní třídu, která dědí ze třídy JFrame. Dále naší instanci nadefinujeme různé parametry (velikost okna, defaultní operaci – zavření, viditelnost). Do okna přidáváme tzv. komponenty (tlačítka, labely, textová pole atd.). To jsou instance tříd (JLabel, JTextField …). Přidáváme je do okna metodou add. Teď pozor! Až doposud jsme je přidávali přímo do okna. To můžeme samozřejmě dělat i nadále. Problém však nastane, budeme-li chtít změnit pozadí okna. Na to existuje metoda setBackground (parametr barva). Pokud ji však přidáme přímo, nestane se nic. Musíme použít tzv. kontejner. Kontejner si můžeme představit jako papír, který vyplní celé okno. Získáme ho přes metodu getContentPane(). Pozadí pak nastavíme konejneru a budeme do něho i nadále přidávat všechny komponenty. Barvu již vytvořit umíme, nadefinujeme ji tedy na nějakou příjemnou barvičku pomocí číslic RGB. Přidáme komponenty stejně jako v předchozích dílech:

public class Gui extends JFrame{

    private JLabel labCislo1, labCislo2, labVysledek;
    private JButton butSecti, butOdecti, butVydel, butVynasob;
    private JTextField tfCislo1;
    private JTextField tfCislo2;

    public Gui()
    {
        super("Kalkulačka");

        Container con = getContentPane();
        con.setBackground(new Color(210, 244, 255));
}

GridBagLayout

FlowLayout, který jsme používali do teď, je jednoduchý, ale není moc použitelný. Mnohem mocnějším nástrojem je tzv. GridBagLayout. Okno nám rozdělí na tabulku s buňkami. K buňkám přistupujeme podle souřadnicového systému X,Y. Vytvoříme instanci třídy GridBagLayout. Další nutnou věcí je instace třídy GridBagConstraints, která slouží k určení souřadnic. Layout nastavíme stejně. Pěkné by bylo, kdyby byl mezi buňkami nějaký prostor. Toho docílíme příkazem:

GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(10, 10, 10, 10);
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Znázornění pro lepší pochopení:

gbLayout

Přidáme na kontejner komponenty. U každého komponentu musíme určit polohu X a Y. Další změnou je parametr metody add(). Na druhé místo přidáme objekt gbc. To nám zaručí správnost rozmístění.

labCislo1 = new JLabel("Zadejte první číslo:");
    gbc.gridx = 0;
    gbc.gridy = 0;

    con.add(labCislo1, gbc);

    tfCislo1 = new JTextField(4);
    gbc.gridx = 1;
    gbc.gridy = 0;
    con.add(tfCislo1, gbc);

    labCislo2 = new JLabel("Zadejte druhé číslo:");
    gbc.gridx = 0;
    gbc.gridy = 1;
    con.add(labCislo2, gbc);

    tfCislo2 = new JTextField(4);
    gbc.gridx = 1;
    gbc.gridy = 1;
    con.add(tfCislo2, gbc);

    butSecti = new JButton("+");
    gbc.gridx = 0;
    gbc.gridy = 2;
    con.add(butSecti, gbc);

    butOdecti = new JButton("-");
    gbc.gridx = 1;
    gbc.gridy = 2;
    con.add(butOdecti, gbc);

    butVynasob = new JButton("/");
    gbc.gridx = 0;
    gbc.gridy = 3;
    con.add(butVynasob, gbc);

    butVydel = new JButton("*");
    gbc.gridx = 1;
    gbc.gridy = 3;
    con.add(butVydel, gbc);

    labVysledek = new JLabel("Výsledek:");
    gbc.gridx = 1;
    gbc.gridy = 4;
    con.add(labVysledek, gbc);

Obsluha tlačítek

Nyní se dostáváme k samotné funkčnosti. Ke každému z tlačítek musíme přidat posluchač události z nějaké třídy, do které implementuje rozhraní ActionListeneru. Asi nás napadne, že bychom mohli vytvořit čtyři třídy. Jedna by sčítala, druhá odčítala atd., ale byl by v tom zbytečný zmatek a navíc bychom dospěli k duplicitě kódu. Proto vytvoříme jenom jednu třídu - Event. Instanci event přidáme na všechna tlačítka.

Event event = new Event();
    butOdecti.addActionListener(event);
    butSecti.addActionListener(event);
    butVydel.addActionListener(event);
    butVynasob.addActionListener(event);

Metoda actionPerformed má jako parametr instanci třídy ActionEvent. Díky ní můžeme zjistit podrobnosti o tlačítku, které jsme stiskli a s něčím je porovnat. K informaci se dostaneme přes metodu getActionComand(), kterou metodou equals() porovnáme s nápisem na tlačítku. Můžeme tedy rozlišit, které tlačítko bylo stisknuto.

public class Event implements ActionListener {

       public void actionPerformed(ActionEvent e)
       {
           if(e.getActionCommand().equals("+"))
           {

           }
           else if(e.getActionCommand().equals("-"))
           {

           }
           else if(e.getActionCommand().equals("/"))
           {

           }
           else if(e.getActionCommand().equals("*"))
           {

           }
       }

Teď se dostáváme k logice programu. Samozřejmě bychom mohli všechno nabouchat mezi “ify“, ale jelikož nejsme žádní amatéři, uděláme to hezky OOP. Vytvoříme si tedy novou třídu Vypocet. Bude vypadat takto:

public class Vypocet {

    int cislo1;
    int cislo2;

    public Vypocet(int cislo1, int cislo2)
    {
        this.cislo1 = cislo1;
        this.cislo2 = cislo2;
    }

    public int secti()
    {
        return cislo1 + cislo2;
    }

    public int odecti()
    {
        return cislo1 - cislo2;
    }

    public int vydel()
    {
        return cislo1 / cislo2;
    }

    public int vynasob()
    {
        return cislo1 * cislo2;
    }
}

Všimněme si, že dělení nulou není ošetřené. K tomu se však ještě dostaneme. Vrátíme se do metody actionPerformed, která bude vypadat následovně:

public void actionPerformed(ActionEvent e)
    {
        int cislo1 = Integer.parseInt(tfCislo1.getText());
        int cislo2 = Integer.parseInt(tfCislo2.getText());
        String text = "";

        Vypocet v = new Vypocet(cislo1, cislo2);

        if(e.getActionCommand().equals("+"))
        {
            text = String.valueOf(v.secti());

        }
        else if(e.getActionCommand().equals("-"))
        {
            text = String.valueOf(v.odecti());
        }
        else if(e.getActionCommand().equals("/"))
        {
            text = String.valueOf(v.vydel());
        }
        else if(e.getActionCommand().equals("*"))
        {
            text = String.valueOf(v.vynasob());
        }

        labVysledek.setText(text);
    }

Výjimka

Nyní by program měl fungovat. Co se ale stane, pokud budu dělit nulou nebo nezadám celá čísla? GUI program sice nespadne, ale v terminálu nám chyby samozřejmě vyskočí. Jelikož bychom museli ošetřovat všude vstupy a k tomu ještě dělení nulou, bylo by to pěkných pár podmínek navíc. Mnohem lepším způsobem jsou tzv. Výjimky. Výjimka má dva bloky – try a catch.

try
{
// běh bez chyby
}
catch(Exception ex)
{
// běh s chybou
}

Jednoduše, pokud nenastane chyba, provede se blok try, pokud nastane jakákoliv chyba, nastane blok catch – chyby můžeme ještě dál dělit, ale o tom snad někdy příště. Za catch je parametrem objekt nějaké z výjimkových tříd. My použijeme třídu Exception. Finální kód bude tedy vypadat následovně:

public void actionPerformed(ActionEvent e)
{
    try
    {
        int cislo1 = Integer.parseInt(tfCislo1.getText());
        int cislo2 = Integer.parseInt(tfCislo2.getText());
        String text = "";

        Vypocet v = new Vypocet(cislo1, cislo2);

        if(e.getActionCommand().equals("+"))
        {
            text = String.valueOf(v.secti());

        }
        else if(e.getActionCommand().equals("-"))
        {
            text = String.valueOf(v.odecti());
        }
        else if(e.getActionCommand().equals("/"))
        {
            text = String.valueOf(v.vydel());
        }
        else if(e.getActionCommand().equals("*"))
        {
            text = String.valueOf(v.vynasob());
        }

        labVysledek.setText(text);
    }
    catch(Exception ex)
    {
        labVysledek.setText("Chyba");
    }

}

V příští lekci, Layouty v Java Swing, se zaměříme na 8 základních layoutů ve formulářových aplikacích Java Swing.


 

Stáhnout

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

 

Předchozí článek
Java GUI - událost
Všechny články v sekci
Java Swing bez grafického návrháře
Článek pro vás napsal Samik11
Avatar
Jak se ti líbí článek?
10 hlasů
Autor se věnuje primárně programování v jazyce Java. Nestraní se ani C# nebo PHP.
Aktivity (3)

 

 

Komentáře

Avatar
Fugiczek
Redaktor
Avatar
Fugiczek:27.3.2013 21:24

Mam k tomu 2 vyhrady.

  1. Proc tu vysvetlujes slozity GridBagLayout, ktery ma o hodne vic vlastnosti nez jsi pouzil ty a obsahuje vetsi problematiku. Navic jsi pouzil vlastnosti, ktere jsou i u GridLayout (rozestoupeni tam nastavis uz v kontruktoru a nemusis urcovat pozici x a y, proste to zadas poporade) a pro zacatecniky je mnohem lepsi k pochopeni.
  2. Osetrovat deleni nulou vyjimkou je pekna prasarna. Vyjimky jsou hlavne dobre kdyz program ladis. Vetsina vyjimek se pak osetruje ifama protoze vyjimky celkem brutalne zpomaluji beh aplikace, i kdyz tady to moc nepoznas.
 
Odpovědět
27.3.2013 21:24
Avatar
Samik11
Redaktor
Avatar
Odpovídá na Fugiczek
Samik11:27.3.2013 21:47

Určení souřadnic je univerzální. Co pokud bys chtěl například tři řádky vynechat? Na výjimce jsem chtěl ukázat, že catch blok se provede při jakékoliv chybě. :)

Odpovědět
27.3.2013 21:47
„Radost vidět a rozumět, to je nejkrásnější dar přírody.“ Albert Einstein
Avatar
Fugiczek
Redaktor
Avatar
Odpovídá na Samik11
Fugiczek:27.3.2013 22:17

Tak to logicky rozdelim na vice casti, nebo druha varianta je to vyplnit prazdnym JLabelem. Jde o to ze na tuto ukazku staci GridLayout, ktery bude vypadat uplne stejne stejne. GridBagLayout je overkill ktery zmate akorat zacatecniky. Toto je myslim serial o GUI ne o vyjimkach. Pokud ji chces tak moc pouzivat tak aspon nepouzivej vseobecnou vyjimku ale vyjimku ktera se pro to nejvice hodi, zde je to ArithmeticExcep­tion. Takhle jde akorat videt ze tomu jazyku vubec nerozumis a nevis kdy je co vhodne pouzit...

 
Odpovědět
27.3.2013 22:17
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Fugiczek
David Čápka:28.3.2013 7:07

"Osetrovat deleni nulou vyjimkou je pekna prasarna" - Na to jsi přišel jak? Přesně k tomu výjimky jsou. Proč bys odchytával výjimky, když program ladíš? To je chceš přeci naopak nechat, abys viděl, kde je chyba. Odchytneš je uživateli, aby mu aplikace nespadla. To, co jsi uvedl s tou podmínkou, je tzv. aktivní ošetření chyb, moc se to nepoužívá, protože výjimky jsou jediná správná cesta, jak s chybovými stavy aplikace pracovat.

Editováno 28.3.2013 7:08
Odpovědět
28.3.2013 7:07
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
Luboš Běhounek Satik
Autoredaktor
Avatar
Odpovídá na Fugiczek
Luboš Běhounek Satik:28.3.2013 9:23

Nevidim zadny duvod, proc by tu ta vyjimka byt nemohla nebo nemela, sice je vyhozeni vyjimky o nekolik radu pomalejsi nez hola podminka, ale zrovna u kalkulacky to asi nikoho moc netizi.
Vyjimkam bych se vyhnul az kdyz jde o nejaky kod, ktery musi bezet rychle a spousti se vicekrat za vterinu.

Zrovna deleni nulou je klasicky pripad na pouziti vyjimky, sice tady v tom programu muze chyba nastat jen pri deleni nulou, ale je to uz aspon hezky pripraveno treba pro pridani dalsich matematickych funkci, jako jsou tangens apod, ktere mohou vyhodit chybu s nekterymi parametry, pak bys musel osetrovat kazdy pripad rucne, na to je mnohem lepsi vyjimka (jak uz jsem psal vyse - pokud to neni casove kriticke, pak by se vyplatilo udelat optimalizaci a misto obecne chyby resit kazdy pripad podminkou a zvlast).

Editováno 28.3.2013 9:25
Odpovědět
28.3.2013 9:23
https://www.facebook.com/peasantsandcastles/
Avatar
Lukáš Hruda
Redaktor
Avatar
Odpovídá na Luboš Běhounek Satik
Lukáš Hruda:28.3.2013 11:05

V něčem máš pravdu. Na druhou stranu si ale myslím, že u chyb tohoto typu, je lepší jim předcházet, než je řešit až když nastanou, respektive zařídit, aby se nulou vůbec dělit nedalo a pak by ta metoda vrátila třeba řetězec "Nulou nelze dělit!". Zase ale, pokud není třeba zjistit v čem přesně chyba nastala, pak je lepší výjimka, protože když nastane chyba někde jinde, výjimka to zachytí. Takže si myslím, že správně je oboje. Co se mi spíš nelíbí, je docela dost dlouhý kus kódu v bloku try, dal bych to do nějaké privátní metody.

 
Odpovědět
28.3.2013 11:05
Avatar
Kit
Redaktor
Avatar
Odpovídá na Lukáš Hruda
Kit:28.3.2013 16:51

Výjimky byly v objektových jazycích zavedeny proto, aby se používaly. Testovat dělitele na nulu před vlastním dělením je hloupost, která se může vymstít například při vícevláknovém zpracování. Výjimka je v daném případě rychlejší, než vytváření zbytečných kritických sekcí.

Odpovědět
28.3.2013 16:51
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Lukáš Hruda
Redaktor
Avatar
Odpovídá na Kit
Lukáš Hruda:28.3.2013 17:01

To ale nemění nic na tom, že ten kód v té výjimce je delší než by musel :)

 
Odpovědět
28.3.2013 17:01
Avatar
Luboš Běhounek Satik
Autoredaktor
Avatar
Odpovídá na Kit
Luboš Běhounek Satik:28.3.2013 18:05

"Testovat dělitele na nulu před vlastním dělením je hloupost, která se může vymstít například při vícevláknovém zpracování. Výjimka je v daném případě rychlejší, než vytváření zbytečných kritických sekcí."

Naprosto chybné.
Teď jsem to schválně testoval, výjimek za sekundu se stihlo provést asi 280 (!), zatímco přístupů k proměnné v kritické sekci ze dvou vláken cca 40 000 000.

Odpovědět
28.3.2013 18:05
https://www.facebook.com/peasantsandcastles/
Avatar
Kit
Redaktor
Avatar
Odpovídá na Luboš Běhounek Satik
Kit:29.3.2013 17:07

To si schválně zkusím udělat také, protože ten tvůj výsledek vypadá velmi podivně. 280 výjimek za sekundu je hrozně málo. Jako kdybys to honil přes GUI.

Odpovědět
29.3.2013 17:07
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Kit
Redaktor
Avatar
Odpovídá na Luboš Běhounek Satik
Kit:29.3.2013 20:24

Zkusil jsem si dva cykly. V jednom jsem před dělením testoval, zda je dělitel nenulový, ve druhém jsem uvnitř cyklu měl try...catch. Cyklus s výjimkami byl nepatrně rychlejší než cyklus, ve kterém se testovala nenulovost dělitele. Výjimky jsou tedy lepší.

Pokud však k výjimce došlo při každém průchodu cyklem, zpracování se zpomalilo zhruba 1000×. To však nehraje roli, protože k výjimkám obvykle dochází výjimečně :)

Můj obstarožní NTB na 700 MHz v cyklu zpracoval zhruba 30000 výjimek dělení nulou za sekundu, takže něco máš asi špatně...

Odpovědět
29.3.2013 20:24
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Luboš Běhounek Satik
Autoredaktor
Avatar
Odpovídá na Kit
Luboš Běhounek Satik:30.3.2013 14:38

Máš pravdu, zapomněl jsem, že se mi výjimky vypisují do konzole, bez toho výpisu se jich stihne provést víc, cca 50 000 za sekundu, ale pořád jsou mnohem mnohem pomalejší, než cokoliv jiného.

Pošli kód, protože pokud cyklus s výjimkami byl rychlejší než cyklus s podmínkou, tak tam máš nějakou chybu, protože už z logiky fungování výjimek je to úplný nesmysl - vždyť porovnání hodnoty, jestli není nula, je jedna jednoduchá jnz nebo jz instrukce navíc, zatímco u výjimek se (kromě uložení stavů některých registrů procesoru) přidávají nějaká data pro obsluhu výjimky na zásobník...

Editováno 30.3.2013 14:39
Odpovědět
30.3.2013 14:38
https://www.facebook.com/peasantsandcastles/
Avatar
Kit
Redaktor
Avatar
Odpovídá na Luboš Běhounek Satik
Kit:30.3.2013 17:36

Zkusím opsat z hlavy kód, který jsem použil. S podmínkou:

int x;
for (int i = 0; i < 1000000000; i++) {
   if (i != 0) {
      x = 2000000000 / i;
   } else {
      x = 0;
   }
}

S výjimkou:

int x;
for (int i = 0; i < 1000000000; i++) {
   try {
      x = 2000000000 / i;
   } catch (Exception e) {
      x = 0;
   }
}

S výjimkou to bylo cca o 1 % rychlejší než s podmínkou.

Odpovědět
30.3.2013 17:36
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
martinkobelka
Redaktor
Avatar
martinkobelka:30.3.2013 23:09

Když chcu přidávat do formuláře prvky pomocí GridBagConstraints, tak mi to zobrazuje výjmku, nevíte čím to je? kod :

package snake;

import java.awt.Color;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import javax.swing.JFrame;
import javax.swing.JLabel;

class Windows extends JFrame{
    public void kresli(){
        setTitle("Kalkulačka");
        setSize(450, 700);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Container con = getContentPane();

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(10, 10, 10, 10);

        JLabel labCislo1 = new JLabel("Zadejte první číslo:");
        gbc.gridx = 1;
        gbc.gridy = 1;

        con.add(labCislo1, gbc);



        setVisible(true);
    }
}
 
Odpovědět
30.3.2013 23:09
Avatar
Samik11
Redaktor
Avatar
Odpovídá na martinkobelka
Samik11:30.3.2013 23:32

Nikde jsi layout nepřidal, to se dělá metodou:

setLayout()

Popravdě moc nechápu jak máš ten kód členěný. Podstatou je, že si vytvoříš třídu která dědí z JFrame, v tvém případě Windows a pak v hlavní metodě vytvoříš objekt této třídy. Zkus si projít ty předchozí díly. Případně stáhni zdrojáky. :-)

Odpovědět
30.3.2013 23:32
„Radost vidět a rozumět, to je nejkrásnější dar přírody.“ Albert Einstein
Avatar
niveses
Redaktor
Avatar
niveses:31.5.2013 17:22

Co lze udělat pro výsledky v mínusech?

 
Odpovědět
31.5.2013 17:22
Avatar
dNtichy
Člen
Avatar
dNtichy:29.10.2013 23:55

Mozem natiahnut component aj cez viac buniek tabulky gridbaglayoutu ako len cez 1?

 
Odpovědět
29.10.2013 23:55
Avatar
D0ll0k
Člen
Avatar
D0ll0k:25.5.2014 21:08

Já jsem tu nový a začínám "programovat". Chci se zeptat kam přesně mám vložit řádky po tom, co se zpátky vracíme k actionPerformed. Já jsem je vložil do třídy Event. Jenomže mi to hlásí chybu: Source not found. Mohl by prosím někdo říct kam je mám vložit?

Odpovědět
25.5.2014 21:08
Veni, vidi, programmato
Avatar
D0ll0k
Člen
Avatar
Odpovídá na D0ll0k
D0ll0k:11.6.2014 7:24

Tak už nic. Našel jsem to. Zapoměl jsem přidal GridBagLayout. Toto tady chybi, jak to zapsat. Musel jsem si to stáhnout a podívat se.

Odpovědět
11.6.2014 7:24
Veni, vidi, programmato
Avatar
reguluscentauri:29.11.2015 22:21

Zdravím,
chtěl bych se jen zeptat, kde dělám chybu
Když vložím GridBagConstraints gbc = new GridBagConstra­ints(), tak mi to vypíše chybu, vytvořím tedy podle návrhu Netbeans class pro gbc, do toho pak musim vložit
Insets insets;
int gridx;
int gridy;
A umazat tam final

A při spuštění mi netbeans vypíše
Exception in thread "main" java.lang.Ille­galArgumentEx­ception: cannot add to layout: constraints must be a GridBagConstraint

Kód jsem porovnal se zdrojákem odsud a je stejný až na to, že autor tam nemá class pro gbc. Je nějaká varianta, jak to obejít, která mi unikla nebo v čem může být problém?

Předem děkuji

 
Odpovědět
29.11.2015 22:21
Avatar
reguluscentauri:1.12.2015 17:51

Tak už jsem chybu objevil, jsem vůl a byl tam překlep v jednom písmenku, akorát zrovna nejspíš nešťastná náhoda nebo nevim, že to Netbeans identifikoval a řešil takhle přes externí class

 
Odpovědět
1.12.2015 17:51
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 21 zpráv z 21.