A Java programozási nyelv egyik ismert GUI csomagja a swing. Ennek népszerű grafikus komponense az adatok táblázatos megjelenítését biztosító JTable komponens. A táblázatos megjelenítéshez több beállítás is szükséges. A JTable egy MVC komponens, így külön kezelendők a modell, nézet és a vezérlő funkcióihoz kötődő beállítások. A modell tárolja az adatokat például DefaultTableModel típusú objektumban, amiben szétválaszthatók a fejlécben és a többi cellákban található adatok. A nézethez tartozik a betűméret, a cellák színezése, az adatok igazítása, megjelenítése, a gördítősáv. A viselkedést, a felhasználói reakciót a vezérlő határozza meg, például rendezés, görgetés, fókusz, kijelölés, oszlopok sorrendjének cseréje.
Feladat
Készítsünk olyan Java swing-es kliensprogramot, amely tetszőleges adatforrásból (XML vagy JSON a hálózatról, JDBC adatbázis kapcsolatból, ORM leképzésből származó objektumokból) képes az átvett adatok grafikus felületen való táblázatos megjelenítésére JTable komponenssel! Építsünk arra, hogy az adatokon kívül metaadatok is rendelkezésünkre állnak! A megoldás legyen univerzális!
Képernyőképek
Modell
A táblázatos GUI komponenst kezdetben inicializálni kell, illetve a benne tárolt adatok is törölhetők, ha újrahasznosításra kerül a sor:
1 2 |
JTable tbEredmeny=new JTable(); tbEredmeny.setModel(new DefaultTableModel()); |
Ki kell nyerni a tároláshoz és a megjelenítéshez kötődő adatokat (1. lépés). A metaadatokból a for() ciklus előállítja az oszlopTomb-öt, és az oszlopTipusTomb-be kerülnek az Oracle adattípusból Java objektumtípusként megfeleltetett adatok. Előbbi a fejléc feliratainak szövegeit tartalmazza, és az utóbbi befolyásolja az egyes cellákban az igazítást, illetve hatással van adott oszlop rendezésére is:
1 2 3 4 5 6 7 |
ResultSetMetaData rsmd=rs.getMetaData(); String[] oszlopTomb=new String[rsmd.getColumnCount()]; Class[] oszlopTipusTomb=new Class[oszlopTomb.length]; for(int i=0; i<oszlopTomb.length; i++) { oszlopTomb[i]=rsmd.getColumnName(i+1); oszlopTipusTomb[i]=Class.forName(rsmd.getColumnClassName(i+1)); } |
Ki kell nyerni a tároláshoz és a megjelenítéshez kötődő adatokat (2. lépés). A while() ciklus végigjárja az eredménytábla sorait és Object típusú tömböt állít elő az összetartozó rekord mezőiből. Ezek először generikus listába kerülnek, majd onnan kétdimenziós Object típusú tömbbe:
1 2 3 4 5 6 7 8 9 10 |
ArrayList<Object[]> adatLista=new ArrayList<>(); while(rs.next()) { Object[] rekord=new Object[oszlopTomb.length]; for(int i=0; i<oszlopTomb.length; i++) rekord[i]=rs.getObject(i+1); adatLista.add(rekord); } Object[][] adatTomb=new Object[adatLista.size()][oszlopTomb.length]; for(int i=0; i<adatTomb.length; i++) adatTomb[i]=adatLista.get(i); |
Mi indokolja a tömbökből álló generikus lista ( adatLista) alkalmazását?
Előállítjuk a vizuális komponens mögötti adatmodellt. Öröklődéssel kiegészítjük két hasznos függvénnyel, így cellák rajzolása/renderelése és rendezése megkaphatja a szükséges adattípust ( getColumnClass()), valamint letiltható a cellák szerkeszthetősége ( isCellEditable()). Utóbbiak inkább a vezérléshez kötődnek, de modellen keresztül itt és így kell beállítani:
1 2 3 4 5 6 7 8 9 10 11 |
DefaultTableModel dtm=new DefaultTableModel(adatTomb, oszlopTomb) { Class[] types=oszlopTipusTomb; @Override public Class getColumnClass(int columnIndex) { return types [columnIndex]; } @Override public boolean isCellEditable(int row, int column) { return false; } }; |
Végül a vizuális komponens mögötti adatmodellt kell átadni:
1 |
tbEredmeny.setModel(dtm); |
Nézet
Adott betűtípus, betűstílus és betűméret használható a táblázat fejlécében, celláiban, illetve a betűmérettől függhet a sorok magassága:
1 2 3 4 |
Font betutipus=new Font("Tahoma", Font.PLAIN, 18); tbEredmeny.getTableHeader().setFont(betutipus); tbEredmeny.setFont(betutipus); tbEredmeny.setRowHeight(24); |
Hasznos ha JScrollPane típusú gördítősáv tartozik a táblázathoz, így dinamikusan megjeleníthető/elrejthető a függőleges/vízszintes gördítősáv:
1 |
spGorditosav.setViewportView(tbEredmeny); |
Vezérlés
Az adatokhoz valahogyan hozzá kell jutni. Most JDBC kapcsolatot használunk és az Oracle HR sémából kérdezünk le adatokat, de a forráskód-részlet univerzális. A folyamat a következő:
- Betöltjük a driver osztályt.
- Autentikációval c kapcsolatot nyitunk az adatbázis-szerver felé.
- Végrehajtjuk a lekérdező SQL parancsot.
- Feldolgozzuk az eredményül kapott ResultSet típusú rs objektumot.
- Végül lezárjuk a c hálózati kapcsolatot.
1 2 3 4 5 |
Class.forName(driver); Connection c=DriverManager.getConnection(url, user, password); ResultSet rs=c.createStatement().executeQuery(sql); //... c.close(); |
Ha engedélyezzük, akkor a megjelenő táblázat fejlécében az egyes oszlopok felirataira kattintva elérhetjük, hogy az adott oszlop típusának megfelelően növekvő vagy csökkenő sorrendbe átrendeződjenek az adatok:
1 |
tbEredmeny.setAutoCreateRowSorter(true); |
A kivételkezelést nem részleteztük a fenti forráskódoknál, de természetesen kötelezően adott.
A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.
A feladat a Java SE szoftverfejlesztő tanfolyam 45-52. óra: Adatbázis-kezelés JDBC alapon, illetve Java adatbázis-kezelő tanfolyam 9-12. óra: Oracle HR séma elemzése, 33-36. óra: Grafikus kliensalkalmazás fejlesztése JDBC alapon, 2. rész alkalmaihoz kapcsolódik.
Tipikus Exceles igény, hogy a táblázat páros és páratlan sorai más háttérszínnel jelenjenek meg. Kérhetek ehhez ötletet, hogy csináljam
JTable
-lel?Áron: a táblázat komponens celláinak háttérszínét az alapértelmezett kirajzolás/renderelés felülírásával tudod megoldani. Öröklődésre swing-ben néztünk példát a júniusi utolsó óránkon, amikor
JButton
keretét szabtuk testre. Az elvet építsd össze a swing tutorial-beli két példával:Hát nem ment könnyen, 🙂 de sikerült.
Szeretnék egy olyan
JTable
cellát, aminek a háttérszíne részben megváltoztatható. Ha a cellát teljesen kitölti egy háttérszín, akkor azt meg tudom csinálni a ColorRenderer.java mintapélda alapján, ami így néz ki (Favorite Color oszlop):A mai dátumból kivonva a
HIRE_DATE
-et és osztva 365-tel megkapjuk kb. egy alkalmazott tapasztalatát. A cégnél eltöltött idő alapján. Ezt már összeraktam SQL-ben. Egy cellában szeretném megjeleníteni a háttérszín arányos kitöltésének beállításával az adatokat, valahogy így (Age oszlop):Arra gondoltam, hogy
JPanel
-be raknék egymás mellé kétJLabel
t. A balra lévőt színezném, a jobbra lévő fehér lenne. És a panel kerülne a cellába. Szeretnék támpontot kérni, hogy jó-e az irány, vagy inkább teljesen másképp induljak el. Köszönöm előre is.Áron: tetszik, hogy ilyen konkrétan megfogalmaztad, hogy mit szeretnél. A linkekről beraktam a képeket a hozzászólásodba.
Jól látod: ez a feladat a rajzolás felüldefiniálásával már nem oldható meg könnyen, ezért jó út a komponens alapú megközelítés. Az ötleted megvalósítható. Ugyanilyen konténer-elvvel építünk GUI-t általában. Azért nem javaslom, mert 3 komponenst használnál egyetlen adat megjelenítéséhez cellánként.
Más utat javaslok, amiben 1 adathoz 1 komponens tartozik.
A felső képeden látható, hogy a Vegetarian oszlopban
JCheckBox
osztályú objektumok jelennek meg. Abból kiindulva, hogy aJTable
bármelyik cellájába „belerendereltethető” bármilyen swing-es komponens, így próbálkozz aJSlider
folyamatindikátor beillesztésével. A cellákhoz javaslom egyéni ToolTip beállításaként a lekérdezett tapasztalati éveket (kerekítve vagy egésszé csonkolva – mint eltöltött teljes év).Sándor: köszönöm az útmutatást. Hát ráment a mai délutánom, de megoldottam.
Áron, tudod: mindenki annyit visz el a tanfolyamról, amennyit beletett. Örülök neki, hogy sikerült megoldanod.
Nincs már több közös kontakt óránk, de egy következő csoporttal kb. október végén járunk majd ennél a témakörnél. Ha lenne kedved, szívesen látunk: gyere be és mutasd meg a többieknek, hogy meddig jutottál, hogyan gondoltad végig, hogyan fejlődtél. Ha szeretnéd, egyeztessük privátban.
Balázs, szeretném módosítani az egyik órai
JTable
-ös példát. Ilyesmi kiválasztást szeretnék:Összeraktam, hogy 1:N kapcsolat van az adatok között, a
JComboBox
-ot bele tudom rakni aDefaultCellEditor
és aDefaultTableCellRenderer
összekötésével. A képen olyan a példa, hogy a táblázat minden sorában ugyanaz a választék a programozási nyelveknél.Azt szeretném, hogy a különböző sorokban különböző választék jelenjen meg a legördülő listákban. Három dologra tudnál válaszolni: megoldható ez, ha igen: hogyan induljak el, ill. ha megy ez az Oracle HR sémából, akkor milyen adatokkal csináljam?
Megoldható Lilla. Látom, nagyon igyekszel gyakorolni. Örülök, hogy már itt tartasz. Meg fog térülni.
Az Oracle HR sémából például ezek az adatok (és a közöttük lévő kapcsolatok) alkalmasak arra, amit szeretnél:
Az MVC-s projektben elérhető az
EMPLOYEE_ID
az adatbázis modell rétegből és/vagy a rekord sorszáma aJTable
mögöttiDefaultTableModel
saját modell rétegből, ami(k)hez egyedileg hozzá tudod rendelni az adott sorban lévőJComboBox
vizuális komponens mögöttiDefaultComboBoxModel
saját modellt adatforrásként.Mindkét esetben kezelni kell azt, ha nincs adat: például 1 fős részlegben nincs kollégája, vagy az organogramban nem áll felette senki.
Értem, köszönöm Balázs. A hétvégén foglalkozom vele.
Balázs: ezek a példák is jók ilyesmi táblázatos/kiválasztós nézethez?
Igen Laci, mindkét példád alkalmas gyakorolni. Utóbbihoz beágyazott térkép is társítható, ha kétlépéses varázslót raksz össze: az 1. lépésben táblázatos a kiválasztás és a 2. lépésben navigálható térkép kerül az egyik órai feladat alapján a GUI-ba. A két lépés között váltani a
JTabbedPane
komponenssel lehet, amin a fülek megjelenítése tetszés szerint ki-/bekapcsolható.Balázs: Lillával dolgozunk a második példámon, amit jóváhagytál. A táblázatban nem jelenik meg az adatok kulcsa (ez az 1:N kapcsolatban az 1 oldal), mert ez nem kell a View-ba. Pedig a legördülő listához kell egy kulcs, mert ez az 1:N kapcsolatban a sok oldal). Hogyan oldjuk ezt meg? Még nem tartunk itt, de kelleni fog majd a válasz a befejezéshez.
Közben rájöttünk közösen, hogy a View-ból kimaradó kulcs a Model-ben benne van és össze is tudtuk kapcsolni. Mindent mindennel, amire szükség volt.
Szuper, gratulálok Lilla és Laci! Én is ezt javasoltam volna, de mire felébredtem 🙂 , már meg is oldottátok.