Image
27.6.2016 0 Comments

Programujeme v Jave /6.časť

Späť na úvod >> Späť na programovanie >> Späť na seriál

Komponenty a dátové modely

Vítam vás už pri šiestej časti seriálu o programovaní v Jave. Tentoraz sa budeme venovať trošku zložitejším komponentom a dátovým modelom komponentov, pri ktorých je ich využitie možné. Pokiaľ vám tento pojem nič nehovorí, nebojte sa, všetko potrebné sa dozviete.

JList

Trieda JList je komponentom predstavujúcim zoznam (list) hodnôt, z ktorých používateľ môže niektoré označiť. Tento komponent je známy z webových formulárov ako multi-select, teda list s možnosťou viacnásobného výberu a so zobrazením viac než jednej položky. Vytvoriť inštanciu JList je veľmi jednoduché. Najjednoduchšie je použiť konštruktor prijímajúci ako parameter pole objektov, ktoré budú zobrazené v zozname. V našom príklade predpokladajme, že chceme zobraziť v zozname jednoducho iba textové reťazce. Druhou možnosťou je namiesto poľa objektov použiť dátovú štruktúru reprezentovanú triedou Vector. Tento postup vám však neodporúčam. Použitie samotnej triedy Vector sa neodporúča, lebo sa čiastočne považuje za zastaranú, pretože ide o synchronizovanú triedu. Po tom, čo inštanciu triedy JList vytvoríte, môžete ju rovnako ako iné komponenty pridať do kontajnera. V prípade, že neviete, koľko položiek bude zoznam obsahovať, môžete komponent zapuzdriť do triedy JScrollPane a zabezpečiť tak rolovanie položiek podobným spôsobom, ako ste to videli v predošlej časti pri použití s komponentom JTextArea. Trieda JList obsahuje veľa užitočných funkcií na špecifikovanie správania sa komponentu. Metóda setSelectionMode() napríklad umožňuje nastaviť spôsob vyberania položiek zoznamu. Ako parameter môžete použiť niektorú z konštánt definovaných v triede ListSelectionModel (o nej ešte bude reč) – konštanta SINGLE_SELECTION zabezpečí, že používateľ môže označiť iba jednu položku v zozname. Konštanta SINGLE_INTERVAL_SELECTION znamená možnosť výberu iba jedného intervalu hodnôt a nakoniec MULTIPLE_INTERVAL_SELECTION umožňuje označovať položky ľubovoľným spôsobom. Zistiť momentálne nastavenie umožňuje metóda getSelectionMode(). Takisto máte možnosť definovať počet viditeľných riadkov metódou setVisibleRowCount(). Jednoduché použitie komponentu JList demonštruje nasledujúci fragment kódu:

    String[] values = {"Paul", "John", "George", "Ringo"};
    JList list = new JList(values);
    list.setVisibleRowCount(2);
    list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    getContentPane().add(new JScrollPane(list));

V tejto jednoduchej ukážke sme vytvorili zoznam so štyrmi hodnotami, nastavili viditeľnú veľkosť zoznamu na dve položky a umožnili používateľovi vybrať iba jednu hodnotu.

Pokiaľ budete chcieť spracovať dáta z formulára obsahujúceho JList, využijete metódy getSelectedIndex() a getSelectedIndices(), resp. getSelectedValue() a getSelectedValues(). Ako už ich názvy naznačujú, getSelectedIndex() vracia index položky vybranej používateľom (v prípade režimu SINGLE_SELECTION) a getSelectedIndices() vracia pole typu int obsahujúce indexy všetkých položiek vybraných používateľom (bez ohľadu na režim). Metóda getSelectedValue() vracia vybranú hodnotu ako objekt typu Object (v prípade režimu SINGLE_SELECTION) a getSelectedValues() pre zmenu pole objektov typu Object obsahujúce hodnoty vybrané používateľom.

