UINF/PAZ1c 2013 – 11. prednáška

Vytváranie Swing aplikácií na zelenej lúke: dotváranie RSS čítačky. Layout managers: GridLayout, BorderLayout.

RSS čítačka

Layout manager v JFrame

  • okno JFrame využíva layout manager typu border layout reprezentovaný triedou BorderLayout.
  • nemusíme ho vôbec deklarovať, používa sa automaticky
  • náš JSplitPane je umiestnený do prostredného sektora.

Combobox

  • Dodajme nový combobox, teda objekt typu JComboBox.

    private JComboBox cmbKanaly = new JComboBox();
    
  • JComboBox sa riadi buď modelom, alebo doň môžeme pridávať prvky po jednom cez metódu addItem().

    List<RssKanal> kanaly = rssService.dajKanaly();
    for (RssKanal rssKanal : kanaly) {
        cmbKanaly.addItem(rssKanal);
    }
    
  • umiestnime ho na sever

    add(cmbKanaly, BorderLayout.NORTH);
    
  • zmenu vieme sledovať cez udalosť action performed:

    cmbKanaly.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            RssKanal vybranyKanal = (RssKanal) cmbKanaly.getSelectedItem();
            zmenaVyberuKanala(vybranyKanal);
        }
    });
    
  • aktuálne vybratú položku získame cez getSelectedItem() [porov. s JListom a jeho getSelectedValue()]

Tlačidlo na pridanie nového kanála

  • klasicky cez

    private JButton btnPridatKanal = new JButton(" + ");
    
  • môžeme ho umiestniť napravo od comboboxu

  • v našom layoute môže ísť na sever
  • pozor: v BorderLayoute môže byť v sektore len jeden komponent
  • komponenty však môžeme vnárať do panelov
  • vytvorme panel

    private JPanel panKanaly = new JPanel();
    
  • panel má štandardne FlowLayout, ktorý ukladá komponenty vedľa seba, ale neškáluje ich do šírky

  • využime radšej vnorený BorderLayout:

    • v strede bude combobox, ktorý vyplní celú šírku
    • na východe EAST bude tlačidlo pre pridanie

      panKanaly.setLayout(new BorderLayout());
      add(panKanaly, BorderLayout.NORTH);
      
      panKanaly.add(cmbKanaly);
      panKanaly.add(btnPridatKanal, BorderLayout.EAST);
      

Úprava servisnej triedy

  • potrebujeme vylepšiť servisnú triedu:

    • dodať metódu pre pridanie nového kanála
    • nainicializovať ju ukážkovými dátami

      package sk.upjs.ics.ereses;
      
      import com.sun.syndication.feed.synd.SyndEntry;
      import com.sun.syndication.feed.synd.SyndFeed;
      import com.sun.syndication.io.FeedException;
      import java.io.IOException;
      import java.net.MalformedURLException;
      import java.net.URL;
      import java.util.LinkedList;
      import java.util.List;
      import org.rometools.fetcher.FetcherException;
      import org.rometools.fetcher.impl.HttpURLFeedFetcher;
      
      public class RssService {
          private List<RssKanal> rssKanaly = new LinkedList<RssKanal>();
      
          public RssService() {
              try {
                  RssKanal sme = new RssKanal("SME", "http://rss.sme.sk/rss/rss.asp?id=frontpage");
                  RssKanal root = new RssKanal("Root", "http://www.root.cz/rss/clanky/");
                  RssKanal cas = new RssKanal("Nový čas", "http://www.cas.sk/rss/");
      
                  this.rssKanaly.add(sme);
                  this.rssKanaly.add(root);
                  this.rssKanaly.add(cas);
              } catch (MalformedURLException e) {
                   throw new RssException("Nemozno nacitat RSS kanal", e);
              }
          }
      
          public List<RssKanal> dajKanaly() {
              return this.rssKanaly;
          }
      
      
          public void pridaj(RssKanal rssKanal) {
              this.rssKanaly.add(rssKanal);
          }
      
      
          /* .... */
      }
      
  • namiesto kontrolovanej výnimky MalformedUrlException vyhodíme nekontrolovanú vlastnú výnimku RssException v duchu konvencií nových projektov v Jave.

  • postup:

    • vytvoríme teda inštančnú premennú pre zoznam,
    • v konštruktore ho naplníme dátami
    • vytvoríme metódu pridaj()
    • a metódu dajKanály() upraceme tak, aby vracala zoznam z inštančnej premennej

