C týden
Tento týden až 80% sleva na e-learning týkající se jazyka C
50 % bodů zdarma na online výuku díky naší Slevové akci!

Lekce 5 - Upomínač narozenin v Java Swing - Logická vrstva

V minulém dílu, Upomínač narozenin v Java Swing - Návrh formulářů, jsme navrhli kompletně formuláře pro naši aplikaci.

V tomto dílu se budeme zabývat návrhem logické vrstvy, tedy tříd, které obsahují logiku aplikace.

Datum

Jelikož práce s datem a časem je v Javě poměrně nepohodlná, přidejme si k projektu kratičkou statickou třídu, která nám ulehčí práci.

public class Datum {

    private static DateTimeFormatter formatData = DateTimeFormatter.ofPattern("d'.'M'.'y");

    public static String zformatuj(LocalDateTime datum) {
        return datum.format(formatData);
    }

    public static LocalDate naparsuj(String datumText) throws ParseException {
        return LocalDate.parse(datumText, formatData);
    }
}

Třída obsahuje definici formátu data. Dále disponuje dvěma metodami pro převod mezi datem a jeho textovou podobou. Datum můžeme načíst z textu (jak ho uživatel zadal) a nebo ho jako text např. vypsat.

Osoba

V naší aplikaci budou zcela jistě figurovat osoby, vytvořme jim tedy třídu.

Vlastnosti

Osoba bude mít 2 vlastnosti: jméno a narozeniny. Jméno bude String, narozeniny budou typu LocalDate. Obě vlastnosti implementujeme jako privátní atributy a vygenerujeme k nim gettery:

public class Osoba {
    private String jmeno;
    private LocalDate narozeniny;

    public String getJmeno() {
        return jmeno;
    }

    public LocalDate getNarozeniny() {
        return narozeniny;
    }

}

Metody

Osoba bude mít několik metod, nyní se však omezme pouze na její konstruktor, abychom naši aplikaci co nejdříve dostali do spustitelné podoby. Později ji doplníme. Do třídy tedy přidáme parametrický konstruktor.

Konstruktor

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Kromě nastavení vlastností instance bude mít konstruktor také za úkol tyto vlastnosti zvalidovat. Ukažme si jeho kód:

public Osoba(String jmeno, LocalDate narozeniny) throws IllegalArgumentException {
    if (jmeno.length() < 3) {
        throw new IllegalArgumentException("Jméno je příliš krátké");
    }
    if (narozeniny.isAfter(LocalDate.now())) {
        throw new IllegalArgumentException("Datum narození nesmí být v budoucnosti");
    }

    this.jmeno = jmeno;
    this.narozeniny = narozeniny;
}

Nejprve ověříme, zda není jméno příliš krátké nebo zda nejsou zadané narozeniny v budoucnosti. Pokud některá ze situací nastane, vyhodíme výjimku IllegalArgumen­tException a do jejího konstruktoru vložíme zprávu pro uživatele.

Pokud jste s výjimkami ještě nepracovali, vůbec to nevadí. Stačí vám vědět, že je to způsob, jakým se v objektových aplikacích pracuje s chybami, zejména těmi, které jsou způsobené zadáním špatné hodnoty od uživatele nebo které vznikly při práci se soubory. Vyvolání výjimky metodu okamžitě ukončí. Jak na výjimku reagovat si ukážeme dále v seriálu. Výjimky budeme vždy vyhazovat v logických třídách.

toString()

Jelikož budeme osoby vypisovat, přepíšeme metodu toString() tak, aby vracela jméno osoby:

@Override
public String toString() {
    return jmeno;
}

Právě tato metoda je později použita JListem k výpisu jeho položek.

Správce osob

Poslední logickou komponentou aplikace bude správce osob. Třída se bude starat o osoby, bude je umět přidávat, odebírat a jejich seznam ukládat do souboru a opětovně načíst. Konečně bude umět mezi osobami vyhledat tu, která má nejbližší narozeniny.

Přidejte si k projektu tedy třídu SpravceOsob.

Atributy

Jedinou vlastností třídy je seznam osob. Seznam je typu DefaultListModel. S touto kolekcí jsme se v seriálu ještě nesetkali. Jedná se o speciální typ kolekce, který lze nastavit jako zdroj dat formulářovým komponentám. ListModel na sobě umí vyvolat událost změny v případě, že se změní jeho obsah. Díky tomuto mechanismu se automaticky obnoví všechny komponenty na formuláři, které mají nastavený jako zdroj dat právě tento ListModel. Asi si dokážete představit, že obnovovat ručně desítky komponent na formuláři v případě změny může být velice nepřehledné. Jakmile v naší aplikaci přidáme novou osobu, bude ihned vidět v seznamu osob aniž bychom ho obnovovali, obnoví se sám. K DefaultListModelu vygenerujeme getter, který přejmenujeme na getModel():

Třída zatím vypadá takto:

public class SpravceOsob {

    private DefaultListModel<Osoba> osoby = new DefaultListModel<>();

    public ListModel getModel() {
        return osoby;
    }
}

Metody

Opět do třídy vložme zatím jen ty nejdůležitější metody.

pridej() a odeber()

Metody pro přidání a odebrání osoby jsou naprosto triviální:

public void pridej(Osoba osoba) {
    osoby.addElement(osoba);
}

public void odeber(Osoba osoba) {
    osoby.removeElement(osoba);
}

getOsoby()

Jelikož DefaultListModel postrádá spoustu důležitých metod, přidáme správci metodu getOsoby(), která vrátí osoby v obyčejném Listu. Můžeme tedy získat jak model, tak list. Model na List převedeme pomocí statické třídy Collections.

public List<Osoba> getOsoby() {
    return Collections.list(osoby.elements());
}

Základ logické vrstvy máme hotový.

Příště, Upomínač narozenin v Java Swing - Propojení vrstev, si ukážeme, jak propojit logiku s formulářem a celou aplikaci zprovozníme.


 

Stáhnout

Staženo 389x (51.79 kB)
Aplikace je včetně zdrojových kódů v jazyce Java

 

Předchozí článek
Upomínač narozenin v Java Swing - Návrh formulářů
Všechny články v sekci
Základy Java Swing
Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
4 hlasů
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 university Autor sítě se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity (7)

 

 

Komentáře
Zobrazit starší komentáře (4)

Avatar
Patrik Vala
Člen
Avatar
Patrik Vala:19. května 21:16

Ahoj, jaký je rozdíl mezi ModelList a List?

 
Odpovědět
19. května 21:16
Avatar
Dominik Bican:21. srpna 15:42

Můj NetBeans hlásí chybu u metody níže:

public List<Osoba> getOsoby() {
    return Collections.list(osoby.elements());
}

Píše to: type List does not take parameters

 
Odpovědět
21. srpna 15:42
Avatar
Atrament
Super redaktor
Avatar
Odpovídá na Dominik Bican
Atrament:22. srpna 16:17

Pravděpodobně importuješ špatný List, koukni jestli neimportuješ java.awt.List místo java.util.List

 
Odpovědět
22. srpna 16:17
Avatar
Odpovídá na Atrament
Dominik Bican:23. srpna 17:48

Díky moc, bylo to tak, jak v NetBeans můžeš kliknout na tu žlutou žárovku s nápovědou, co importovat, tak tam bylo na výběr obojí a já zvolil to špatné, pak už se bohužel to správné vůbec neukazovalo.
Takže tento problém vyřešen, nicméně mám jiný problém.
I přesto, že jsem kód nakonec zcela zkopíroval z tutoriálu, nejde mi přidat nová osoba.
V JDialog při kliknutí na OK se okno nezavře a dole vyskočí chyba, viz. níže. Pořád to nějak odkazuje na naši metodu naparsuj, jak parsujem datum z textu do proměnné, ale nevím proč. Když spustím tu apku z tutoriálu, běží v pohodě.

Exception in thread "AWT-EventQueue-0" java.time.format.DateTimeParseException: Text '' could not be parsed at index 0
        at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2050)
        at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1878)
        at Datum.naparsuj(Datum.java:34)
        at OsobaJDialog.okJButtonActionPerformed(OsobaJDialog.java:106)
        at OsobaJDialog$1.actionPerformed(OsobaJDialog.java:53)
 
Odpovědět
23. srpna 17:48
Avatar
Odpovídá na Atrament
Dominik Bican:23. srpna 17:49

Díky moc, bylo to tak, jak v NetBeans můžeš kliknout na tu žlutou žárovku s nápovědou, co importovat, tak tam bylo na výběr obojí a já zvolil to špatné, pak už se bohužel to správné vůbec neukazovalo.
Takže tento problém vyřešen, nicméně mám jiný problém.
I přesto, že jsem kód nakonec zcela zkopíroval z tutoriálu, nejde mi přidat nová osoba.
V JDialog při kliknutí na OK se okno nezavře a dole vyskočí chyba, viz. níže. Pořád to nějak odkazuje na naši metodu naparsuj, jak parsujem datum z textu do proměnné, ale nevím proč. Když spustím tu apku z tutoriálu, běží v pohodě.

Exception in thread "AWT-EventQueue-0" java.time.format.DateTimeParseException: Text '' could not be parsed at index 0
        at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2050)
        at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1878)
        at Datum.naparsuj(Datum.java:34)
        at OsobaJDialog.okJButtonActionPerformed(OsobaJDialog.java:106)
        at OsobaJDialog$1.actionPerformed(OsobaJDialog.java:53)
 
Odpovědět
23. srpna 17:49
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Odpovídá na Atrament
Dominik Bican:23. srpna 17:50