Je možné, že budete niekedy potrebovať označiť alebo odznačiť položky v komponente JList programovo. Vtedy sa vám bude hodiť metóda setSelectedIndex(), ktorej ako parameter odovzdáte index položky, ktorá má byť označená. V prípade, že používate režim viacnásobného výberu, je možné použiť metódu setSelectedIndices(), ktorej ako parameter odovzdáte pole typu int obsahujúce indexy položiek, ktoré majú byť vybrané. Nakoniec metóda setSelectionInterval() umožňuje vybrať súvislý interval položiek – stačí, ak ako parametre zadáte indexy prvej a poslednej položky, ktoré majú byť vybrané (budú vybrané všetky položky medzi nimi vrátane týchto krajných položiek, index prvej položky nemusí byť menší ako index druhej).

Pokiaľ chcete mať možnosť reagovať na akékoľvek zmeny v označení položiek v zozname JList, môžete metódou addListSelectionListener() zaregistrovať poslucháča typu ListSelectionListener. Toto rozhranie deklaruje jedinú metódu valueChanged(), ktorá je volaná pri akejkoľvek zmene vo výbere položiek. Ako parameter preberá inštanciu triedy ListSelectionEvent, z ktorej môže získať všetky potrebné informácie o zmene výberu. Musíte však dávať pozor na to, že pri jednej zmene výberu je metóda valueChanged() volaná dvakrát, raz počas zmeny výberu a druhýkrát po samotnej zmene. Pokiaľ chcete vedieť, ktorá z udalostí práve prebieha, použite metódu getValueIsAdjusting() triedy ListSelectionEvent. V prípade prvej udalosti vráti hodnotu true, v prípade druhej false. Pomocou metódy getFirstIndex() rovnakej triedy môžete získať index prvej položky, pri ktorej nastala zmena výberu, a pomocou metódy getLastIndex() index poslednej položky, pri ktorej nastala zmena výberu. Neexistuje však spôsob, ako priamo z tejto inštancie triedy ListSelectionEvent získať informáciu o tom, či položka bola odznačená alebo označená. Musíte metódou getSource() získať odkaz na komponent JList, ktorý udalosť vyvolal, a potrebné informácie o výbere zistiť z neho.

Dátový model

V predchádzajúcom odseku som si pripravil vhodnú pôdu na to, aby som vám mohol vysvetliť, čo je to dátový model. Triedy knižnice Swing sú postavené na architektúre MVC (model/view/controller), čo zjednodušene znamená, že dáta zobrazené v komponentoch sú oddelené od samotných komponentov. Túto architektúru využívajú takmer všetky komponenty vrátane textových, o ktorých bola reč v predošlej časti. Textové komponenty slúžia na zobrazenie a zadávanie nejakého textu, no tieto textové dáta sú uložené samostatne, najčastejšie v triede typu Document, ktorá je k danému textovému komponentu pridružená. Každý textový komponent má teda svoj dokument, z ktorého získava informácie o tom, čo a ako má zobraziť. Vďaka tomu je možné použiť jeden textový komponent na zobrazenie akokoľvek formátovaného textu, či už ide len o čistý text (plain text), alebo text formátovaný pomocou HTML. Vďaka robustnosti architektúry MVC môžete navrhovať bez problémov a jednoducho vlastné typy jednotlivých vrstiev tejto architektúry. Dátový model je tou najspodnejšou vrstvou, ktorá obsahuje dáta, ktoré majú byť akýmkoľvek spôsobom zobrazené či inak spracované.

V prípade triedy JList sú dáta reprezentované ako jednotlivé položky zoznamu. Tieto dáta je teda vhodné udržiavať mimo samotného komponentu, čo je aj v knižnici Swing zabezpečené. Dátový model pre triedu JList je reprezentovaný triedou typu ListModel. Toto rozhranie deklaruje štyri metódy:

addListDataListener() – metóda slúžiaca na registráciu poslucháčov typu ListDataListener,
removeListDataListener() – metóda slúžiaca na odregistrovanie poslucháčov ListDataListener,
getElementAt() – metóda, ktorá vráti hodnotu na pozícii zadanej ako parameter,
getSize() – metóda, ktorá vracia počet položiek v zozname.

Pokiaľ chcete implementovať vlastnú triedu s rozhraním ListDataListener, musíte implementovať všetky tieto štyri metódy.

