|
||||||||||||||||||||||||||||||||||||||||||||||||
Menu
Distributions (131)
Software (10844)
|
Java (27) - seznamy, stromy, tabulkyPrakticky žádná větší aplikace se neobejde bez zobrazování a modifikace
větších datových celků. Obvykle bývají reprezentovány jako různé seznamy,
stromy a tabulky. Framework Swing nám nabízí v tomto ohledu velice užitečný
a snadno použitelný soubor prostředků.
Modely a jejich smyslNež přejdeme ke konkrétním objektovým třídám, dovolil bych si nejprve představit obecný mechanismus práce s daty, který je odděluje od vlastního GUI. Jsou to tzv. modely. Model není nic jiného než implementace rozhraní, přes které grafická komponenta přistupuje k datům pro jejich zobrazení a úpravy. Komponenta tedy nemusí vůbec znát detaily implementace, vystačí si s rozhraním. K čemu je to dobré? Použití modelů výrazně zlepšuje variabilitu implementace a umožňuje důsledné oddělení GUI od backendu. Máme-li například tabulku, mohou být data uložena jak přímo v aplikaci (v kolekcích apod.), tak třeba i v databázi, klidně i s přímým přístupem (bez mezičlánku v podobě úložiště v aplikaci). Současně lze manipulace nad daty provádět také jinak než přes GUI, aniž by se na způsobu uložení dat cokoli měnilo.
Praktická realizace modelů ve Swingu je řešena tak, že máme jednak základní
rozhraní (např. Důležitou vlastností modelů je oznamování událostí. Dojde-li ke změně v datech, model to oznámí všech přihlášeným odběratelům. Již jsem to zmiňoval někdy dříve, ale opět to připomínám. Využívá se standardní mechanismus práce s událostmi (tedy vytvoření instance objektu události a její předání v argumentu metody pro obsluhu dané události). Seznamy ve SwinguMůžeme v zásadě rozlišovat dva druhy seznamů: obyčejné (v okénku je nějaký počet prvků, některé z nich mohou být "vybrané") a rozbalovací, tzv. combo boxy (normálně je zobrazen jen jediný prvek, při rozbalení se teprve zobrazí další). Každý z těchto seznamů má svoji třídu a svůj model. Podívejme se na ně blíže. Na úvod bych chtěl říci, že ačkoli seznamy obsahují metody pro operace nad daty, pokud máme přímo k dispozici referenci k modelu, je lepší volat metody modelu. Tím spíš, že model obecně nemusí podporovat všechny operace, které lze prostřednictvím seznamu volat. Obyčejný seznam
Je reprezentován třídou
Co se týká samotného vykreslování, seznam nemá vlastní posuvníky (je to podobné
jako např. u DefaultListModel m = new DefaultListModel(); m.addElement("Prvek 1"); m.addElement("Prvek 2"); m.addElement("Prvek 3"); JList list = new JList(m); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); JScrollPane sp = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); ... Object o = list.getSelectedValue();
V příkladu nejprve vytvoříme instanci výchozího datového modelu a vložíme
do ní prvky. Pak zkonstruujeme seznam (s použitím daného modelu), tomu
nastavíme režim výběru (pouze jednotlivé položky) a vložíme ho do komponenty
s posuvníky. V reálné aplikaci bychom případně pak nastavovali velikost,
pozici apod. Poslední řádek příkladu pak ukazuje, jak zjistit, který prvek
seznamu je vybrán. Tato metoda vrací přímo příslušný prvek (případně
Co by se stalo, pokud bychom přidali do modelu nějaký prvek? Výchozí implementace
na to reaguje zavoláním metody Rozbalovací seznam
Tuto komponentu potřebujeme možná ještě častěji než normální seznam. Je
tvořena třídou Combo box se z hlediska použití příliš neliší od běžného seznamu. Protože ovšem zobrazuje (v nerozbaleném stavu) jen jednu položku, do GUI se začleňuje podobným způsobem jako textové pole. Rozbalený seznam má v případě potřeby posuvník, není třeba se o to starat. Výběr může samozřejmě obsahovat pouze jediný prvek.
Rozlišujeme dva druhy rozbalovacích seznamů - editovatelné a needitovatelné.
U needitovatelných se pracuje pouze s výběrem prvku. Editovatelný seznam
umožňuje upravit vybranou položku, která však implicitně zůstává mimo vlastní
seznam - v modelu se tedy neobjeví a metoda Následující příklad ukazuje základy práce s rozbalovacím seznamem: String sa[] = { "Prvek 1", "Prvek 2", "Prvek 3", "Prvek 4" }; DefaultComboBoxModel m = new DefaultComboBoxModel(sa); JComboBox cb = new JComboBox(m); cb.setEditable(true); cb.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { ... } } }); Nejdřív opět vytvoříme výchozí model. Tento příklad ukazuje jiný způsob než minule - konstruktoru se předává připravené pole prvků. Pak zkonstruujeme seznam a označíme ho jako editovatelný. Další úsek kódu slouží k reakci na změnu výběru. Když uživatel vybere prvek ze seznamu (nebo upraví ten, který je vybraný), provedeme zvolenou akci. Reagovat lze samozřejmě i na zrušení výběru (odvybrání). StromyZatímco seznamy byly ve své podstatě velmi jednoduché, stromy jsou o něco složitější. Stromové datové struktury asi každý zná, stromové zobrazení v GUI rovněž. Proto by snad nikdo neměl mít problémy s pochopením toho, jak swingovská implementace stromů funguje.
Začneme modelem. Základem je rozhraní
Nelze opomenout třídu
Ačkoli rozhraní
K dispozici je též výchozí implementace,
Třída void addFiles(File dir, DefaultMutableTreeNode parent) { File fa[] = dir.listFiles(); for (int i=0; i<fa.length; i++) { File f = fa[i]; Arrays.sort(fa); DefaultMutableTreeNode node = new DefaultMutableTreeNode(f.getName()); parent.add(node); if (f.isDirectory()) { addFiles(f, node); } } } File home = new File(System.getProperty("user.home")); DefaultMutableTreeNode root = new DefaultMutableTreeNode(home.getName()); addFiles(home, root); DefaultTreeModel m = new DefaultTreeModel(root); JTree tree = new JTree(m); JScrollPane sp = new JScrollPane(tree, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); Příklad ukazuje zobrazení adresářového stromu (pod domovským adresářem) s rekurzivní implementací. Připomínám, že takto by se s adresářovým stromem pracovat nemělo, může být velice rozsáhlý a jeho načtení by trvalo dlouho. Vhodnější je načítat obsah adresáře až v okamžiku, kdy se rozbaluje. Zde jsem ovšem pro jednoduchost zvolil načtení celého stromu naráz.
Podívejme se nejdřív na metodu Při samotném použití pak zjistíme domovský adresář (získáme ho z vlastností systému), vytvoříme pro něj kořenový uzel a zavoláme výše popsanou metodu. Až je logický strom vytvořen, zkonstruujeme pro něj model a ten předáme konstruktoru grafické komponenty stromu. Zbývá už jen vložit komponentu do oblasti s posuvníky.
Když už jsem zmínil to postupné načítání při rozbalování, naznačím ještě, jak
by se to dělalo. Existuje rozhraní tree.addTreeWillExpandListener(new TreeWillExpandListener() { public void treeWillExpand(TreeExpansionEvent e) throws ExpandVetoException { DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.getPath().getLastPathComponent(); ... } public void treeWillCollapse(TreeExpansionEvent e) throws ExpandVetoException {} });
Příklad pracuje s anonymní třídou. Implementovat musíme samozřejmě obě metody
rozhraní, i když používáme jen jednu z nich. Objekt události obsahuje cestu
k uzlu, který bude rozbalen. Lze snadno získat přímo tento uzel, jak je
v příkladu ukázáno. Pokud bychom ale takto implementovali načítání adresářového
stromu, takto jednoduché by to nebylo. Buď bychom museli jako uživatelský
objekt stromu použít něco jiného než obyčejný řetězec s název souboru
(objekt Tabulky
Posledním objektem v tomto dílu seriálu bude tabulka, představovaná třídou
Model tabulky má metody pro zjištění počtu řádků a sloupců, získání a uložení hodnoty buňky, zjištění názvu a třídy sloupce (viz dále), a zjištění editovatelnosti buňky. Třída sloupce má zásadní význam - ovlivňuje totiž zobrazování a případné úpravy buněk v daném sloupci (různé např. pro textové řetězce a pravdivostní hodnoty). Sloupcový model umožňuje velice detailní ovlivnění toho, jak se bude se sloupci tabulky zobrazovat. Bohužel na tu dostatečný popis není dost prostoru, proto ho musím vynechat.
Pro případy, kdy si vytváříme vlastní model pro tabulková data, je nejlepší
použít abstraktní třídu
V jednodušších případech si lze vystačit s výchozí implementací modelu, třídou
Pro metody třídy DefaultTableModel m = new DefaultTableModel(); m.addColumn("Název"); m.addColumn("Hodnota"); Properties p = System.getProperties(); Iterator<Object> it = p.keySet().iterator(); while (it.hasNext()) { String key = (String) it.next(); String row[] = { key, p.getProperty(key) }; m.addRow(row); } JTable table = new JTable(m); JScrollPane sp = new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); Příklad ukazuje tabulku vlastností systému. V prvním sloupci je název vlastnosti, ve druhém její hodnota. S vlastnostmi systému jsme se setkali jak v tomto článku, tak i v některých dřívějších - jsou to textové hodnoty popisující prostředí, v němž běží program (např. domovský a pracovní adresář, oddělovač adresářových cest, kódování znaků a mnoho dalších parametrů). Hodnoty se zobrazují neseřazené.
V příkladu se nejprve vytvoří prázdný model a přidají se do něj dva sloupce.
Pak se postupně přidávají jednotlivé hodnoty - získávají se pomocí iterátoru.
Další kroky jsou pak triviální (a stejné jako u přechozích příkladů),
vytvoří se instance
Kdo si příklad vyzkouší ("omáčka" je pochopitelně vynechána a každý si ji
doplní sám - např. na základě příkladů z přechozích kapitol), možná zjistí, že
může obsah kterékoli buňky bez problémů změnit (dvojklikem na buňce se
spustí editace). To je přesně v souladu s tím, jak jsem popsal chování třídy
Vlastní zobrazení - proč ne?Příští kapitola naváže na tuto a bude věnována vlastním zobrazovačům (rendererům) a editorům. Při zobrazování a úpravách hodnot v tabulkách a dalších grafických objektech totiž nejsme zdaleka omezeni jen tím, co nám tyto komponenty nabízejí. Můžeme si vytvořit vlastní třídy, které se budou chovat tak, jak právě potřebujeme. Potřebujeme třeba zobrazovat záporná čísla červeně nebo při editaci kontrolovat formát vkládaných dat. Obojí, a ještě mnoho dalšího, lze bez problémů zařídit.
|
Search Software
Search Google
|
||||||||||||||||||||||||||||||||||||||||||||||
©Pavel Kysilka - 2003-2024 | maillinuxsoft.cz | Design: www.megadesign.cz |