Vytváranie Swing aplikácií na zelenej lúke. Komponenty, reakcie na udalosti. Anonymné vnútorné triedy. Demonštrácia na projekte RSS čítačky.
RSS čítačka
Trieda pre biznis logiku
- Využijeme knižnicu Rome, modul Rome-Fetcher.
Pridáme závislosť
<dependency> <groupId>org.rometools</groupId> <artifactId>rome-fetcher</artifactId> <version>1.2</version> </dependency>
Vytvoríme servisnú triedu:
package sk.upjs.ics.ereses; import com.sun.syndication.feed.synd.SyndEntry; import com.sun.syndication.io.FeedException; import java.io.IOException; import java.net.URL; import java.util.List; import org.rometools.fetcher.FetcherException; import org.rometools.fetcher.impl.HttpURLFeedFetcher; public class RssService { public List<SyndEntry> getEntries(URL adresa) { try { SyndFeed feed = new HttpURLFeedFetcher().retrieveFeed(adresa); return feed.getEntries(); } catch (IOException e) { throw new RssException("Nemožno načítať RSS kanál kvôli I/O chybe. Je sieť dostupná?", e); } catch (FeedException e) { throw new RssException("Nesprávny formát RSS kanála. Syntaktická chyba?", e); } catch (FetcherException e) { throw new RssException("Chyba pri spracovani kanala. HTTP komunikácia zlyhala.", e); } } }
metóda vráti zoznam položiek
com.sun.syndication.feed.synd.SyndEntry
, ktoré v sebe nesú všetky základné veci (titulok, dátum, popis).RssException
je naša vlastná výnimka dediaca odRuntimeException
.
Prázdne okno
public class MainForm extends JFrame {
/* tu nič nie je */
}
Spustenie
public class Runner {
public static void main(String[] args) {
new MainForm().setVisible(true)
}
}
- Spustí sa prázdne sivé okno
- S nulovými rozmermi
- V ľavom hornom rohu monitora
Dodanie zoznamu
public class MainForm extends JFrame {
private RssService rssService = new RssService();
private JList lstPolozky;
public MainForm() throws MalformedURLException {
lstPolozky = new JList();
URL url = new URL("http://rss.sme.sk/rss/rss.asp?id=frontpage");
List<SyndEntry> entries = rssService.getEntries(url);
lstPolozky.setListData(entries.toArray());
add(lstPolozky);
setBounds(0, 0, 640, 480);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) throws Exception {
new MainForm().setVisible(true);
}
}
- komponenty zavádzajme ako inštančné premenné
- cez
setListData()
vieme doJListu
dodať pole objektov, ktoré sa majú zobraziž
Ďalšie nastavenia okna:
- cez
setBounds()
nastavíme rozmery a polohu okna - cez
setDefaultCloseOperation()
povieme, že aplikácia sa má ukončiť po zatvorení posledného okna. Štandardné správanie len schová okno, aplikácia však ďalej pobeží. (To si nemusíme všimnúť!)
Prispôsobenie zobrazovania položiek
- Položky teraz ukazujú čudesné veci — volanie metódy
toString()
naSyndEntry
. Prispôsobiť ich môžeme cez vlastný cell renderer:
package sk.upjs.ics.ereses; import com.sun.syndication.feed.synd.SyndEntry; import java.awt.Component; import javax.swing.DefaultListCellRenderer; import javax.swing.JList; public class SyndFeedListCellRender extends DefaultListCellRenderer { @Override public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { SyndEntry entry = (SyndEntry) value; String title = entry.getTitle(); return super.getListCellRendererComponent(list, title, index, isSelected, cellHasFocus); //To change body of generated methods, choose Tools | Templates. } }
použitie:
lstPolozky.setCellRenderer(new SyndFeedListCellRender());
Dodanie komponentu pre zobrazovanie textu položky
- Dodajme ďalší komponent pre textu položky: teda samotný text článku prevzatý z RSS atribútu
<description>
- Použijeme textové pole
JTextArea
. - Na oddelenie zoznamu položiek a textového políčka využijeme oddeľovač
JSplitPane
- oddeľovač berie dva komponenty: jeden hore, druhý dole
- zoznam a textové políčko už nemusíme pridávať do okna; urobí to za nás oddeľovač.
- oddeľovač funguje v horizontálnom režime (štandardný; položky vedľa seba) alebo vertikálnom (položky pod sebou;
JSplitPane.VERTICAL_SPLIT
).
Výsledok:
public class MainForm extends JFrame {
private RssService rssService = new RssService();
private JList lstPolozky;
private JSplitPane oddelovac;
private JTextArea txtDescription;
public MainForm() throws MalformedURLException {
lstPolozky = new JList();
URL url = new URL("http://rss.sme.sk/rss/rss.asp?id=frontpage");
List<SyndEntry> entries = rssService.getEntries(url);
lstPolozky.setListData(entries.toArray());
lstPolozky.setCellRenderer(new SyndFeedListCellRender());
txtDescription = new JTextArea();
oddelovac = new JSplitPane(JSplitPane.VERTICAL_SPLIT, lstPolozky, txtDescription);
add(oddelovac);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
}
/*...*/
}
Scrollovanie v zozname
- zoznam položiek sa natiahne na takú výšku, koľko zaberajú všetky položky dohromady
- chceme zaviesť podporu pre scrollovanie
- samotný zoznam
JList
nevie scrollovať - vieme ho však hodiť do scrollovacej plochy, ktorá bude scrollovať ním:
JScrollPane
do oddeľovača potom nepošleme zoznam položiek, ale celú
JScrollPane
public class MainForm extends JFrame {
private RssService rssService = new RssService(); private JList lstPolozky; private JSplitPane oddelovac; private JTextArea txtDescription; private final JScrollPane lstPolozkyScrollPane; public MainForm() throws MalformedURLException { lstPolozky = new JList(); URL url = new URL("http://rss.sme.sk/rss/rss.asp?id=frontpage"); List<SyndEntry> entries = rssService.getEntries(url); lstPolozky.setListData(entries.toArray()); lstPolozky.setCellRenderer(new SyndFeedListCellRender()); txtDescription = new JTextArea(); lstPolozkyScrollPane = new JScrollPane(lstPolozky); oddelovac = new JSplitPane(JSplitPane.VERTICAL_SPLIT, lstPolozkyScrollPane, txtDescription); add(oddelovac); setDefaultCloseOperation(EXIT_ON_CLOSE); pack(); }
Oživenie zoznamu
- dodajme interakciu: výber položky v zozname
JList
zobrazí jej popis v textovom políčku - zoznam
JList
vie vyvolávať udalosť „zmenil sa výber“ (“list selection”)- vieme teda pridať poslucháča
ListSelectionListener
- poslucháč je ľubovoľná trieda implementujúca tento interfejs
- pridáme ho cez metódu
addListSelectionListener()
- vieme teda pridať poslucháča
využime anonymné vnútorné triedy
lstPolozky.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if(e.getValueIsAdjusting()) { return; } SyndEntry polozka = (SyndEntry) lstPolozky.getSelectedValue(); txtDescription.setText(polozka.getDescription().getValue()); } });
pri výbere položky môže nastať viacero udalostí po sebe. Z metódy
getValueIsAdjusting()
vieme zistiť, či ešte prebieha zmena výberu alebo už skončila- zo zoznamu vyberieme aktuálny objekt a pretypujeme ho
- v treťom kroku nastavíme textové políčko na správnu hodnotu
Doladenie textového políčka
- Ak chceme, aby textové políčko scrollovalo tiež, hodíme ho do JSplitPane
- Nastavme mu tiež zalamovanie riadkov cez
setLineWrap()
- a nastavme zalamovanie na hraniciach slov (nie na písmenách) cez
setWrapStyleWord()
nastavme tiež preferovanú veľkosť po štarte aplikácie
txtDescription = new JTextArea(); txtDescription.setLineWrap(true); txtDescription.setWrapStyleWord(true); txtDescription.setPreferredSize(new Dimension(200, 200));
Celý kód:
public class MainForm extends JFrame {
private RssService rssService = new RssService();
private JList lstPolozky;
private JSplitPane oddelovac;
private JTextArea txtDescription;
private final JScrollPane lstPolozkyScrollPane;
private final JScrollPane txtDescriptionScrollPane;
public MainForm() throws MalformedURLException {
lstPolozky = new JList();
URL url = new URL("http://rss.sme.sk/rss/rss.asp?id=frontpage");
List<SyndEntry> entries = rssService.getEntries(url);
lstPolozky.setListData(entries.toArray());
lstPolozky.setCellRenderer(new SyndFeedListCellRender());
lstPolozky.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if(e.getValueIsAdjusting()) {
return;
}
SyndEntry polozka = (SyndEntry) lstPolozky.getSelectedValue();
txtDescription.setText(polozka.getDescription().getValue());
}
});
lstPolozkyScrollPane = new JScrollPane(lstPolozky);
txtDescription = new JTextArea();
txtDescription.setLineWrap(true);
txtDescription.setWrapStyleWord(true);
txtDescription.setPreferredSize(new Dimension(200, 200));
txtDescriptionScrollPane = new JScrollPane(txtDescription);
oddelovac = new JSplitPane(JSplitPane.VERTICAL_SPLIT, lstPolozkyScrollPane, txtDescriptionScrollPane);
add(oddelovac);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
}
public static void main(String[] args) throws Exception {
new MainForm().setVisible(true);
}
}
Nastavenie výzoru
- skúsme meniť výzor (look-and-feel)
metóda
UIManager.getSystemLookAndFeelClassName()
vráti názov triedy výzoru pre systémovo štandardný L&F. (Na Windowse aplikácia pripomína výzor Windowsu, na MacOSe Aqua…)public static void main(String[] args) throws Exception { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); new MainForm().setVisible(true); }
alternatívne môžeme zaviesť niektorý zo zabudovaných výzorov:
<dependency> <groupId>com.jgoodies</groupId> <artifactId>jgoodies-looks</artifactId> <version>2.5.2</version> </dependency>
následne:
UIManager.setLookAndFeel(new com.jgoodies.looks.windows.WindowsLookAndFeel());