Na chvíľu ešte odbočím a vysvetlím niečo o spomínaných poslucháčoch typu ListDataListener. Poslucháč tohto typu slúži na obsluhu zmien v dátovom modeli komponentu JList. Obsluhuje však iba zmeny v jeho dátovom modeli ListModel, nemá žiadnu nadväznosť na komponent JList. Z toho vyplýva, že zmeny, na ktoré reaguje, sú iba tie, ktoré majú vplyv na dáta – pridanie položiek, výber položiek alebo zmena položiek. Nie je tu nijaká nadväznosť na zmenu výberu položiek, na to slúži iný poslucháč, o ktorom som písal už v predchádzajúcom oddiele.

Ak budete chcieť implementovať vlastný dátový model ListModel, môžete použiť už predpripravenú triedu AbstractListModel. Táto trieda implementuje hneď dve metódy rozhrania ListModel – addListDataListener() a removeListDataListener(). Vďaka tomu sa už nemusíte starať o registráciu a obsluhu poslucháčov ListDataListener. Zostáva vám implementovať zostávajúce dve metódy. Pokiaľ by sme chceli fragment kódu z predchádzajúceho odseku prepísať tak, aby využíval triedu AbstractListModel, mohli by sme to spraviť takto:

class MyListModel extends AbstractListModel {
 
  private String[] values = {"Paul", "John", "George", "Ringo"};
 
  public int getSize() {
    return values.length;
  }
 
  public Object getElementAt(int index) {
    return values[index];
  }
 
}

Následne treba použiť konštruktor JList, ktorý preberá ako parameter odkaz na dátový model:

    ListModel model = new MyListModel();
    JList list = new JList(model);

Výsledok tohto kódu je rovnaký ako toho z predchádzajúceho odseku.

Aj keď sme predtým nepoužili dátový model priamo, ale sme iba vytvorili pole hodnôt a to sme odovzdali konštruktoru triedy JList, v skutočnosti prebehol takmer rovnaký proces, aký som demonštroval v druhej ukážke, teda bol vytvorený dátový model implementáciou dvoch abstraktných metód triedy AbstractListModel a jeho inštanciáciou. Následne je tento dátový model odovzdaný konštruktoru JList preberajúcemu ako parameter dátový model. O tom všetkom sa môžete presvedčiť nahliadnutím do zdrojového textu triedy JList.

Vďaka architektúre MVC môžete veľmi jednoducho meniť dáta zobrazované v komponentoch. Môžete si napríklad vytvoriť viacero dátových modelov s rôznymi dátami a priraďovať ich jednému komponentu podľa potreby pomocou metódy setModel(). Pokiaľ teda potrebujete zmeniť zobrazované dáta, nemusíte najprv vybrať všetky, ktoré tam už sú, a následne vložiť nové, ale stačí iba zmeniť dátový model.

 

JComboBox

Komponent reprezentovaný triedou JComboBox predstavuje ďalší veľmi známy prvok z webových formulárov či aplikácií GUI. Ide o zoznam položiek, z ktorých môže byť vybraná a zobrazená vždy len jedna, takže z malej časti ide o komponent podobný komponentu JList. Podobá sa mu aj zoznamom komponentov, pretože preťaženým verziám konštruktorov môžete ako parameter odovzdať pole obsahujúce hodnoty zoznamu, ale aj inštanciu triedy Vector, obsahujúcu tieto položky. Rovnako ako v prípade triedy JList je pri konštrukcii komponentu JComboBox vytvorený a pridružený dátový model, v tomto prípade reprezentovaný rozhraním ComboBoxModel. Môžete vytvoriť vlastnú triedu implementujúcu toto rozhranie a odovzdať jej inštanciu konštruktoru triedy JComboBox. V prípade, že použijete inú verziu konštruktora, prebehne tento proces v pozadí. Aj v tomto prípade môžete použiť niektorú z predpripravených tried dátového modelu pre JComboBox, napríklad DefaultComboBoxModel (nejde o abstraktnú triedu). Trieda DefaultComboBoxModel je použitá v prípade, že nepoužijete konštruktor preberajúci ako parameter odkaz na inštanciu dátového modelu. Po vytvorení inštancie triedy DefaultComboBoxModel môžete do tohto dátového modelu pridávať a odoberať položky podľa ľubovôle:

    DefaultComboBoxModel model = new DefaultComboBoxModel();
    model.addElement("Paul");
    model.addElement("John");
    model.addElement("George");
    model.addElement("Ringo");
    JComboBox cb = new JComboBox(model);