Díky moc, bylo to tak, jak v NetBeans můžeš kliknout na tu žlutou žárovku s nápovědou, co importovat, tak tam bylo na výběr obojí a já zvolil to špatné, pak už se bohužel to správné vůbec neukazovalo.
Takže tento problém vyřešen, nicméně mám jiný problém.
I přesto, že jsem kód nakonec zcela zkopíroval z tutoriálu, nejde mi přidat nová osoba.
V JDialog při kliknutí na OK se okno nezavře a dole vyskočí chyba, viz. níže. Pořád to nějak odkazuje na naši metodu naparsuj, jak parsujem datum z textu do proměnné, ale nevím proč. Když spustím tu apku z tutoriálu, běží v pohodě.

Exception in thread "AWT-EventQueue-0" java.time.format.DateTimeParseException: Text '' could not be parsed at index 0
        at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2050)
        at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1878)
        at Datum.naparsuj(Datum.java:34)
        at OsobaJDialog.okJButtonActionPerformed(OsobaJDialog.java:106)
        at OsobaJDialog$1.actionPerformed(OsobaJDialog.java:53)
 
Odpovědět
23. srpna 17:50
Avatar
Atrament
Super redaktor
Avatar
Odpovídá na Dominik Bican
Atrament:23. srpna 18:52

Ukaž obsah svého OsobaJDialog.java

 
Odpovědět
23. srpna 18:52
Avatar
Odpovídá na Atrament
Dominik Bican:23. srpna 19:40
import java.text.ParseException;
import java.time.LocalDate;
import javax.swing.JOptionPane;


/**
 *
 * @author Domča a Lenča
 */
public class OsobaJDialog extends javax.swing.JDialog {

    public Osoba osoba = null;

    /**
     * Creates new form OsobaJDialog
     */
    public OsobaJDialog(java.awt.Frame parent, boolean modal) {
        super(parent, modal);
        initComponents();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        jmenoJTextField = new javax.swing.JTextField();
        narozeninyJFormattedTextField = new javax.swing.JFormattedTextField();
        okJButton = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setTitle("Přidat osobu");

        jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/adduser.png"))); // NOI18N

        jLabel2.setText("Jméno");

        jLabel3.setText("Datum narození");

        narozeninyJFormattedTextField.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.DateFormatter()));

        okJButton.setText("OK");
        okJButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                okJButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                            .addComponent(jLabel3, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 87, Short.MAX_VALUE)
                            .addComponent(jLabel2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(narozeninyJFormattedTextField, javax.swing.GroupLayout.Alignment.TRAILING)
                            .addComponent(jmenoJTextField)))
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                .addGap(117, 117, 117)
                                .addComponent(okJButton))
                            .addGroup(layout.createSequentialGroup()
                                .addGap(77, 77, 77)
                                .addComponent(jLabel1)))
                        .addGap(0, 71, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 141, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(39, 39, 39)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel2)
                    .addComponent(jmenoJTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(18, 18, 18)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel3)
                    .addComponent(narozeninyJFormattedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 18, Short.MAX_VALUE)
                .addComponent(okJButton)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>

    private void okJButtonActionPerformed(java.awt.event.ActionEvent evt) {
        try {
            LocalDate narozeniny = Datum.naparsuj(narozeninyJFormattedTextField.getText());
            osoba = new Osoba(jmenoJTextField.getText(), narozeniny);
            dispose();
        } catch (ParseException | IllegalArgumentException ex) {
            JOptionPane.showMessageDialog(null, "Chyba: " + ex.getMessage());
        }
    }

    /**
     * @param args the command line arguments
     */

    // Variables declaration - do not modify
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JTextField jmenoJTextField;
    private javax.swing.JFormattedTextField narozeninyJFormattedTextField;
    private javax.swing.JButton okJButton;
    // End of variables declaration

    public Osoba getOsoba() {
        return osoba;
    }
}
 
Odpovědět
23. srpna 19:40
Avatar
Atrament
Super redaktor
Avatar
Odpovídá na Dominik Bican
Atrament:23. srpna 20:06

Nemáš správně nastavenou FormatterFactory na narozeninyJFormatedTextField, máš tam :

narozeninyJFormattedTextField.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.DateFormatter()));

ale mělo by to být

narozeninyJFormattedTextField.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.DateFormatter(new java.text.SimpleDateFormat("d.M.yyyy"))));

pokud pracuješ s datumem ve stejném formátu jako je v tutoriálech.
Jak to správně nastavit je ukázané v předchozím článku v podkapitolce o JFormattedTextField

 
Odpovědět
23. srpna 20:06
Avatar
Odpovídá na Atrament
Dominik Bican:23. srpna 21:46

Moc ti díky, funguje to :-)

 
Odpovědět
23. srpna 21:46
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 10 zpráv z 14. Zobrazit vše