Tartalom
A Java nyelv korai időszakában (már az 1.0-ás verzióban) megjelent egy grafikus felhasználói felületek (graphical user interfaces, GUI) készítésére szolgáló programkönyvtár (library). Ennek eredeti tervezési céljai között az szerepelt, hogy segítségével egy minden platformon jól kinéző felhasználói felületet lehessen létrehozni, azonban ezt a célt nem sikerült elérni. Ez a programkönyvtár az absztrakt ablakozó eszközkészlet (abstract windowing toolkit, AWT) volt, amelynek segítségével minden platformra egyformán középszerűen kinéző felületeket lehetett létrehozni, ráadásul mindemellett sok kényelmetlen korlátozást is tartalmazott: többek között csak négyféle betűtípus használatára volt lehetőség és nem lehetett elérni az egyes operációs rendszerekben létező kifinomult felületelelemeket sem. Ezen túlmenően az AWT programozási modellje is nagyon kényelmetlen és nem eléggé objektumorientált volt.
A Java 1.1 már egy sokkal letisztultabb objektumorientált megközelítésű AWT eseménymodellt hozott (a JavaBeans komponensmodellel együtt), azonban az átalakulás azzal lett teljes, hogy a Java 2 (JDK 1.2) kialakításakor létrejött a Java Foundation Classes (JFC), amelynek a grafikus felhasználói felületekkel foglalkozó részét Swingnek nevezték, de emellett lehetőséget biztosít a kinézet és a felhasználói élmény (vagyis a look and feel) cserélhetőségére, API-kat biztosít az akadálymentesítés és kétdimenziós grafikák készítéséhez, és egy nemzetköziesítési (internationalization, röviden i18n) megoldás segítségével lehetővé teszi többnyelvű alkalmazások létrehozását is. Könnyen használható és könnyen érthető JavaBeans komponenseket biztosít a programozók számára, amelyekkel akár fogd és vidd (drag and drop) módszerrel, akár kézzel leprogramozva megfelelő felhasználói felületek hozhatóak létre.
Ebben a fejezetben a Swing keretrendszer alapelemeit mutatjuk be. A legtöbb Swing komponens a javax.swing csomagban illetve annak alcsomagjaiban található. A Swing keretrendszer rengeteg API-t, komponenst és egyéb lehetőségeket rejt. Éppen ezért fontosnak tartjuk megjegyezni, hogy e fejezetnek nem célja sem a Swing komponenseinek átfogó bemutatása, sem pedig az egyes tárgyalt komponensek metódusainak mindenre kiterjedő bemutatása. A Swing programkönyvtára hatalmas, ezért a fejezet célja csupán annyi, hogy betekintést nyújtson az alapvető elemekbe és fogalmakba, és kiindulópontként szolgáljon a további ismeretszerzéshez. Ebben nagy segítséget nyújthat a Swing tutoriál [ SwingTutorial ], illetve a témában íródott számos szakkönyv valamelyike [ SCHILDT2006 ].
Egy Swingben írt felület alapvetően komponensek hierarchiáiból és eseménykezelőkből épül fel. A legfelső szinten lévő komponensek, a tárolóobjektumok adják a többi objektum keretét. Ezek általában ablakok vagy dialógusablakok, amelyeken panelek helyezkednek el. A panelekhez további paneleket vagy egyéb komponenseket (például gombokat, szövegmezőket, táblázatokat, fastruktúrákat, legördülő listákat) adhatunk hozzá.
A Swingben írt felületek valójában komponensek hierarchiái,
amelyeket a legmagasabb szinten lévő komponensekbe, konténerekbe
helyezhetünk el. Minden Swing komponens konténer is egyben, amelybe
újabb komponensek tehetők, de mivel azok szintén konténerek, így újabb
komponenseket rendelhetünk hozzájuk. A
javax.swing.JComponent
osztály a korábbi
grafikusfelület-programozási API egyik központi osztályának, a
java.awt.Container
-nek a leszármazottja.
A legmagasabb szinten lévő komponensek, az ablakok a hierarchiák
gyökérelemei. Három olyan konténerosztály létezik, amely egy
gyökérelemet reprezentálhat: a JFrame
, a
JDialog
és a
JApplet
.
Minden GUI komponens csak egy konténerben szerepelhet egyszerre: ha áthelyezzük egy másik konténerbe, akkor az előzőből automatikusan törlődik. Azonos típusú objektumokból több is szerepelhet egy konténerben.
Menüsort csak legfelső szinten lévő komponensekhez adhatunk. A menüsor a tartalompanelen kívül helyezkedik el.
A legfelső szinten elhelyezkedő komponensek közül több is képezhet
gyökérelemet. Például, amennyiben egy alkalmazás több felületből áll:
egy JFrame
(főablak) és több
JDialog
(dialógusablakok) objektumból, akkor ezek
mindegyike gyökérelem. Appletek esetében azonban csak egy
JApplet
objektum alkothat gyökeret.
Miután létrehozunk egy új komponenst vagy komponenshierarchiát (amely maga is komponens), hozzáadjuk a keret objektum tartalompaneljéhez.
frame.getContentPane().addComponent(jTabbedPane1);
Ez az utasítás egy fülekkel ellátott lapokból álló panelt ad hozzá a tartalompanelhez.
A modern integrált fejlesztői környezetekben nem szükséges kézzel programoznunk a grafikus felhasználói felületeket. Az IDE olyan segédeszközöket (például palettát) biztosít, amelyek használatával a szerkesztési területre húzhatók a komponenesek, a környezet pedig legenerálja a megfelelő Java-kódot a háttérben.
A legelterjedtebb integrált fejlesztőeszközökben lehetőségünk van a felhasználói felület drag-and-drop módon történő fejlesztésére. A Netbeans a Swing GUI Builder (korábban Matisse) nevű felületépítő eszközkészletet tartalmazza, míg az Eclipse-hez a legelterjedtebb ilyen eszköz a WindowBuilder nevű plugin, amely az Eclipse for Java Developers változatban előre telepítve van, az egyéb Eclipse változatokban pedig az Eclipse Marketplace segítségével pluginként telepíthető. Az IntelliJ Idea is rendelkezik Swing GUI Designer-rel, és az Oracle JDeveloper is beépítetten tartalmaz ilyen eszközt.
Minden Swing komponens a JComponent
osztály leszármazottja, így örökli annak adattagjait és metódusait.
A leszármazott osztályok egyedi jellemzőin túl vannak olyan
tulajdonságok, amelyekkel minden Swing komponensnek rendelkeznie
kell. Ezek a tulajdonságok a közös ősben, a
JComponent
osztályban találhatók, és hét
területet ölelnek fel, amelyek a következők:
A komponens megjelenítésének testreszabása (keret, előtér, háttér, betűk).
A komponens állapotának lekérdezése és beállítása (engedélyezés, láthatóság, szerkeszthetőség).
A Figyelő (Observer) és a Parancs (Command) tervezési mintát megvalósító eseménykezelők (különböző listener objektumok) hozzárendelése a komponenshez.
A komponens megjelenítése (kirajzolása).
Azon komponenshierarchia kezelése, amelynek a komponens a gyökéreleme.
A tartalmazott komponensek elhelyezkedésének (layout) kezelése.
Információk és beállítások a komponens méretével és pozíciójával kapcsolatban.
A következő szakaszokban néhány alapvető komponenst ismerhetünk meg. Az összetettebb komponenseknél bepillanthatunk a mögöttük álló modellek és eseménykezelők működésébe is.
Az első csoportban az egyszerű szövegmezők találhatók. Ezek
egysoros, rövidebb szöveget kezelő komponensek. A második és a
harmadik csoportba a szövegterületek tartoznak, amelyek hosszabb,
többsoros szöveges adatok megjelenítésére és szerkesztésére
használhatók. A szövegmezők és –területek értékét a
getText
metódussal kérdezhetjük le, illetve a
setText
metódus segítségével állíthatjuk
be.
String username = jTextField1.getText(); String password = new String(jPasswordField1.getPassword()); jTextField1.setText(""); jPasswordField1.setText("");
Jelszót tároló szövegmező esetében a
getPassword
metódus használata javasolt a
getText
metódus helyett, azonban az
String
helyett char[]
-öt ad
vissza.
A listák és legördülő listák egy vagy több választási lehetőséget kínálnak a felhasználónak. Egyszerű listákat akkor célszerű használni, ha a felületen sok hely van, vagy fontos, hogy minden pillanatban minél több elem látsszon. Legördülő listákat (comboboxokat) akkor célszerű alkalmazni, ha kevés a hely a felületen, mert ezek a komponensek összezárt állapotban mindössze egyetlen sornyi helyet foglalnak el.
A listák és legördülő listák listamodellekkel dolgoznak, amelyek az adatokat tartalmazzák.
A következő kódrészlet bemutatja, hogyan készíthetünk saját listamodellt, valamint a Swing keretrendszer által biztosított alapértelmezett listamodellek használatát.
A modelleket a JList
komponens és a
JComboBox
komponens
setModel
metódusával adjuk át a
komponenseknek.
private List<String> categories;
A saját listamodell létrehozása például névtelen osztállyal
történhet. Miután a kategóriákat tartalmazó listát feltöltjük az
adatbázisból, átadjuk azt a listamodellnek. A felüldefiniált
getSize
metódus a kategóriákat tartalmazó
lista méretével tér vissza, a getElementAt
metódus pedig ennek a listának az adott elemét kérdezi le és adja
át.
private AbstractListModel categoryListModel; List<String> categories = new ArrayList<>(); for (List<Object> cols : entityManager.select("select distinct category from products")) { categories.add("" + cols.get(0)); } categoryListModel = new javax.swing.AbstractListModel() { @Override public int getSize() { return categories.size(); } @Override public Object getElementAt(int i) { return categories.get(i); } }; jList2.setModel(categoryListModel);
A következő kódrészletben a legördülő listának egy alapértelmezett modellt adunk át, amely az általunk beállított értékeket tartalmazza.
jComboBox1.setModel(new javax.swing.DefaultComboBoxModel(new String[]{"ÜGYFÉL", "ADMINISZTRÁTOR", "VEZETŐ"}));
A listák és legördülő listák egy
ListSelectionModel
objektumot használnak
elemeik kiválasztásának módjához. Három kiválasztási mód
lehetséges:
Egy időben csak egy listaelem választható ki. Ha a felhasználó egy másik elemet jelöl ki, az előző automatikusan deszelektálódik.
Több, egymást közvetlenül követő elem kiválasztása. Amikor a felhasználó egy új intervallumot választ, az előzőleg kijelölt elemek deszelektálódnak.
Ez az alapértelmezett kiválasztási mód. Az elemek bármely kombinációja választható. A felhasználó maga deszelektálja az elemeket, ha szükséges.
A kiválasztási modellt a
setSelectionMode
metódus segítségével
állíthatjuk be egy lista komponensen. A lista kiválasztási modellt
táblázatobjektumokra is alkalmazhatjuk.
Egyszeres kiválasztási mód beállítása.
jList2.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); ... jTable1.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
A Swing eseménymodellje hatékony és rugalmas. Bármely eseménytípust tetszőlegesen sok forrásból felismerhet több eseményfigyelő objektum (event listener). Egy esemény figyelésére egy objektum is beállítható, de egy listener objektum minden forrás minden eseményét is figyelheti, valamint egy esemény figyelésére egyetlen forrásból több listener objektum is ügyelhet.
Minden esemény egy objektum, amely információkat tartalmaz az eseményről és az esemény forrásobjektumáról. Az események forrása legtöbbször komponens vagy modell, de bármely objektumtípus funkcionálhat eseményforrásként.
A Swing az eseményeket csoportosítja is: beszél alacsony szintű és szemantikus eseményekről. Az alacsony szintű események főként az ablakozó rendszer eseményei, mint amilyenek például a közvetlenül a felhasználótól érkező billentyűzet- és egéresemények. A szemantikus események ezeknél magasabb szintűek, és felhasználói bemenet váltja ki: például rákattint egy gombra. Jellemző, hogy egy szemantikus esemény mögött alacsony szintű események egész sorozata húzódik meg: egy gombnyomáshoz, mint szemantikus eseményhez az egérkurzor pozícionálására, az egérgomb lenyomására és felengedésére is szükség van (ezek mind alacsony szintű műveletek). Ráadásul ugyanazt a szemantikus eseményt több alacsony szintű eseménysoorozattal is elérhetjük, hiszen a gombnyomás szemantikus esemény pusztán a billentyűzet segítségével is kiváltható: a TAB billentyűvel kiválasztva a gombot a szóköz illetve az Enter billentyű lenyomásával.
A szemantikus eseményeket négy osztály reprezentálja:
az ActionEvent
, amely ezek közül
a leggyakoribb, gombnyomáskor, menüválasztáskor,
listaelem-kiválasztáskor, illetve a szövegmezőn
Enter leütésekor keletkezik ilyen
esemény;
az AdjustmentEvent
, amely egy
görgetősáv (scroll bar) használata során következik be;
az ItemEvent
akkor áll elő, ha a
felhasználó jelölőnégyzetek segítségével vagy listaelemekből
kiválaszt valamit;
míg végül a TextEvent
akkor jön
létre, ha egy szövegmező vagy szövegterület tartalma
megváltozik.
Az alacsony szintű események osztályai az alábbiak:
ComponentEvent
, amely amellett,
hogy komponensek átméretezése, mozgatása, megjelenítése
illetve elrejtése során következik be, az összes alacsony
szintű esemény ősosztálya is;
KeyEvent
, amely egy billentyű
lenyomása és felengedése esetén generálódik;
MouseEvent
, amely az egérkurzor
mozgatása, az egyes egérgombok lenyomása és felengedése, és
vonszolás során jön létre;
FocusEvent
, amelynek a
segítségével arról értesülhhetünk, ha egy komponens megkapta
vagy éppen elvesztette a fókuszt;
WindowEvent
, akkor generálódik,
ha egy ablakot aktiváltak, deaktiváltak, ikon állapotúra
kicsinyítettek, ikon állapotúról felnagyították, vegy éppen
bezárták; és végül
ContainerEvent
, ami egy-egy
komponens konténerhez adásakor vagy onnan való törlésekor jön
létre.
Ha csak lehet, a szemantikus eseményekre kell feliratkoznunk, nem pedig az alacsony szintűekre. Ennek több előnye is van: egyrészt így tudjuk a kódunkat a leginkább hordozhatóvá és robusztussá tenni, másrészt pedig így akkor is értesülünk egy eseményről (például egy nyomógombra kattintásról), ha azt nem az elvárt módon végezték. Egy nyomógomb megnyomását például csak egérrel érhetjük el, hanem a billentyűzettel is. Ha ekkor a szemantikus esemény helyett csupán arra iratkoztunk volna fel, hogy egy adott területen (ahol a gomb van) történik-e kattintás és felengedés egérgomb segítségével, akkor a billentyűzet segítségével végzett „gombnyomásról” bizony lemaradnánk.
A következő táblázat a komponensek és a hozzájuk rendelhető figyelő objektumok típusát tartalmazza.
7.1. táblázat - Komponensek és figyelőik [SwingTutorial]
Komponens | ActionListener | CaretListener | ChangeListener | DocumentListener,UndoableEditListener | ItemListener | ListSelectionListener | WindowListener | További listener fajták |
---|---|---|---|---|---|---|---|---|
button |
![]() |
![]() |
![]() | |||||
check box |
![]() |
![]() |
![]() | |||||
color chooser |
![]() | |||||||
combo box |
![]() |
![]() | ||||||
dialog |
![]() | |||||||
file chooser |
![]() | |||||||
frame |
![]() | |||||||
list |
![]() | ListData | ||||||
password field |
![]() |
![]() |
![]() | |||||
radio button |
![]() |
![]() |
![]() | |||||
table |
![]() | TableModel, TableColumnModel, CellEditor | ||||||
text area |
![]() |
![]() | ||||||
text field |
![]() |
![]() |
![]() |
Egy tevékenységfigyelő (action listener) implementálásával
megadhatjuk, hogy mi történjen, ha a felhasználó egy bizonyos
tevékenységet végez. A felhasználó által végzett tevékenység lehet
például egy gomb megnyomása, egy listaelem kiválasztása, vagy az
Enter billentyű megnyomása egy szövegmezőn.
Mindennek az az eredménye, hogy egy
actionPerformed
üzenet kerül elküldésre
minden olyan tevékenységfigyelő objektumnak, amely fel van iratkozva
az esemény forráskomponensére.
Egy tevékenységfigyelő implementálása a következő lépésekből áll:
Egy eseménykezelő osztály deklarálása, amely
implementálja az ActionListener
interfészt.
public class MyActionListener implements ActionListener { ... }
Az interfész actionPerformed
metódusának implementálása. A példában a kombóbox segítségével
kiválasztott elemet (egy szerepkört) a
currentRole
nevű változónak adjuk
értékül.
public void actionPerformed(java.awt.event.ActionEvent evt) { JComboBox cb = (JComboBox) evt.getSource(); currentRole = (String) jComboBox1.getSelectedItem(); }
Amennyiben az eseménykezelőt több eseményforráshoz is
csatoljuk (regisztráljuk), és szeretnénk attól függő
feldolgozást végezni, hogy melyik volt az az eseményforrás,
amelyen az esemény ténylegesen bekövetkezett és amely miatt
az eseménykezelő aktivizálódott, akkor az ActionEvent
objektumból tudjuk mindezt kinyerni, ahogyan a fenti példa
is mutatja (evt.getSource()
). Erre általában
nincs szükségünk, mert sokszor vagy csak egy
eseményforrással dolgozunk, vagy ha több is van belőlük, nem
feltétlenül van szükség forrásfüggő feldolgozásra. Ekkor a
paramétert gyakorlatilag figyelmen kívül hagyjuk.
Az osztály egy példányának egy vagy több komponens
figyelésére történő feliratkoztatása. Ez tulajdonképpen nem
más, mint a Megfigyelő (Observer) minta megjelenése, amikor a
megfigyelő (ez esetben a
MyActionListener
példány) regisztrálja
magát a megfigyeltnél (ez a jComboBox1
változónévvel hivatkozott kombóbox).
jComboBox1.addActionListener(new MyActionListener());
Ezt követően, ha a felhasználó elvégez egy bizonyos
tevékenységet, a komponens elindít egy tevékenységeseményt. Ez az
ActionListener
interfészt megvalósító
objektum actionPerformed
metódusának
meghívását eredményezi. A metódus egyetlen paramétere egy
ActionEvent
objektum, amely információt adhat
az eseményről és annak forráskomponenséről.
A tevékenységfigyelőt gyakran névtelen osztályként
valósítjuk meg. Különösen így van ez akkor, ha csak egyetlen
felületelemhez tartozik az eseménykezelő, hiszen akkor elég csak
egyetlen helyen, a megfigyeltnél történő regisztációkor
(addActionListener
hívás)
hivatkozni.
A JTable
osztály objektumainak
segítségével táblázatos adatokat tudunk megjeleníteni és kezelni. A
táblázatok oszlopokból és sorokból állnak, amelyeknek metszetében a
cella található. A táblázat adatait a kódban egy kétdimenziós
objektumtömbként vagy Vector
-ok
Vector
-aként adhatjuk meg
(adatok
paraméter).
Object[][] adatok = {...}; Object[] oszlopnevek = {...}; JTable tablazat = new JTable(adatok, oszlopnevek);
Első konstruktor: public JTable(Object[][] adatok,
Object[] oszlopnevek);
Második konstruktor: public JTable(Vector adatok,
Vector oszlopnevek);
Egy táblázat objektum sorain és oszlopain szűrési és rendezési műveleteket is végezhetünk, amelyekhez saját implementációt adhatunk.
Egy táblázaton összetettebb módon hajthatunk végre kijelöléseket, mint listák esetében, habár az összetettebb kijelölések visszavezethetők az egyszerűbbekre. A táblázatban sorokat, oszlopokat, és cellákat jelölhetünk ki. A kiválasztás módja lehet összefüggő, valamint nem összefüggő többszörös, vagy egyszeres.
A legtöbb Swing komponens mögött áll egy modell. Például egy
nyomógomb (JButton
) objektum rendelkezik egy
ButtonModel
interfészt megvalósító
objektummal, amely a gomb állapotáról tárol információt. Némelyik
komponenshez több modell is hozzá van rendelve, például egy
JList
objektum egy
ListModel
-t használ a tartalma
tárolására és egy
ListSelectionModel
-t az aktuális
kijelölések számára.
Legtöbbször nem szükséges tudnunk ezekről a modellekről, elegendő magát a komponenst programoznunk.
Mindazonáltal, szükség van modellekre az adatok rugalmas tárolásának és elérésének érdekében. Például, egy egyedi tábla esetében javasolt saját táblamodell implementációt megadnunk, ha az alapértelmezett modell nem felel meg az elvárásoknak.
A modellek nem csupán tárolják az adatokat, hanem automatikusan továbbítják az adatokon bekövetkezett változásokat a figyelő objektumok felé. Például egy listaelem hozzáadásánál a komponens helyett a listamodell megfelelő metódusa hívódik meg. Ha változás következik be, a listamodell értesíti a megjelenítő komponenst és a feliratkozott figyelőket, így a felület azonnal frissül.
A Swing egy módosított modell–nézet–vezérlő (Model–View–Controller) minta mentén épül fel, ahol a modell jól elkülöníthető a nézettől és a vezérlőtől, azonban a megjelenítés és a vezérlő egymástól nem teljesen szétválasztható.
Minden táblázatobjektum mögött áll egy táblamodell, amely a
táblázat adatait kezeli. A táblamodell objektumnak implementálnia
kell a TableModel
interfészt. Ha
nem rendelünk táblamodellt egy táblázat komponenshez,
automatikusan hozzárendelődik egy úgynevezett
DefaultTableModel
típusú
objektumpéldány.
public class MyTableModel extends AbstractTableModel { protected Object[][] tableData; protected List<String> columnNames;
A legördülő lista, amelynek segítségével a felhasználó kiválaszthatja, hogy a könyvet a polcra vagy a kosárba teszi-e.
private final JComboBox moreSelection;
A táblázat sorainak számát visszaadó metódus.
@Override public int getRowCount() { return this.tableData.length; }
A táblázat oszlopainak számát visszaadó metódus.
@Override public int getColumnCount() { return this.columnNames.size(); }
Az adott cellában található értéket visszaadó metódus.
@Override public Object getValueAt(int rowIndex, int colIndex) { return this.tableData[rowIndex][colIndex]; }
Adott oszlop nevét visszaadó metódus.
@Override public String getColumnName(int colIndex) { return this.columnNames.get(colIndex); }
Ez a metódus az adott oszlop típusát határozza meg. Ha nem implementáljuk a metódust, akkor minden oszlop értéke szövegként jelenik meg. A példa azt mutatja be, hogy mit tegyünk akkor, ha a logikai értékek megjelenítését jelölőnégyzet (checkbox) segítségével szeretnénk megvalósítani.
@Override public Class getColumnClass(int colIndex) { Class c = getValueAt(0, colIndex).getClass(); if (c.equals(Boolean.class)) { if (colIndex != selectionColIndex) { return Object.class; } } return c; }
Ez a metódus megmondja, hogy az adott cella szerkeszthető-e. Csak akkor szükséges implementálni, ha a táblázat szerkeszthető.
@Override public boolean isCellEditable(int rowIndex, int colIndex) { if (colIndex < this.selectionColIndex) { return false; } else { return true; } }
Az adott cellában található értéket beállító metódus. Csak akkor szükséges implementálni, ha a táblázat adatai változhatnak.
@Override public void setValueAt(Object value, int rowIndex, int colIndex) { this.tableData[rowIndex][colIndex] = value; fireTableCellUpdated(rowIndex, colIndex); selectionValuesById.put(this.tableData[rowIndex][0], value); }
A fireTableCellUpdated
metódus
hívásának hatására az összes érdeklő figyelő értesítést kap
arról, hogy a [rowIndex, colIndex] cella tartalma
frissült.
Egyazon táblamodell objektumra akár több figyelővel is
feliratkozhatunk, amelyek így a modell adatain (állapotán)
bekövetkezett változásokról értesítést kapnak. A táblamodell
figyelőinek a TableModelListener
interfészt kell implementálniuk.
public class MyTableModelListener implements TableModelListener { public MyTableModelListener() { ... table.getModel().addTableModelListener(this); } public void tableChanged(TableModelEvent e) { //a kiválasztás első sora int row = e.getFirstRow(); //a kiválasztott oszlop int column = e.getColumn(); //az esemény forrása TableModel model = (TableModel)e.getSource(); //a kiválasztott oszlop neve String columnName = model.getColumnName(column); //a cellában található érték Object data = model.getValueAt(row, column); ... } }
Amennyiben a felület osztályából elérjük a táblát, nincs
szükség TableModelEvent
objektumra. Ekkor egy
egyszerű actionPerformed
metódus kezeli az
eseményt.
private void jButton14ActionPerformed(java.awt.event.ActionEvent evt) { int selectedRow = jTable1.getSelectedRow();
Amennyiben a táblázat rendezve is van, az alábbi metódust is meg kell hívnunk a helyes index megtalálásához.
selectedRow = jTable1.convertRowIndexToModel(selectedRow); // Könyv azonosítója (ISBN) idOfSelectedProduct = (String)jTable1.getModel().getValueAt(selectedRow, 0); }
Az adatok megváltozásának eseménye csak úgy váltható ki, ha a
táblamodell tudatában van annak, hogy hogyan kell egy
TableModelEvent
objektumot előállítani. Ez
bonyolult feladat, azonban a
DefaultTableModel
osztály megfelelő
implementációt biztosít hozzá. Így ha saját implementációt írunk, a
DefaultTableModel
osztály kiterjesztése
javasolt. Amennyiben az alapértelmezett táblamodell osztály mégsem
felel meg alaposztályként, az
AbstractTableModel
osztályt kell
kiterjesztenünk. Ebben az esetben saját osztályunknak elegendő a
következő metódusok egyikét hívni, amikor egy külső forrás módosítja
a táblamodell adatait.
Metódus | Változás |
---|---|
fireTableCellUpdated
| Egy cella módosítása |
fireTableRowsUpdated
| Sorok módosítása |
fireTableDataChanged
| A teljes táblázat adatainak változása (csak az adatok) |
fireTableRowsInserted
| Új sor beszúrása |
fireTableRowsDeleted
| Sorok törlése |
fireTableStructureChanged
| A táblázat adatainak és szerkezetének módosítása |
A következó kódrészlet egy
fireTableCellUpdated
metódus hívását
mutatja be.
@Override public void setValueAt(Object value, int rowIndex, int colIndex) { this.tableData[rowIndex][colIndex] = value; fireTableCellUpdated(rowIndex, colIndex); selectionValuesById.put(this.tableData[rowIndex][0], value);}
Egy elrendezéskezelő vagy elhelyezéskezelő a
LayoutManager
interfészt implementáló
objektum, amely a komponensek méretét és pozícióját határozza meg egy
tárolón belül.A Swing komponensek alapértelmezett elrendezéskezelővel
rendelkeznek, amely például panelek esetében a
FlowLayout
, míg tartalompaneleknél a
BorderLayout
. Az alapértelmezett
elrendezéskezelők leválthatók, erre azonban legtöbbször nincs
szükség.
JPanel panel = new JPanel(new BorderLayout()); ... Container contentPane = frame.getContentPane(); contentPane.setLayout(new FlowLayout());
Amennyiben nem használunk elhelyezéskezelőt, abszolút pozícionálást kell végeznünk, amely értelmében minden komponensnek explicit módon kell megadnunk a méretét és a pozícióját a tárolón belül. Ezek az értékek rögzítettek, amelyek például az ablak átméretezésekor sem frissülnek. Éppen ezért ez kerülendő.
BorderLayout
: a tartalompanelek
(content pane) alapértelmezett elrendezéskezelője, így az összes
legfelső szintű konténerben (ablakkeretben, dialógusablakban és
appletben) ezt használhatjuk alapból. A rendelkezésre álló
területet öt részre osztja fel: fölső, alsó, jobb oldali, bal
oldali és középső, ahogyan az az ábrán is látható.
BoxLayout
: a komponenseket egy
sorba vagy egy oszlopba helyezi, figyelembe véve a komponens
által igényelt maximális méretet.
CardLayout
: segítségével olyan
felület valósítható meg, ahol a különböző időpontokban különféle
komponensek jelennek meg. Tulajdonképpen fülek segítségével
elért lapokként gondolhatnuk rá, ahol sokszor tényleges fülek
helyett egy combobox segítségével válthatunk az egyes lapok
között.
FlowLayout
: a
JPanel
-ek alapértelmezett
elrendezéskezelője, sorfolytonosan tölti ki a rendelkezésre álló
teret. Ha egy sorban már nincs elegendő helye egy elhelyezendő
komponens számára, akkor új sort kezd.
GridLayout
: a komponensek számára
azonos méretet határoz meg egy n×m-es rácshálóban.
GridBagLayout
: nagyon fejlett és
rugalmas elrendezéskezelő, a GridLayout
továbbfejlesztéseknét jött létre. A komponenseket szintén egy
rácsháló alapján helyezi el, de megengedi, hogy egy-egy
komponens több sort, illetve oszlopot is elfoglaljon. A rácsháló
egyes sorai akár eltérő magasságúak, oszlopai pedig akár eltérő
szélességűek is lehetnek. Nagyfokú rugalmassága mellett az egyik
legnehezebben használható elrendezéskezelő is egyben.
GroupLayout
: a grafikus
felhasználói felületet drag-and-drop módszerrel öszeállító
integrált fejlesztői keretrendszerek számára jött létre.
Külön-külön foglalkozik a vízszintes és a függőleges
elrendezéssel, vagyis minden komponens helyét kétszer kell
definiálni: egyszer vízszintesen, egyszer pedig függőlegesen
kell elhelyezni.
SpringLayout
: szintén
IDE-támogatásként jött létre. Lehetővé teszi, hogy az egyes
komponensek szélei között pontos kapcsolatokkal írhassuk le
komponenseink elhelyezkedését. Definiálható segítségével
például, hogy adott komponens bal széle és egy másik komponens
jobb széle között mekkora legyen a távolság (amely akár
dinamikusan is számítható).
Az elhelyezéskezelőt nem szükséges kézzel beállítanunk, az integrált fejlesztői keretrendszerek biztosítanak olyan eszközöket, amelyek segítségével ez egyszerűen elvégezhető.
Amennyiben úgy döntünk, hogy mégis segédprogram nélkül, kézzel állítjuk be a layout menedzsereket, a következő rész nyújt segítséget annak eldöntésében, hogy milyen esetben mely elrendezéskezelőt érdemes használni.
Amennyiben a lehető legnagyobb helyen szeretnénk a
komponenst megjeleníteni, akkor a
GridLayout
vagy a
BorderLayout
a megfelelő választás, egy
komponens esetében. Több komponensnél érdemesebb a
GridBagLayout
-ot használni.
Amennyiben néhány komponenst akarunk egy sorban
megjeleníteni, átméretezés nélkül, akkor a
FlowLayout
, a
BoxLayout
vagy a
SpringLayout
a megfelelő
választás.
Amennyiben néhány komponenst akarunk megjeleníteni
egyforma méretben oszlopokba és sorokba rendezve, akkor a
GridLayout
szerinti elrendezést érdemes
választanunk.
Amennyiben néhány komponenst szeretnénk egy sorban vagy
egy oszlopban megjeleníteni, változó térközzel, igazítással és
méretezéssel, akkor a BoxLayout
-ot
érdemes használni.
Amennyiben rendezett oszlopokat szeretnénk megjeleníteni
egy űrlap elrendezéséhez hasonló módon, ahol a címkék az egyes
szövegmezők megnevezésére szolgálnak, a
SpringLayout
a legmegfelelőbb
választás.
Amennyiben összetett elrendezést kívánunk használni
nagyszámú komponenssel, valamely rugalmas elrendezéskezelőt
érdemes választanunk, például a
GridBagLayout
-ot vagy a
SpringLayout
-ot. Egy másik módszer, ha
az összetett elrendezést szétválasztjuk kisebb panelekre,
amelyeken önálló elrendezést állítunk be önálló kezelővel, ez
általában véve is jó stratégia a bonyolultság
csökkentésére.
Az elhelyezéskezelők alapvetően két dolgot tesznek: kiszámolják a tároló minimális, maximális és preferált méretét, és elrendezik a tároló beágyazott komponenseit. Ezek a komponenesek ráadásul rendelkezhetnek saját elrendezéskezelővel, amelyek előnyt élveznek a tartalmazó komponens elrendezéskezelőjével szemben, vagyis a specifikusabb komponensre előírt elhelyezés felülírja az általánosabbra előírtat.
Egy konténer állapota lehet érvényes vagy érvénytelen. Ahhoz,
hogy egy konténer érvényes állapotba kerüljön, minden gyermekén
végbe kell mennie az elrendezésnek, és így érvényes állapotba kell
kerülnie. Az elrendezés elvégzését és a komponens érvényes állapotba
juttatását a Container
osztály
validate
metódusa végzi.
Miután egy komponens elkészült, még érvénytelen állapotban
van. Első alkalommal a Window.pack
metódus
hívása érvényesíti az ablakot, és végzi el az alkomponensek
elrendezését.