To isté však môžete dosiahnuť aj bez toho, aby ste vytvárali dátový model vy, môže to za vás urobiť konštruktor:

    JComboBox cb = new JComboBox();
    cb.addItem("Paul");
    cb.addItem("John");
    cb.addItem("George");
    cb.addItem("Ringo");

Metódy triedy JComboBox na pridávanie (addItem(), insertItemAt()) a odoberanie (removeItem() a jej variácie) položiek sú v skutočnosti iba delegované na príslušné metódy dátového modelu.

Medzi zaujímavé vlastnosti komponentu JComboBox patrí možnosť zadania vlastnej hodnoty takej, ktorá nie je v zozname hodnôt. Pokiaľ metóde setEditable() odovzdáte ako parameter hodnotu true, umožníte používateľovi zadanie vlastnej hodnoty, takže pokiaľ sa mu nebudú páčiť možnosti v zozname, môže zadať inú. Trieda JComboBox obsahuje mnoho ďalších užitočných metód, odporúčam preštudovať si dokumentáciu k API.

Pri spracovaní informácií z JComboBox budete potrebovať metódu getSelectedIndex(), ktorá vracia index momentálne vybranej položky alebo -1 v prípade, že nebola vybraná žiadna položka alebo ak používateľ zadal a vybral vlastnú hodnotu. Alternatívnou metódou je použitie metódy getSelectedItem(), ktorá vráti hodnotu vybranej položky ako objekt typu Object. K týmto metódam existujú aj metódy na nastavenie vybranej položky – setSelectedIndex() a setSelectedItem().

Trieda JComboBox umožňuje registráciu poslucháčov typu ItemListener. Toto rozhranie obsahuje jedinú metódu itemStateChanged(). Táto metóda je pri zmene výberu volaná dvakrát – prvýkrát pre udalosť odznačenia pôvodne označenej položky, druhýkrát pre udalosť označenia novo označenej položky. Ako parameter preberá objekt typu ItemEvent, ktorá obsahuje informácie o zmene. Pomocou metódy getStateChange() tejto triedy môžete získať informáciu o tom, či bola položka odznačená alebo označená ako vybraná – na to slúžia atribúty SELECTED a DESELECTED tejto triedy. Metódou getItem() môžete získať hodnotu vybranej položky.

Príklad

Na záver tejto časti som vám pripravil príklad použitia komponentov JComboBox a JList. V ňom máte možnosť zvoliť si pomocou JComboBoxu skupinu a po jej zvolení sú v komponente JList zobrazené mená jej členov. Pre JList sme použili vlastný dátový model, rozšírený o metódu setBand(), pomocou ktorej je možné nastaviť, ktorá skupina bude zobrazená. Túto metódu voláme z poslucháča typu ItemListener, ktorá jej odovzdá ako parameter index zvolenej položky, ktorý určuje danú skupinu.

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
 
public class ModelTest extends JApplet {
 
  private JList list;
  private RockModel model;
 
  public void init() {
    this.setSize(300, 100);
    Container kontajner = getContentPane();
    kontajner.setLayout(new FlowLayout());
 
    JComboBox cb = new JComboBox();
    cb.addItem("The Beatles");
    cb.addItem("Rolling Stones");
    cb.addItem("Queen");
    cb.addItem("Pink Floyd");
    cb.setSelectedIndex(0);
    cb.addItemListener(new ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        if (e.getStateChange() == ItemEvent.SELECTED) {
          int index = ((JComboBox) e.getSource()).getSelectedIndex();
          model.setBand(index);
          list.setSelectedIndex(-1);
        }
      }
    });
    kontajner.add(cb);
 
    list = new JList();
    model = new RockModel();
    list.setModel(model);
    list.setVisibleRowCount(2);
    list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    kontajner.add(list);
  }
 
}

 

class RockModel extends AbstractListModel {
 
  private String[][] members = {{"Paul McCartney", "John Lennon", "George Harrison", "Ringo Starr"},
                                {"Mick Jagger", "Keith Richards", "Charlie Watts", "Ron Wood", "Bill Wyman"},
                                {"Freddie Mercury", "Brian May", "Roger Taylor", "John Deacon"},
                                {"Syd Barrett", "Roger Waters", "Dave Gilmour", "Rick Wright", "Nick Mason"}};
  private int band = 0;
 
  public void setBand(int index) {
    fireIntervalRemoved(this, 0, members[band].length);
    band = index;
    fireIntervalAdded(this, 0, members[band].length);
  }
 
  public int getSize() {
    return members[band].length;
  }
 
  public Object getElementAt(int index) {
    return members[band][index];
  }
 
}

Všimnite si, že v tele metódy setBand() okrem nastavenia skupiny ešte voláme ďalšie dve metódy, ktorých úlohou nie je nič iné než informovať o zmene dát v dátovom modeli. Rozhranie ListModel totiž predpokladá možnosť registrácie poslucháčov typu ListDataListener, ktorých informuje o každej zmene dát v dátovom modeli. V našom prípade prebehnú hneď dve zmeny – najprv prestanú byť platné pôvodné položky (ako keby boli z dátového modelu vybrané) a následne je simulované vloženie nových položiek. Vo väčšine prípadov bývajú takýmito poslucháčmi komponenty, teda napríklad aj JList. Tento komponent je informovaný o každej zmene v dátovom modeli a vďaka tomu môže pri každej zmene upraviť korektne zoznam svojich položiek.

Je pravdepodobné, že v skutočnosti by ste asi túto úlohu riešili inak, ale nám šlo teraz o demonštrovanie použitia komponentov JList a JComboBox a dátového modelu.

Záver

Po tejto časti sú vám známe ďalšie dva komponenty a takisto už viete, akým spôsobom môžete pracovať s dátovým modelom komponentov knižnice Swing. Pokiaľ máte pocit, že to bolo na vás málo, skúste zistiť, akým spôsobom by bolo možné ovplyvniť to, ako sú vykresľované jednotlivé položky týchto komponentov. Napoviem vám, že sa oplatí prečítať si niečo v dokumentácii triedy JList (a aj jej metódy setCellRenderer() a getCellRenderer()) a ListCellRenderer.

V nasledujúcom pokračovaní sa budeme venovať komponentom na tvorbu tabuliek a stromov. Dovtedy dovidenia.

 

 

Autor: Andrej Chu

Nechajte si posielať prehľad najdôležitejších správ emailom

Mohlo by Vás zaujímať

Ako na to

Ako zbaviť fotky hmly

08.12.2016 11:59

Hmla alebo dym sú často veľmi kreatívne nástroje. No všetkého veľa škodí. Fotka potom stráca kontrast a v podstate na nej nič nevidieť. Hmlu môžete neraz následnými úpravami odstrániť alebo zredukovať ...

Ako na to

Užitočné SW nástroje

08.12.2016 11:53

AllDup v4.0.3 Určenie: program na vyhľadávanie a odstraňovanie duplicitných súborov Vlastnosti: duplicitné súbory sa vyhľadávajú len na zvolených diskových jednotkách alebo len v rámci vybraných ...

Ako na to

Fotografovanie s bleskom

08.12.2016 11:47

Ak máte moderný fotoaparát so vstavaným alebo externým bleskom, zdá sa vám téma článku triviálna. Jednoducho nastavíte vhodný režim, vyberiete najlepšiu kompozíciu záberu, exponujete a o zvyšok sa už ...

Žiadne komentáre

Vyhľadávanie

Kyocera - prve-zariadenia-formatu-a4-s-vykonom-a3

Najnovšie videá