A fejezetet Kovács Vincent írta a Scala Swing Design dokumentum alapján.
A scala.swing
csomag a JAVA Swing
csomagon alapszik. A csomag segít
abban, hogy a Swing használata átláthatóbb és Scala „barát” legyen. A következő programkód tekinthető a scala.swing
"Hello Word!"-jének:
Az „egyszerű” Swing
alkalmazás tartalmaz egy main
objektumot, mint a fenti HelloWorld
mintaalkalmazás is, ami a SimpleSwingApplication
osztályból származik. Az alkalmazásnak
implementálnia kell a top
metódust, amelyben egy paraméternélküli Frame
-t kell
visszaadni. Ebben az esetben ez a MainFrame
, ami a Frame
osztály egyik leszármazottja.
A Frame
egy standard operációs rendszeri ablak, ami tartalmazza a minimalizáló,
maximalizáló és bezáró gombokat is. A MainFrame
bezáráskor automatikusan terminál maga az alkalmazás
is.
Indításkor a SimpleSwingApplication
gondoskodik a Swing
keretrendszer
megfelelő inicializálásáról és megnyitja a top metódus által visszaadott Frame
-t. A fenti implementáció
létrehoz egy fő keretet egy anonim osztálydefiníció segítségével:
Az anonim osztály definíció megengedi, hogy a MainFrame
összes változóját elérjük a „kapcsos”
zárójeleken belül. Elsősorban beállításra kerül a keret címe (title
paraméter), ami egy
"Hello, World!"
szöveg lesz, majd beállításra kerül a keretnek a tartalma (content
paraméter) is.
Sok függvény és osztály a scala.swing
-ben hasonlóképpen épül fel, mint a Java
-ban.
Például egy új gomb létrehozásához elég csak annyit írni, hogy:
Ha nagyon sok paramétert szeretnénk megadni, akkor ajánlott az anonim osztálydefiníciót alkalmazni, ami a kódot sokkalta olvashatóbbá teszi.
A SimpleSwingApplication
osztály a SwingApllication
osztály leszármazottja, ami
implementál egy alapértelmezett main
metódust. Ezen felül további metódusokat is implementál:
A startup
metódus az alapértelmezett main
metódus elején kerül
meghívásra és a kliens, vagyis a származtatott osztály, által kell implementálva lennie.
A quit
metódus bezárja magát az alkalmazást.
A shutdown
metódus a kliens által felül lehet definiálni, hogy az erőforrások a
megfelelőképpen kerüljenek felszabadításra az alkalmazás bezárásakor.
A SimpleSwingApplication
egy alapértelmezett implementációt tartalmaz a startup
metódusra, ami megjeleníti a kliens által implementált top
metódus által visszaadott
Frame
-t.
Az előzőekben láthattuk, hogy egy gombot adtunk hozzá a fő ablakkerethez. Egy bonyolultabb megjelenítéssel
rendelkező alkalmazásnál ez már nem elég. Ebben az esetben szükségünk van az egyes grafikus elemeket
konténerekbe rendezni. A scala.swing
-ben hasonlóan, mint a Java Swing
-ben két fajta
konténer komponens van: a panes
és a panel
.
A Java Swing
esetében a konténerek kettéválik az elrendezéstől. Ahhoz, hogy létrehozásra kerüljön
egy Java Swing
konténer, létre kell hozni egy JPanel
-t és egy hozzá tartozó
LayoutManager
-t:
A konténerhez tartozó komponensek a különböző add metódusokkal lehet hozzáadni, amit a
java.awt.Component
alaposztály tartalmazza:
A fenti példakódhoz egy új gombot a következő kóddal lehet hozzáadni:
A scala.swing
-ben a Java Swing
-gel ellentétben a konténerek és az elrendezések
egybeolvadnak.
A LayoutContainer
egy trait
, amelyből származtatni lehet a különböző
konténereket. Ehhez implementálni kell a következő absztrakt metódusokat:
constraintsFor
metódus az aktuális konténerhez tartozó megszorításokat kérdezi
le.
areValid
metódus fordítási időben ellenőrzi, hogy a megadott megszorítások
érvényesek-e és ha nem akkor egy opcionális hibaüzenetet ad.
add
metódus egy új komponenst ad hozzá a konténerhez a hozzá tartozó
megszorításokkal együtt.
A scala.swing
az eseményeket egy publisher
-ek (esemény kiváltók) és
reactor
-ok (esemény megfigyelők) felépítés alapján kezeli.
Minden scala.swing
komponens egyben egy esemény megfigyelő, amely figyeli az eseményeket és egy
esemény kiváltó, amely kivált különböző eseményeket. A következő egyszerű kódrészlet bemutatja, hogyan lehet
hőmérsékleteket átváltani Celsiusról Fahrenheitre eseményeknek a segítéségével:
A fenti mintakódban látható, hogy a kerethez hozzáadódik két szövegmező, amiben a felhasználó megadhatja a kívánt
hőmérsékleteket (celsius
, fahrenheit
változók). A TextField
objektumok az
EditDone
eseményeket hirdetik meg, ha a szövegmezőn a szerkesztés befejezésre kerül. Ahhoz, hogy ezt az
eseményt el tudjuk kapni a reactions
változóban található megfigyelőhöz kell hozzáadni az
szövegmezőket. A következő kódrészlettel kell kiegészíteni a programunkat:
Jelen esetben a reactions
az alkalmazás globális megfigyelő objektuma.
Először a listenTo
metódussal feliratkozunk a szövegmezők eseményeire. (A listenTo
a Reactor
alaposztály
egyik
metódusa, amely a SwingApplication
osztály egyik trait
-ja.) A következő részben egyenként
megadásra kerülnek azok az események, amelyeket fel szeretnénk dolgozni, vagyis a fahrenheit és a celsius
paraméterekben tárolt szövegmezők EditDone
eseményeire.
A feliratkozott események kiválasztását a scala.swing
egyszerű mintaillesztéssel végzi el.
A scala.swing
-ben mindent objektum egyedi, így a mintaillesztés könnyen működik. Ezzel szemben a
Java Swing
-ben azesemények két szakaszban hajtódnak végre. Először az esemény típusa, plusz a hozzá
Listener
metódus.
A következő kódrészlet megmutatja, hogy a Java Swing
-ben, hogyan kapunk el egy egyszerű egér
kattintást:
Ezzel ekvivalens kód a scala.swing
-ben a következőképpen néz ki:
Hatékonysági okokból az egér eseményei nem a Component
-ből származnak, hanem az egyik paraméteréből, a
mouse
-ből, ezért először fel kell a listenTo
metódussal erre iratkozni.
Az egész eseménykezelő rendszer a Publisher
, Reactor
és
a Reactions
osztályokon alapszik. Az API
használó felhasználók részére,
csak a Publisher
osztályban a publish
metódus az, ami érdekes. Ez értesíti a feliratkozott
reactor
-okat:
Ahhoz hogy egy osztály kiválthasson eseményeket a Publisher
osztályból kell származnia és a publish
metódus kell meghívnia az események kiváltásához.
A reactor
-ok feliratkozhatnak a különböző eseményekre, illetve le is iratkozhatnak a
Reactor
osztály következő metódusaival:
A Reactor
osztálynak van egy különleges paramétere, amely tulajdonképpen a reaction
-ok
gyűjteménye. Egy reaction
egy Reactions.Reaction
típusú objektum.
Új reaction
-t a +=
metódussal lehet hozzáadni, törölni pedig a
-=
metódussal lehetséges. Mindkettő metódus visszaadja a kapott reaction
objektumot:
A Java Swing
egy javax.swing.Action
interfészt biztosít, hogy az
eseményekre adott válaszok futtathatóak legyenek. Ezek különbeznek a Listener
-ektől, mégpedig abban,
hogy információkat szolgáltatnak, amelyek későbbiekben hasznosak lehetnek. Ezeknek lehetnek gyakran használt
változók például az akció neve, gomb esetén maga a gomb felirata, vagy egy menü elem esetében a menünek a címe, vagy
az akcióhoz tartozó billentyűrövidítés, stb.
A scala.swing
-ben erre az Action
trait
szolgál. A következő
beépített metódus nagyon kényelmessé teszi az akciók létrehozását:
Egy új akció létrehozásához csak elég annyit írni, hogy:
Egy komponenshez hozzárendelhető egy akció, amihez a Action.Triger
trait
-t
kell származtatnia. A követező kódrészlet bemutatja, hogy hogyan lehet egy gomb kattintásához egy akciót
hozzárendelni:
Mivel gyakori, hogy egy gombhoz akciókat rendeljünk hozzá, ezért van egy beépített gyár metódus is:
Ahhoz, hogy egy komponensnek megmondjuk, hogy nincs hozzárendelve egy akció az
ActionNoAction
objektumot kell hozzárendelni, ami egyben az alapértelmezett érték is:
A lista és táblázat nézek komponensek feladata, hogy megadott mennyiség elemet egy egységes elrendezésbe helyezze. A lista elrendezések az adatok függőlegesen, vagy vízszintesen felsorolva jelenítik meg. A táblázat elrendezéseket a megadott adatokat egy táblázatos formában jelenítik meg. Opcionálisan megadható az oszlop és a sor fejlécek is.
A ListView
osztálynak a konstruktora egy listát vár paraméterül, ami tartalmazza a
felsorolni kívánt
elemeket. Egy ListView
a következőképpen hozható létre:
A ListView
definiál egy selection
változót, amelynek köszönhetően a kliens információt
nyerhet az aktuális kiválasztott elemről. Ennek megfelelően a fenti kódrészletet kicsit átalakítva:
Ezután lekérdezhető az aktuálisan kiválasztott City
objektumot és azon belül is azoknak a nevei:
Ha fent megadott városokat megjelenítő ListView
-t kirajzoljuk a Frame
-re, akkor csak egy
összefűzött felsorolást fogunk kapni, valami hasonló formában:
A megjelenítő automatikusan az összes elemre meghívja a toString
metódust. Tehát, ha egy
City
-n meghívjuk a toString
metódust, akkor az automatikusan visszaadja a konstruktor
paraméterein szereplő változókat toString
formában.
A ListView
osztály példánya a ListView.Renderer
osztályt használja az
egyes elemek kirajzolásához. Egy új megjelenítő definiálásval ezt a listát szebbé, lehet tenni:
A fenti megjelenítő, csak a neveket fogja kirajzolni.
A Table
felépítése és a hozzá tartozó megjelenítők hasonlóak a ListView
-hoz.
A Table
osztály nem csak listák megjelenítését támogatja, de kétdimenziós tömbök megjelenítésére is
alkalmas. A táblázat egy eleme több mindent reprezentálhat, mint például, egy sort, oszlopot, vagy magát csak egy
cella elemet.
Az egyedi rajzolás a scala.swing
-ben hasonló a Java Swing-hez. Ehhez elegendő a megfelelő
Component
osztályt, vagy ennek egyik alosztályát példányosítani és a paintComponent
metódust felüldefiniálni.
Például egy vonal kirajzolása scala.swing
-ben:
A super.paintComponent
-t azért szükséges meghívni, hogy a háttér és a különböző dekorációk is
kirajzolásra kerüljenek. Ezután kirajzolunk egy szöveget és egy útvonalat, amikor a felhasználó húzza az egeret.
A Component osztályban a következő rajzoló metódusok vannak:
A paint
metódust a Swing
rajzoló mechanizmusa hívja meg, amikor az adott
komponenst ki akarja rajzolni. Az alapértelmezett paint
metódus meghívja a másik három rajzoló
metódust.