Továreň pre RssSlužbu

  • k triede RssService teraz budú pristupovať už dve triedy:
    • hlavné okno
    • a sekundárne okno pre pridávanie kanála [to vytvoríme o chvíľu]
  • vytvorme teda továreň pre RssService: predídeme tým strašným problémom s viacerými inštanciami RSS služby, ktoré budú mať separátne a nezávislé inštancie zoznamov kanálov, čo povedie k vysokozložitým chybám
  • využime kombináciu návrhových vzorov singleton a factory:

    package sk.upjs.ics.ereses;
    
    public enum RssServiceFactory {
        INSTANCE;
    
        private RssService rssService = new RssService();
    
        public RssService getRssService() {
            return rssService;
        }
    }
    
  • využitie továrne:

    public class MainForm extends JFrame {
    
        private RssService rssService = RssServiceFactory.INSTANCE.getRssService();
    

Okno pre pridanie nového kanála

  • Vytvorme samostatné okno pre pridanie kanála.

Entita

  • Vytvorme entitu pre kanál RssChannel:

    package sk.upjs.ics.ereses;
    
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class RssKanal {
        private String nazov;
    
        private URL url;
    
        public RssKanal(String nazov, String url) throws MalformedURLException {
            this(nazov, new URL(url));
        }
    
        public RssKanal(String nazov, URL url) {
            this.nazov = nazov;
            this.url = url;
        }
    
        public String getNazov() {
            return nazov;
        }
    
        public URL getUrl() {
            return url;
        }
    
        @Override
        public String toString() {
            return nazov;
        }
    
    
    }
    
  • Všimnime si convenience constructor, teda konštruktor pre uľahčenie často využívaného prípadu, keď kanál vytvoríme pomocou reťazcového názvu a reťazcovej URL adresy.

Layout manager

  • V okne využijeme škaredý, ale jednoduchý layout manager GridLayout, ktorý rozhádže komponenty do mriežky.

    package sk.upjs.ics.ereses;
    
    public class RssKanalForm extends JDialog {
        private JLabel lblNazov = new JLabel("Názov:");
        private JTextField txtNazov = new JTextField("", 25);
    
        private JLabel lblUrl = new JLabel("URL:");
        private JTextField txtUrl = new JTextField("", 25);
    
        private JButton btnOk = new JButton("OK");
        private JButton btnCancel = new JButton("Storno");
    
        private RssService rssService = RssServiceFactory.INSTANCE.getRssService();
    
        public RssKanalForm() {
            setLayout(new GridLayout(0, 2));
    
            add(lblNazov);
            add(txtNazov);
            add(lblUrl);
            add(txtUrl);
    
            add(btnOk);
            add(btnCancel);
    
            setDefaultCloseOperation(DISPOSE_ON_CLOSE);
            setResizable(false);
    
            pack();
    
        }
    }
    
  • Vytvoríme mriežku o neobmedzenom počte riadkov (0) a dvoch stĺpcoch:

    setLayout(new GridLayout(0, 2));
    
  • Komponenty len pridávame, polohu určí layout manager.

  • Nastavíme implicitnú akciu po zavretí okna: disposenutie, teda uvoľnenie prostriedkov cez

    setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    
  • Nastavíme vypnutie zmeny veľkosti používateľom:

    setResizable(false);
    
  • Nastavíme automatické preusporiadanie komponentov a veľkosti okna:

    pack();
    

Oživenie okna

  • Dodajme pre tlačidlá poslucháčov na udalosť action performed:

    btnOk.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            btnOkActionPerformed(e);
        }
    
    });
    
  • Všimnime si, že kvôli prehľadnosti kódu presmerujeme volanie na metódu btnOkActionPerformed() v triede okna. Takúto metódu vytvoríme ručne na základe názvu inštančnej premennej komponentu a udalosti: btnOk + actionPerformed. Vývojári triedy potom môžu prejsť príslušné metódy a veľmi rýchlo nájsť obslužný kód pre udalosti nad konkrétnym komponentom.

    • presne takto to robí aj NetBeans
  • v kóde pre tlačidlo OK vybudujeme entitu a uložíme ju do RSSService:

    public void btnOkActionPerformed(ActionEvent e) {
        try {
            String nazov = txtNazov.getText();
            String url = txtUrl.getText();
    
            RssKanal rssKanal = new RssKanal(nazov, url);
    
            rssService.pridaj(rssKanal);
    
            setVisible(false);
        } catch (MalformedURLException ex) {
            JOptionPane.showMessageDialog(this, 
                    "Kanál sa nepodarilo pridať",
                    "Chyba",
                    JOptionPane.ERROR_MESSAGE
            );
        }
    }
    
  • ak nastane chyba (teda výnimka), upozorníme na to používateľa prívetivým varovným dialógom.

Modalita okna

  • Modalitu okna zapneme úpravou konštruktora:

    • dodáme parameter typu Frame (JFrame je potom tejto triedy), ktorý sa stane vlastníkom modálneho okna
    • zavoláme rodičovský konštruktor s parametrom true, ktorým zapneme modalitu
  • Kód:

    public RssKanalForm(Frame vlastnik) {
        super(vlastnik, true);
        /*...*/
    }
    

Zobrazenie okna

  • Doupravujme hlavný formulár a dodajme action listener pre tlačidlo pridávania

    btnPridatKanal.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            btnPridatKanalActionPerformed(e);
        }
    });
    
  • Metóda:

    public void btnPridatKanalActionPerformed(ActionEvent e) {
        RssKanalForm rssKanalForm = new RssKanalForm(this);
        rssKanalForm.setVisible(true);
    
        inicializujCmbKanaly();
    }
    
  • V inicializovaní kanála zmažeme všetky existujúce položky cez removeAllItems() a nanovo ich natiahneme z RssServiceu.

    private void inicializujCmbKanaly() {
        cmbKanaly.removeAllItems();
    
        List<RssKanal> kanaly = rssService.dajKanaly();
        for (RssKanal rssKanal : kanaly) {
            cmbKanaly.addItem(rssKanal);
        }
    }
    

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *