JList v Jave nie je len hlúpa trieda pre zoznamy, ktorá zobrazuje reťazce pod sebou a tam končí. Je značne flexibilná a nie je veľký problém dosiahnuť všakovaké výzory objektov, ktoré bude používateľ obdivovať.
Urobme si napr. zoznam kontaktov, a.k.a. paródiu na instant messenger (po slovenský okamžitý správovník).
Tu, hľa, výsledný stav:
Čo budeme potrebovať?
- objekt pre kontakty
JListso zoznamom a jeho modelom- okno, ktoré to zobrazí
- vec, ktorá zobrazuje kontakty krajšie než bežný prací prášok, pardón, krajšie než obvykle.
Objekt pre kontakty
Kontakt. Čo dodať? Kontakt bude niesť nick a indikáciu toho, či je online alebo offline.
Trieda je nudné POJO: samé gettre, settre a konštruktory.
package sk.upjs.ics.paz1c.cellrenderer;
public class Contact {
private String nick;
private boolean isOnline;
public Contact(String nick) {
this.nick = nick;
}
public void setOnline(boolean isOnline) {
this.isOnline = isOnline;
}
public String getNick() {
return nick;
}
public boolean isOnline() {
return isOnline;
}
}
Okienečko so zoznamom, diel 1.
Okno so zoznamom bude opäť nuda. Vytvoríme jeden JList a napcháme ho do JFrame:
package sk.upjs.ics.paz1c.cellrenderer;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
public class ContactListFrame extends JFrame {
public ContactListFrame() {
setTitle("Contacts");
JList contactList = new JList();
add(contactList);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ContactListFrame contactListFrame = new ContactListFrame();
contactListFrame.setVisible(true);
contactListFrame.pack();
}
});
}
}
Po spustení dostaneme skvelú vec:
Hm, skúsme to natiahnuť:
O nič lepšie?
Vyzerá to divne, ale naozaj problém spočíva v tom, že zoznam nemá žiadne dáta. Dodajme ich teda!
Model zoznamu
Ako je známe, zoznam JList ťahá dáta z modelu. V tomto prípade nemusíme šaškovať s vlastnou implementáciou, ale rovno využijeme konštruktor JListu, do ktorého narveme pole objektov. Sám JList si potajme vytvorí inštanciu modelu (zrejme DefaultListModel) a bude ju používať.
Navyrábajme teda kontakty a napchajme ich do zoznamu:
Contact john = new Contact("john");
john.setOnline(true);
Contact lea = new Contact("lea");
Contact sokumi = new Contact("sokumi");
sokumi.setOnline(true);
Contact[] contacts = { john, lea, sokumi };
JList contactList = new JList(contacts);
Spusťme to a…
…hrôza! Podivnosti, náhodné čísla. Znalci vedia, že príčinou je chýbajúca metóda toString()v triede Contact. Zoznam JList štandardne využije na zobrazenie objektov práve túto metódu.
public class Contact {
/*...*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(nick).append(" [");
sb.append(isOnline ? "online" : "offline");
sb.append("]");
return sb.toString();
}
}
Po druhom spustení je to lepšie.
Vec, ktorá zobrazuje kontakty krajšie než obvykle
Vec, ktorá prevezme objekt z modelu a namaľuje ho do JListu je vyčlenená pomocou návrhového vzoru strategy. Ak vezmete objekt typu ListCellRenderer a nastavíte ho na JListe cez setCellRenderer(), môžete si dľa nálady meniť spôsob zobrazovania položiek.
Presnejšie povedané, tento objekt mapuje objekty z modelu na swingovské komponenty.
Implicitný ListCellRenderer je DefaultListCellRenderer, ktorý vezme objekt z modelu, zavolá na ňom toString() a výsledok zobrazí v utajenom JLabeli.
Chcete zmeniť text objektu? (Bez zmeny toString()u?) Nie je nič jednoduchšie, stačí oddediť od DefaultListCellRenderera, zobrať objekt z modelu, vytiahnuť príslušný reťazec a poslať ho do rodiča, nech ho zobrazí po svojom:
package sk.upjs.ics.paz1c.cellrenderer;
import java.awt.Component;
import javax.swing.DefaultListCellRenderer;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JList;
public class ContactListCellRenderer extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Contact contact = (Contact) value;
String displayedText = contact.getNick();
return super.getListCellRendererComponent(list, displayedText, index, isSelected, cellHasFocus);
}
}
Nezabudneme nastaviť tento cell renderer na objekte JList!
JList contactList = new JList(contacts);
contactList.setCellRenderer(new ContactListCellRenderer());
Zrazu to bude vyzerať nasledovne:
Vec, ktorá zobrazuje kontakty ešte krajšie než obvykle
Vytvorme si dva obrážteky: online.png (zelený kruh) a offline.png (červený kruh) a vložme ich do adresára so zdrojákmi.
Ako vieme zobraziť obrážteky vedľa textu?
Vyššie bol spomínané, že DefaultCellRenderer prevádza objekty na JLabely. Dokumentácia k tejto triede je pomerne záludná, ale dajú sa vytušiť dve veci:
DefaultCellRendererdedí odJLabel(och, divná to dedičnosť)- metóda
getListCellRendererComponent()v tejto triede vracia ako výsledok samú seba.
Áno, trieda je JLabel a zároveň ListCellRenderer, ktorý na sebe nastaví rozličné vlastnosti podľa modelu (text, obrázok) a potom vráti samú seba ako prvok, ktorý sa má namaľovať do JListu.
Toto nie je popísané explicitne, ale dá sa to vytušiť zo zdrojákov… a všetci sa na to spoliehajú.
Tam, kde existuje JLabel, možno nastaviť nielen text (setText()), ale aj obrážtek (setIcon()).
Na reprezentáciu obrázkov slúži v Swingu trieda ImageIcon, ktorá načíta obrázok z ľubovoľnej cesty. Ak využijeme fintu s classpathom, vyriešime naháňanie obrázkov po projekte (tie budú bok po boku s .class súbormi.)
Vytvorme teda dva ImageIcony a na základe toho, či je kontakt online alebo offline a vyrobíme kompletný dokonalý renderer:
public class ContactListCellRenderer extends DefaultListCellRenderer {
private ImageIcon onlineIcon;
private ImageIcon offlineIcon;
public ContactListCellRenderer() {
onlineIcon = new ImageIcon(ContactListCellRenderer.class.getResource("online.png"));
offlineIcon = new ImageIcon(ContactListCellRenderer.class.getResource("offline.png"));
}
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Contact contact = (Contact) value;
String contactText = contact.getNick();
JLabel label = (JLabel) super.getListCellRendererComponent(list, contactText, index, isSelected, cellHasFocus);
if(contact.isOnline()) {
label.setIcon(onlineIcon);
} else {
label.setIcon(offlineIcon);
}
return label;
}
}
Nezabudneme overiť, že je naozaj nastavený a môžeme obdivovať farebný zoznam kontaktov, ako sme videli na začiatku.




