Bevezetés
Ebben a leckében arról lesz szó, hogy hogyan készíthetünk grafikus felhasználói felületet programjainkhoz a NetREXX segítségével. A JDK nagyon sok osztályt tartalmaz, amelyek felhasználhatók - elvileg operációsrendszer-független - grafikus felületek készítésére. Mivel a NetREXX nem tartalmaz ilyen osztályokat, ezért rá vagyunk utalva a Java környezet által rendelkezésre bocsátott eszközkészletre. A Java környezet ezen része folyamatosan változik. Az 1.0-s és 1.1-es verziók között például igen lényeges különbségek vannak. Leckénkben az 1.1-es Java környezetbôl indulunk ki, mivel ez még viszonylag korszerûnek mondható és szinte minden operációs rendszerre elérhetô.
Alkalmazások és appletek
A Java nyelven írt programok két csoportra oszthatók attól függôen, hogy milyen környezetben futnak. Az appletek közé azon programok tartoznak, amelyek nem képesek önálló mûködésre, mivel csak Web-böngészôben, vagy esetleg applet nézegetôben futtathatók. Az appleteket minden esetben HTML fájlba ágyazzák. Az alkalmazások ugyanakkor böngészô nélkül, önállóan futtatható programok, használatukhoz csak a Java környezetre van szükség. Mivel az appleteket többnyire az interneten keresztül, Web-oldalak részeként töltjük le, ezért ezekkel az esetek túlnyomó részében sokkal óvatosabban bánunk. A Java környezet alapesetben például nem engedélyezi, hogy az applet hozzáférjen a lokális állományokhoz, és csak azzal a szerverrel létesíthet kapcsolatot, amelyikrôl letöltöttük. Az alkalmazásokra ugyanakkor nem érvényesek ezek a korlátozások.
Az appletek általános struktúrával rendelkeznek. Minden applet a Java környezet által definiált applet osztályból kell hogy származzon. Az applet osztály hat metódust definiál, amelyeket a futtatókörnyezet használ az applet mûködtetéséhez. A metódusokat és azok funkcióját összefoglaltuk az alábbi táblázatban:
| Metódus: | Feladat: |
| init | Az applet inicializálását végzô metódus, az applet betöltésekor fut. |
| start | Az init után végrehajtott metódus. Minden olyan esetben végrehajtódik, amikor az appletet tartalmazó oldal láthatóvá válik. |
| stop | Az applet futását leállító metódus. Minden olyan esetben végrehajtódik, amikor az appletet tartalmazó oldal eltûnik. Ez a metódus a destroy metódus végrehajtása elôtt is meghívódik. |
| destroy | Az applet mûködését záró metódus, amely a lefoglalt erôforrásokat szabadítja fel. |
| update | Az applet újrafestését végzô metódus. Az alapimplementáció újrafesti a hátteret és meghívja a paint metódust. |
| paint | A grafikus komponenseket újrafestô metódus. |
A Java alkalmazások a parancssorból indíthatók. Az egyetlen metódus, amelyet a futtatókörnyezet automatikusan meghív, a main metódus, amely egy publikus osztálymetódus:
method main( ARGS = String[] ) public static
A main metódus tipikus feladata a parancssorban megadott paraméterek értelmezése és az alkalmazás fôablakának megjelenítése. A NetREXX-ben a main metódus definiálása elmaradhat, mivel ezt szükség esetén elvégzi helyettünk a fordítóprogram. A továbbiakban nézzük meg elsô példaprogramunkat, amely jó példája egy kezdetleges, ám mégis teljes értékû alkalmazásnak, amely megjeleníti a futtatás idôpontját:
/* lecke05a.nrx - az elsô grafikus NetRexx alkalmazásunk */
import java.text. -- a SimpleDateFormat osztály miatt importálni kell
class lecke05a
Properties inheritable
window = Frame
method main( args=String[] ) public static
lecke05a() -- létrehozunk egy objektumot a lecke05 osztály alapján
-- és megjelenítjük az alkalmazás fôablakát
method lecke05a()
window = Frame("Az elsô GUI alkalmazás" ) -- a keret létrehozása
window.setSize(210,100) -- a méretek beállítása
d = window.getToolkit().getScreenSize() -- pozícionálás középre
s = window.getSize()
window.setLocation((d.width - s.width) % 2, (d.height - s.height)%2)
f = SimpleDateFormat("H:mm:ss" ) -- az idô lekérdezése
text = Label("Az indítás idôpontja:" f.format(Date()), Label.CENTER)
window.add("Center", text) -- a középre igazított szöveg kiíratása
-- figyeljük a bezárást
window.addWindowListener( CloseWindowAdapter() )
window.setVisible(1) -- megjelenítjük a kész felületet
class CloseWindowAdapter extends WindowAdapter
method windowClosing( e=WindowEvent )
exit 0
A main metódus készít egy objektumot a lecke05a osztály alapján. Az osztály konstruáló metódusa definiál egy keretobjektumot (frame) és megjeleníti azt a képernyôn. Mivel nem akarjuk, hogy a keret az alapértelmezés szerinti bal felsô sarokban kerüljön megjelenítésre, ezért lekérdezzük a képernyô méretét a Toolkit osztály segítségével és középre pozícionáljuk az ablakot. Aki az appletek írásában járatos, az már valószínûleg észrevette, hogy nem használjuk a paint metódust, amely egyébként az ablak újrafestését végezné. Mivel az alkalmazások felülete grafikus objektumokból épül fel, ezért nem kell az ablakot újrafesteni. Ezek után lekérdezzük a pontos idôt és megalkotjuk az ablak közepén megjelenítendô üzenetet. A keret azt is lehetôvé teszi, hogy lekezeljük az ablak bezárását jelzô üzenet. Mivel alapesetben a keret ezt az üzenetet figyelmen kívül hagyja, ezért kibôvítjük az alkalmazást ennek az eseménynek a figyelésével (WindowListener), hogy bekövetkezésekor kiléphessünk a programból. A futtatás után az alábbi ábrán látható ablakot kell kapnunk:
![[NTRX0501]](ntrx0501.gif)
Lehetôség van arra is, hogy egy programot úgy írjunk meg, hogy az appletként és alkalmazásként is funkcionáljon. Az általános recept a következô. Írjuk meg a programot, mintha az applet lenne! Definiáljunk egy main metódust, amely készít egy objektumot az applet osztálya alapján, definiálja a keretet, megjeleníti azt, majd pedig meghívja az applet init metódusát. Ezzel elérjük, hogy az alkalmazásként történô futtatáskor a main metódus rendezze a keret meghívását és az applet elindítását. Appletként történô futtatáskor a környezet (böngészô) felelôs a keret elkészítéséért és az init metódust is az hívja meg.
Építôelemek
A grafikus felületek készítéséhez felhasználható építôelemek mindegyike a JDK Component osztályból származik. Az elemeket reprezentáló osztályok metódusok sorozatát tartalmazzák, amelyekkel a grafikus elemek értéke és kinézete manipulálható. Az alábbi táblázatban összefoglaltuk a JDK legfontosabb grafikus elemeit, illetve azok funkcióját. Mivel a részletes ismertetés túlmenne leckénk keretein, ezért itt csak utalunk arra, hogy további információ található a JDK dokumentációjában.
| Osztály: | Leírás: |
| Label | Statikus szöveg megjelenítésére szolgáló objektum. A szöveget a felhasználó nem tudja módosítani. |
| TextField | Szövegbeviteli mezô. A felhasználó adja meg az objektum által megjelenítendô szöveget. |
| TextArea | Többsoros szövegbeviteli mezô. Ugyanazt tudja, mint a TextField, valamint többsoros szöveg bevitelét is lehetôvé teszi. |
| Button | Szöveggel ellátott nyomógomb. |
| Checkbox | Logikai 0-át vagy 1-et modellezô választómezô. Bejelölt állapotban az értéke a logikai igaznak, bejelöletlenben pedig hamisnak felel meg. A választómezôket csoportba lehet szervezni a CheckboxGroup osztállyal. Ebben az esetben a választómezôk rádiógombokként funkcionálnak, vagyis az azonos csoportba tartozó gombok közül egyszerre csak egy lehet bekapcsolt állapotban. |
| List | Választható karakterláncok listája. A beállítástól függôen egy, vagy pedig egyszerre több karakterlánc is kiválasztható. |
| Choice | Lenyíló lista. A List osztály helytakarékosabb formája. A Choice listájából egy elem mindig ki van választva, a List esetében ez nem kötelezô. |
| Scrollbar | Csúszka, amellyel (bizonyos határok között) egy érték állítható be. Csúszkarendszert valósíthatunk meg a ScrollPane osztállyal. |
| Menubar | A keretablakhoz tartozó menüsor (fômenü). Csak alkalmazásban lehet használni, hiszen az appletek nem rendelkeznek keretablakkal. |
| Menu | A menüsorból lenyíló menü (almenü). |
| Menuitem | Az almenü eleme. |
| CheckboxMenuitem | Választható menüelem. |
| PopUpMenu | A keretablak menüsorától független, rendszerint a jobb egérklikkantásra (vagy felengedésre) elôbukkanó menüsor. Mivel független a kerettôl, ezért appletekben is lehet alkalmazni. |
Az alábbiakban bemutatunk egy példaprogramot, amely a választómezôk használatát demonstrálja. A programban nincs main metódus, ezért csak appletként futtatható. Az ehhez szükséges HTML fájlt (lecke05b.htm) mellékeltük a példaprogramoz.
/* lecke05b.nrx */
class lecke05b extends Applet
method init()
setLayout(GridLayout(0,1)) -- egy oszlopba rendezzük az elemeket
cg = CheckboxGroup() -- a CheckboxGroup létrehozása
add(Checkbox("Gomb 1", cg, 0)) -- az elsô checkbox tag
add(Checkbox("Gomb 2", cg, 1)) -- a második checkbox tag
add(Checkbox("Gomb 3", 1 )) -- a harmadik elem
add(Checkbox("Gomb 4", 1 )) -- a negyedik elem
Az applet betöltése után automatikusan meghívódik az init metódus, amely elsô sora egy oszlopba rendezi a grafikus elemeket. Ezután létrehozunk egy CheckboxGroup objektumot. Az elsô két Checkboxot (Gomb 1 és Gomb 2) hozzáadjuk ehhez a csoporthoz, hogy ezek egy rádiógomb-komplexumot képezzenek. Alapesetben a Gomb 2 van bejelölve, amelyet a megfelelô Checkbox utasítás 1-es paramétere jelez. A 3. és 4. gomb közönséges választómezôk lesznek, amelyek alapesetben szintén be vannak jelölve. Az applet a böngészôben valahogy úgy fog kinézni, mint ahogy azt az alábbi képen is láthatjuk:
![[NTRX0502]](ntrx0502.gif)
A felületmenedzser
A felületmenedzser (layout manager) feladata, hogy egy elôre megadott filozófia alapján minden körülmények között optimálisan rendezze el a grafikus elemeket a rendelkezésre álló felületen. A felületmenedzser megkönnyíti a programozók feladatát, mivel nem kell különösebben törôdniük azzal, hogy hogyan is fog kinézni az alkalmazás, ha a felhasználó például átméretezi az ablakot. A felület egyes részeinek külön felületmenedzserük is lehet. Ez azt jelenti, hogy az alkalmazás felületét több részre bonthatjuk (konténerek), s ezeken a részeken belül más és más stratégia alapján rendeztethetjük el az elemeket. Minden konténer rendelkezik egy alapértelmezett felületmenedzserrel. Ablakok esetében ez az úgynevezett BorderLayout, paneleknél pedig a FlowLayout menedzser. A panelek olyan speciális konténerek, amelyek egymásba is ágyazhatók. Mindegyik felületmenedzser használja a minimális és a kedvelt méret paramétereket. Egy építôelem minimális mérete az a méret, amely alá semmiképpen sem csökkenhet a mérete. A kedvelt méret pedig az a méret, amely alkalmazása esetében a legjobban néz ki az adott objektum.
Az egyik legegyszerûbb felületmenedzser a FlowLayout menedzser. A filozófiája roppant egyszerû. A rendelkezésre álló terület bal felsô sarkában kezdi el fokozatosan jobbra haladva a komponensek elhelyezését. Ha betelik egy sor, akkor új sort kezd. A FlowLayout menedzser a kedvelt méret alapján számítja ki a komponens által elfoglalt helyet. A komponensek között alapértelmezés szerint 5 pixel helyet hagy ki, s a sorokat középre igazítja.
Kissé komplikáltabb a BorderLayout stratégiája, amely a komponenseket öt pozícióba (észak, dél, kelet, nyugat, közép) helyezi el. Amikor egy elemet hozzáadunk a felülethez, akkor mindig meg kell adni az öt pozíció valamelyikét:
window.add('South', Button('Press Me'))
A fenti sorral például egy "Press Me" feliratú gombot helyezünk el a déli területre. Az északi és déli területre elhelyezett objektumokat a menedzser vízszintes irányban széthúzza, hogy azok kitöltsék a rendelkezésre álló helyet. A komponensek magasságát nem változtatja meg. Ugyanez történik a keleti és a nyugati területre helyezett elemekkel, csak azoknak éppen a magasságát állítja hozzá a rendelkezésre álló helyhez és a szélesség marad változatlan. A középre elhelyezett elem magasságát és szélességét is hozzáigazítja a rendelkezésre álló helyhez. Az elemek között a menedzser alapértelmezés szerint nem hagy ki helyet. Az öt pozícióba természetesen paneleket is elhelyezhetünk. A paneleken belüli elrendezéshez tetszôleges felületmenedzsert lehet használni. Az alábbi applet jól példázza a BorderLayout menedzser mûködését:
/* lecke05c.nrx */
class lecke05c extends Applet
method init()
setLayout(BorderLayout(4,4))
add( "North", Button("North" ) )
add( "South", Button("South" ) )
add( "West", Button("West" ) )
add( "East", Button("East" ) )
add( "Center", Button("Center" ) )
Az init metódus setLayout sorával állítjuk be a kívánt felületmenedzsert. A paraméterként megadott értékek a vízszintes és függôleges hézagok méretét állítják át nulláról négy pixelre. A következô öt sorban öt gombot helyezünk el az öt lehetséges pozícióba. Ha minden jól megy, akkor a következô eredményt kapjuk a böngészôbe történô betöltés után:
![[NTRX0503]](ntrx0503.gif)
Jól látható, hogy a gombok kitöltik a rendelkezésre álló helyet, valamint az is, hogy kis hézag van az elemek között.
Viszonylag könnyen érthetô a GridLayout felületmenedzser mûködése is, amely elôre definiált sorok és oszlopok metszéspontjában helyezi el az objektumokat, amelyek méretét úgy változtatja meg, hogy azok kihasználják a rendelkezésre álló helyet. A minimális és kedvelt méreteket figyelmen kívül hagyja. A rácspontokon történô elhelyezés a rendelkezésre álló terület bal felsô sarkában kezdôdik és jobbra folytatódik. A sor betelése esetén új sorban folytatódik az elhelyezés.
A legbonyolultabb felületmenedzser a GridBagLayout felületmenedzser, amely az építôelemeket rácspontokhoz rendelt cellákban helyezi el. Az azonos oszlopban elhelyezkedô cellák szélessége megegyezik. Ugyanez igaz az azonos sorban elhelyezkedô cellák magasságára is. A cellákat rácspontok koordinátái alapján lehet azonosítani. A felület bal felsô sarkában elhelyezkedô cella a (0, 0) koordinátákkal rendelkezik. Bármelyik cellába elhelyezhetünk objektumot, ám nem kötelezô minden cella kitöltése. Egy építôelem több cellát is elfoglalhat. A komponens által elfoglalt területet a komponens megjelenítési területének (component display area) nevezik. A komponensek elhelyezésével kapcsolatos információkat (például koordináták) a GridBagConstraints objektum 7 publikus változója (Position, Size, Fill, Anchor, Insets, Weights, Internal Padding) tárolja. A GridBagLayout felületmenedzser használata nagyon sok kódírást igényel, amelyet az egyik, a leckéhez mellékelt példaprogramban (lecke05d.nrx) is láthatunk. A NetREXX-rôl szóló IBM-es Piros könyv (Red Book) mellékletei között találunk egy SimpleGridBagLayout.nrx osztályt, amely jelentôsen megkönnyíti ennek a felületmenedzsernek a használatát. Akinek rendszeresen kell ezt az elrendezési filozófiát használnia, annak feltétlenül érdemes megismernie ezt az osztályt.
Legutolsóként a CardLayout felületmenedzserrel ismerkedünk meg, amelyet elsôsorban jegyzettömbök megvalósítására szokás használni. Ez a felületmenedzser ugyanis egyszerre csak egy komponenst (panelt) mutat meg a rendelkezésre álló felületen, a többit pedig elrejti. A komponensek között váltáshoz több metódus is rendelkezésre áll. Az alábbi képen látható felület is ezzel a felületmenedzserrel készült. A forráskód megtalálható a leckéhez mellékelt példaprogramok (lecke05e.nrx) között.
![[NTRX0504]](ntrx0504.gif)
Keret- és dialógusablakok
Mind az alkalmazások, mind pedig az appletek használhatnak keret- vagy dialógusablakokat, amelyek a Window osztály alosztályai. Appletek esetében a megjelenített ablak címsorában automatikusan megjelenik a Warning szó. Ezzel figyelmeztetik a felhasználót, nehogy az applet például login ablakot szimuláljon, és ellopja a jelszavat. A konstruálás során az ablakok nem láthatóak, csak a setVisible metódus meghívása után lesznek azok. A megjelenítés elôtt meg kell adni az ablak méretét is. A méretet beállíthatjuk fixre a setSize metódussal, vagy kiszámíttathatjuk a pack metódussal.
A keretablakok a címsor mellett rendelkezhetnek menüsorral is. A keretablakokat a Frame osztályból képezzük. A dialógusablakok nem rendelkezhetnek menüsorral, csak címsorral. A dialógusok létrehozásakor mindig szükség van egy szülôablakra, amely egy keretablak kell hogy legyen. A dialógusablakokat a Dialog osztályból származtatjuk le:
Frame(title = String ' ') Dialog(parent=Frame, title = String ' ', modal=boolean 0)
A dialógusok a szülôablak elôtt jelennek meg. Amennyiben modális dialógusról van szó, akkor a szülôablak egészen addig nem elérhetô, amíg a dialógusablakot be nem zárjuk.
Események lekezelése
Eddig tulajdonképpen arról volt szó, hogy hogyan lehet egy grafikus felületet felépíteni a Java környezet által rendelkezésre bocsátott elemekbôl. Az ily módon épített felületekkel azonban nem tudunk túl sokat kezdeni, mivel tulajdonképpen csak élettelen objektumok halmazáról van szó. A felületek akkor fognak megelevenedni, ha programunkba azt is beépítjük, hogy az elemek hogyan reagáljanak az ôket ért hatásokra. Ez utóbbit nevezzük az események lekezelésének. Sajnos ezen a területen igen lényeges különbségek lehetnek az egyes Java verziók (például 1.0 és 1.1) között, ezért programozás elôtt feltétlenül nézzük át ezt a részt Java környezetünk dokumentációjában is. Még egyszer hangsúlyozzuk, hogy a továbbiakban leírtak csak az 1.1-es környezetre érvényesek. Öröm lehet az ürömben, hogy az újabb verziók többnyire kompatibilisek a régi verziók szabályai szerint írt programokkal, így azok változtatás nélkül futtathatóak.
Az eseményeket generáló objektumokat eseményforrásoknak (event sources), az eseményeket feldolgozó komponenseket pedig eseményfogadóknak (event listeners) nevezzük. Az eseményfogadó komponensnek rendelkeznie kell egy olyan interfésszel, amely a figyelni kívánt eseményre reagál. Az interfész definiálja azokat a metódusokat, amelyeket végre akarunk hajtatni, ha a figyelt esemény bekövetkezik. Az eseményforrások rendelkeznek a mindenkori fogadók listájával. Amikor az esemény bekövetkezik, akkor a forrás errôl egymás után tájékoztatja a fogadókat. Ha a fogadó úgy dönt, hogy elfogyasztja az eseményt (lásd a consume metódust), akkor a sorban utána következô fogadók már nem kapják meg az esemény bekövetkeztének hírét.
Az eseményeket osztályok reprezentálják. Az osztályok tartalmazzák az esemény leírásához szükséges változókat is, ám ezek értéke csak a getAttribute és setAttribute metódusokkal manipulálható. Szükség esetén saját eseményosztályokat is lehet definiálni. A JDK két fajta eseménytípust különböztet meg: alacsonyszintû események és szemantikus események. Az alacsonyszintû események mindig valamilyen grafikus komponenssel kapcsolatos alapeseményt (például fókuszbakerülés, átméretezés, stb.) reprezentálnak. A szemantikus események forrása nem szükséges, hogy grafikus komponens legyen. Egy nem látható osztály, például egy idôzítôprogram is generálhat eseményeket, amelyre megint csak más osztályok reagálhatnak. A szemantikus események mindig valamilyen magasabbszintû történést reprezentálnak, mint az alacsonyszintû események.
Az eseményfogadás céljából az eseményosztályok interfészeket definiálnak. Az interfészek a különbözô eseménytípusokhoz rendelnek metódusokat, így átmenetet képeznek a két véglet (külön metódus minden egyes eseményhez, illetve egy metódus az összes eseményhez) között. Ha azt akarjuk, hogy egy adott komponens reagáljon bizonyos eseményekre, akkor meg kell adnunk a komponens eseményfogadó interfészét. Ennek általános formája a következô:
komponens.addEventListener( EventListenerObject )
Az EventListener helyébe az interfész nevét kell beírni. Ha például egy ablakhoz (win) a WindowListener interfészt szeretnénk hozzárendelni, akkor azt a következô utasítással tehetjük meg:
win.addWindowListener(winlist)
A winlistnek egy a WindowListener osztály alapján képzett objektumnak kell lennie. Az interfészeket az eseménytípusokhoz hasonlóan alacsonyszintû és szemantikus interfészekre lehet bontani. Az szemantikus interfészek egy, az alacsonyszintûek pedig több metódust is definiálhatnak. Az alacsonyszintû interfészek között kiemelkedô szerepe van az egérrel kapcsolatos eseményeknek, amelyekhez két fogadót is definiáltak. Ezzel azt érték el, hogy az alkalmazások bizonyos egérmûveletekre tudnak reagálni a nélkül, hogy meg kellene várniuk az egér mozgatását közvetítô eseményeket.
Az alacsonyszintû interfészek használatát könnyítik meg az adapterek, amelyek alacsonyszintû interfészeket megvalósító absztrakt osztályok. A Java nyelvben jelenleg 6 adapter (Component, Focus, Key, Mouse, MouseMotion, Window) létezik, amelyekbôl egyszerû leszármaztatással lehet alosztályokat készíteni, s ezekben aztán csak azt a metódust kell fölülírnunk, amelyikre szükségünk van. A többi metódus implementálásával nem kell törôdnünk.
Fontok használata
A Java nyelv operációs rendszer független fontkezelést valósít meg. A fontok három fontos tulajdonsággal rendelkeznek: a font neve, stílusa és mérete. A legfontosabb fontok között a Helvetia, TimesRoman, Courier, Dialog, DialogInput és Symbol fontokat lehet megemlíteni. Ha olyan fontnevet adunk meg, amelyet a Java nem ismer, akkor az operációs rendszertôl függô default font kerül behelyettesítésre. Font objektum készítését a Font osztály konstruáló metódusával végezzük:
aFont = Font( fontname = String, style = int, size = int)
A font stílusának a FONT.PLAIN (PLAIN/ROMAN), FONT.ITALIC, FONT.BOLD valamint FONT.ITALIC + FONT.BOLD értékeket lehet választani. A font méreteként megadott értéktôl eltérhet a valóságban létrehozott font mérete. A valós érték lekérdezhetô a FontMetrics osztály segítségével. Mivel ez egy absztrakt osztály, ezért ebbôl nem lehet egy lépésben objektumot készíteni. Ezért inkább a Component, Graphics és Toolkit osztályok getFontMetrics metódusát szokás használni:
-- obj = a Component, Graphics vagy Toolkit osztályból képzett objektum -- aFont = a Font osztályból képzett objektum fm = FontMetrics fm = obj.getFontMetrics(aFont)
Képek használata
A képek használatát elôsegítô osztályokat szétosztották a java.applet, java.awt és java.awt.image csomagok között, így nem könnyû eligazodni közöttük. A képeket a java.awt.Image osztályából leszármaztatott objektumok reprezentálják. Az Image osztály absztrakt osztály, ezért a képet betöltô kódrészlet egy, az Image osztályból leszármaztatott alosztályból képzett objektum azonosítóját kapja vissza. A képeket az Applet (appletek) vagy a Toolkit (alkalmazások és appletek) osztályok getImage metódusával lehet megjeleníteni. A Java 1.1-ben még csak a GIF és JPEG formátumú képeket lehetett használni, ez viszont a késôbbiekben bôvülni fog. A getImage metódus a meghívás után rögtön visszatér (nem akarjuk, hogy egy lassan betöltôdô kép blokkolja a program futását), s nem is ellenôrzi, hogy a megadott kép létezik-e. A kép csak akkor töltôdik be valójában, amikor a programnak az elsô alkalommal meg kell jelenítenie azt. A MediaTracker osztály segítségével elôre tölthetünk be képeket a háttérben elindított szálak segítségével, nyomon követhetjük a betöltést és szükség esetén várakozhatunk, amíg a kép teljesen be nem töltôdik.
A már betöltött képek kirajzolásához a Graphics osztály drawImage metódusát használhatjuk. Míg a getImage metódus tipikus elôfordulása appletek esetében az init() metódus, addig a drawImage legtöbbször a paint() vagy update() metódusokban fordul elô. Szinte mindenki ismeri a valószínûleg legnépszerûbb Java nyelven írt alkalmazásokat, az animációkat. Ezek általában olyan appletek, amelyekben fut egy szál, amely folyamatosan hívogatja a repaint metódust. Ha ez elég gyorsan történik, akkor az egymás után megjelenített képek a folyamatos mozgás benyomását keltik. Sajnos az animációk villódzásra hajlamosak, amely arra vezethetô vissza, hogy az operációs rendszer néha a kirajzolás közben frissíti a képernyôt. A villódzást nagy mértékben lehet redukálni, ha az update() metódusban a hátteret nem frissítjük, mielôtt meghívjuk a paint() metódust. Egy másik lehetôség a dupla pufferelés. Ez azt jelenti, hogy nem közvetlenül a képernyôn látható objektumra, hanem egy láthatatlan objektumra rajzolunk, majd egy utasítással a képernyôn látható objektumra másoljuk a már teljesen kirajzolt képet. Az alábbiakban közreadunk egy appletet, amely a dupla pufferelés technikáját használja egy animált NetREXX szöveg megjelenítésére. A NetREXX szöveg elôször a rendeltetésre álló hely középen jelenik meg, majd lassan bal oldalra mozdul el. Amikor eléri a rajzolási terület bal szélét, akkor megváltozik a színe és el is fordul. Késôbb aztán újabb színváltozások közepette visszafordul a szöveg és visszakúszik középre, ahol megáll, majd pedig eltûnik (fehér színû lesz).
/* lecke05f.nrx */
class lecke05f extends Applet implements Runnable
Properties inheritable
netrexx = String "NetRexx" -- a megjelenítendô karakterlánc
stringImage = Image -- a karakterláncból készített image
gi = Graphics -- az image objektuma
imagepos = int -- az image pozíciója (y)
x = int 200 -- kiindulási pozíció (x)
height = int -- az image magassága
imgwidth = int -- az image szélessége
width = int -- az elfordított image szélessége
/* az applet inicializálása */
method init()
x = 200; height = 0; width = 0
f = Font(' Helvetica' ,Font.BOLD,30) -- fontválasztás
fm = getFontMetrics(f) -- lekérdezzük a fontadatokat
imagepos = fm.getAscent() -- a rajzolási pozíció
/* a karakterlánchoz elôbb képet készítünk */
stringImage = createImage(fm.stringWidth(netrexx)+20, fm.getHeight())
gi = stringImage.getGraphics() -- grafikus objektum létrehozása
gi.setFont(f)
drawText(Color.black) -- fekete színnel kezdünk
imgwidth = stringImage.getWidth(this) -- az image szélessége
height = stringImage.getHeight(this) -- az image magassága
Thread(this, 'Animator Thread').start() -- indul az animáció
/* rányomtatjuk a karakterláncot az image-re */
method drawText(c=Color) private
gi.setColor(c) -- beállítjuk a színt
gi.drawString(netrexx,0,imagepos) -- rárajzoljuk a szöveget
/* az x koordináta kiszámítása mozgatás közben */
method calculatePosition(i=int) private
x = 200-i
/* az elforgatás közbeni szélesség kiszámítása */
method calculateSize(i=int) private
width = imgwidth-i
/* vibrálásmentes update metódus */
method update(g=Graphics)
paint(g)
/* a paint metódus */
method paint(g=Graphics)
g.setClip(x,10,stringImage.getWidth(this)+5,stringImage.getHeight(this)+10)
g.drawImage(stringImage,x,10,width,height+10,this)
/* a run metódus */
method run()
width = imgwidth
ct = Thread.currentThread() -- szál a várakozáshoz
do
loop i=1 to 200 by 2 -- a karakterláncot balra mozgatjuk
calculatePosition(i)
repaint()
ct.sleep(10)
end
drawText(Color.red) -- beállítjuk a piros színt
loop w=1 to imgwidth-25 by 2 -- elforgatjuk a karakterláncot az oldalnál
calculateSize(w)
repaint()
ct.sleep(10)
end
ct.sleep(150) -- várunk egy kicsit
drawText(Color.green) -- beállítjuk a zöld színt
loop while w > 0 -- visszaforgatjuk a karakterláncot
calculateSize(w)
repaint()
ct.sleep(10)
w = w - 1
end
drawText(Color.blue) -- beállítjuk a kék színt
loop i=200 to 150 by -1 -- jobbra mozgatjuk a karakterláncot
calculatePosition(i)
repaint()
ct.sleep(220 - i) -- lelassítjuk a mozgást
end
ct.sleep(2000) -- várunk egy kicsit
drawText(Color.white) -- beállítjuk a fehér színt
repaint()
catch InterruptedException
end
REXX GYÍK:
K1. A NetREXX-ben írt programjaim futnak ugyan a legtöbb platformon, azonban nem pontosan ugyanúgy néznek ki mindenhol. Miért?
V1. A grafikus építôelemek egy részének megjelenítése valóban operációs rendszer függô. Ennek a problémának a megoldására vezették be a Java 1.1-ben a Swing project (http://java.sun.com/products/jdk/awt/swing) keretében kifejlesztett könnyûsúlyú (LightWeight) komponenseket, amelyek minden operációs rendszerben azonos módon néznek ki. Használatuk kicsit bonyolultabb, mint a hagyományos komponenseké.
K2. Bár nagyjából megértettem a leckében leírtakat, mégis úgy érzem, hogy nem tudnék önállóan egy grafikus felületet összeállítani. Mit tegyek?
V2. Amennyiben még sohasem programoztál Java-ban, akkor ez teljesen érthetô. Ebben a leckében megpróbáltuk ugyanis összesûríteni mindazon Java ismereteket, amelyek ahhoz kellenek, hogy legalább alapszinten tudjunk foglalkozni a grafikus felületek programozásával a NetREXX környezetben. A teljes ismeretanyag viszont olyan nagy, hogy külön tanfolyamot lehetne írni róla. A kezdô Java programozóknak csak azt tudjuk tanácsolni, hogy nézzenek utána az itt leírtaknak egy Java könyvben, és mielôbb próbálkozzanak meg saját programok írásával.
Gyakorlatok:
1. Készítsen egy felületet, amelyhez BorderLayout felületmenedzsert használ. Helyezzen el a felületen öt panelt, amelyeken belül FlowLayout felületmenedzsert definiál!
2. Készítsen egy Java alkalmazást, amely a szövegmezôbe beírt szöveget egy gombnyomásra kiírja egy másik mezôben!
| Kádár Zsolt 2000. 02. 20. | [ Elôzô lecke | Következô lecke | Tartalom ] |