Labor 08 - Java Swing alapok és alacsony szintű grafika¶
Bevezető és Előkészületek¶
Készítette: Hartung István (Swing alapok, 2015) és Goldschmidt Balázs (Alacsony szintű grafika iMSc, 2015), BME IIT.
A feladatok megoldásához felhasználandó osztályok leírásait az alábbi URL-eken találod meg:
- Java SE 8 API dokumentáció: http://docs.oracle.com/javase/8/docs/api/
javax.swing.JFrame: http://download.oracle.com/javase/6/docs/api/javax/swing/JFrame.html-
java.awt.Graphics(iMSc): http://docs.oracle.com/javase/8/docs/api/java/awt/Graphics.html -
Projekt neve:
SwingLabor(Alapfeladatok) /LowGraphLabor(iMSc) - Java csomag (package):
hu.bme.iit.prog3.swing - Teljesítési követelmény: Az első 4 feladat megoldása a labor teljesítésének feltétele. A többi feladatot érdemes legkésőbb otthon befejezni.
Nincs feltöltés! (Lokális ellenőrzés)
Ezen a laboron nincs GitHub vagy Moodle feltöltés! A feladatokhoz tartozó képernyőképeket közvetlenül a gépeden (ajánlottan a projekt gyökerébe) mentsd el f1.png, f2.png, stb. néven.
Minden képernyőképen látszódjon a Neptun-kódod kommentként a forráskódban (vagy az ablak címsorában)! A laborvezető a gépednél fogja ellenőrizni a megoldást.
Kötelező alapfeladatok (1–4. feladat)¶
1. Caesar-kódolás¶
Írj egy olyan függvényt, amely Caesar-kódolást valósít meg az angol ABC nagybetűivel (amennyiben a bemenet nem nagybetűs, alakítsd azzá): http://hu.wikipedia.org/wiki/Caesar-rejtjel
A függvény fejléce:
A függvény egyik bemeneti paramétere legyen a kódolni kívánt String, a másik pedig az eltolódás mértéke karakterben megadva. Pl. 'K' esetén A -> K kódolást csináljon. Figyelj arra, hogy a 'Z' karakter után az 'A' következzen! A függvény működését teszteld is!
Segítség a karakterkódokhoz
- A
'Z'karakterkódja:90(az'A'karakterkódja:65). - Az angol ABC
26betűs. - Az eltolás mértéke kiszámítható:
int shift = Character.toUpperCase(offset) - 'A';, majd minden nagybetűs karakterre:(char) ('A' + (ch - 'A' + shift) % 26).
BEMUTATANDÓ KÉPERNYŐKÉP (Kötelező)
Készíts egy képernyőképet, amelyen látszik a caesarCode függvény megvalósítása (Neptun-kóddal kommentben) és egy rövid konzolos teszt kimenete!
Fájlnév a projekt gyökerében: f1.png. A laborvezető a gépednél ellenőrzi!
2. JFrame ablak létrehozása¶
Készítsd el első ablakodat (az osztály neve legyen CaesarFrame). Ehhez a JFrame osztályból való leszármazás (extends JFrame) szükséges. Olvasd át ehhez az osztály API-ját!
- Az osztály konstruktorában gondoskodj arról, hogy ha a felhasználó az ablak sarkában levő X-re kattint, akkor az alkalmazás be is záródjon, ne maradjon futva a háttérben! Ehhez használd a
setDefaultCloseOperation(EXIT_ON_CLOSE)függvényt! - Keresd meg azt a függvényt (
setTitle), amivel az ablak címsorában levő szöveget lehet beállítani, ezt állítsd a"SwingLab"karaktersorozatra! - Állítsd be az ablak méretét
400x110pixelesre (setSize(400, 110)), és állítsd be, hogy ne lehessen átméretezni (setResizable(false))! - Az ablak megjelenítéséhez a
mainfüggvényben példányosítsd az osztályt, és hívd meg azt a metódust, amely láthatóvá teszi az ablakot (setVisible(true))!
BEMUTATANDÓ KÉPERNYŐKÉP (Kötelező)
Készíts egy képernyőképet a CaesarFrame konstruktoráról és a megjelenő üres 400x110-es "SwingLab" ablakról!
Fájlnév a projekt gyökerében: f2.png. A laborvezető a gépednél ellenőrzi!
3. Elrendezés (Layout)¶
Vizsgáld meg a Java-ban található főbb elrendezéseket (Layout), különös tekintettel az egyszerűbb BorderLayout, FlowLayout és GridLayout osztályokra: http://download.oracle.com/javase/tutorial/uiswing/layout/visual.html
Hozd létre az alábbi felépítésű ablakot:
- Felső
JPanel(FlowLayout): tartalmaz egyJComboBox-ot (A–Z betűkkel), egy20karakter széles bemenetiJTextField-et, és egy"Code!"feliratúJButton-t. - Alsó
JPanel(FlowLayoutvagyBorderLayout): tartalmaz egy"Output:"feliratúJLabel-t és egy20karakter széles kimenetiJTextField-et.
Megvalósítási lépések:
- A következő osztályokból érdemes a
CaesarFrameosztálybanprivateláthatóságú tagváltozókat létrehozni (a későbbi feladatok miatt): a felső és alsóJTextField(new JTextField(20)), aJButtonés aJComboBox. A többi elemet (JPanel,JLabel) elegendő a konstruktorban létrehozni. - A
JComboBoxlétrehozása előtt egyObject[](vagyCharacter[]) tömböt kell feltöltened az angol ABC nagybetűivel ('A'–'Z'), és célszerű ezt átadni aJComboBoxkonstruktorának. - A
CaesarFrameelrendezése legyen pl.GridLayout(2, 1)vagyBorderLayout(NORTHésSOUTH/CENTER), amelybe elhelyezed a felső és alsóJPanel-t. - Állítsd be, hogy az alsó mezőt ne lehessen szerkeszteni (
setEditable(false))! - Tedd az ablakot átméretezhetővé (
setResizable(true)), és próbáld is ki ezt a funkciót!
BEMUTATANDÓ KÉPERNYŐKÉP (Kötelező)
Készíts egy képernyőképet a felépített Swing felületről (a két panellel, legördülő listával, szövegmezőkkel és gombbal) és a hozzá tartozó kódrészletről!
Fájlnév a projekt gyökerében: f3.png. A laborvezető a gépednél ellenőrzi!
4. Listenerek (Eseménykezelés)¶
Definiálj a CaesarFrame osztályon belül egy belső osztályt (private class OkButtonActionListener implements ActionListener), hogy elérje a befogadó osztály attribútumait is.
- Ez a listener implementálja az
ActionListenerinterfészt úgy, hogy a bekövetkezett esemény (actionPerformed) hatására a fölül levő szövegmezőbe beírt szöveget Caesar-kódolja aJComboBox-ban beállított karakter eltolással, és a kódolt szöveget jelenítse meg az alsó szövegmezőben! - A feladat megoldásához használd a korábban elkészített
caesarCodefüggvényt! - Segítség: a
JTextField-nek vannakgetText()éssetText(String s)metódusai, aJComboBox-ban kiválasztott érték lekérdezéséhez agetSelectedItem()által visszaadott értéket kell karakterré ((Character)vagy(char)) kasztolni. - Ennek az osztálynak egy példányát állítsd be a
JButtoneseményfigyelőjének (addActionListener(new OkButtonActionListener())) az ablak konstruktorában!
BEMUTATANDÓ KÉPERNYŐKÉP (Kötelező - 1–4. feladat teljesítése)
Készíts egy képernyőképet, amelyen látszik az OkButtonActionListener kódja, valamint a működő ablak, ahogy egy beírt szöveget gombnyomásra elkódol az alsó mezőbe!
Fájlnév a projekt gyökerében: f4.png. A laborvezető a gépednél ellenőrzi!
További Swing feladatok (5–7. feladat)¶
5. Adapterek (KeyAdapter)¶
Valósítsd meg, hogy a felső szövegdobozban történt minden billentyűleütés esetén azonnal megtörténjen a kódolás!
- Hozz létre egy
InputFieldKeyListenernevű belső osztályt, ami aKeyAdapterosztályból származik le (extends KeyAdapter), és felülírja a megfelelő eseménykezelő függvényt (pl.keyTypedvagykeyPressed)! - Az így létrejövő osztályt add hozzá a felső bemeneti mezőhöz
KeyListener-ként (addKeyListener)! - Milyen hibát tapasztalsz? Mi okozhatja ezt?
Magyarázat: Miért marad le egy karakterrel a keyTyped?
Amikor a keyTyped (vagy keyPressed) esemény lefut, a leütött karakter még nem került be a JTextField belső szövegmodelljébe (Document), így a getText() még az eggyel korábbi állapotot adja vissza! (A keyReleased már látná, de az egérrel való beillesztést vagy törlést az sem kezeli megbízhatóan.)
6. DocumentListener¶
Javítsd ki az előző feladatot úgy, hogy minden szövegváltozásnál (gépelés, törlés, vágólapról beillesztés) helyesen és késleltetés nélkül működjön!
- Ehhez a
javax.swing.event.DocumentListenerinterfész megvalósítása szükséges (insertUpdate,removeUpdate,changedUpdatemetódusok). - Tipp: el kell kérni a felső
JTextFieldmodelljét agetDocument().addDocumentListener(...)hívással!
BEMUTATANDÓ KÉPERNYŐKÉP
Készíts egy képernyőképet a DocumentListener implementációjáról és a gépelés közben azonnal frissülő kódolásról!
Fájlnév a projekt gyökerében: f5.png. A laborvezető a gépednél ellenőrzi!
7. Másik irányba kódolás (FocusListener)¶
Az alsó JTextField-et állítsd át, hogy szerkeszthető legyen (setEditable(true)), és valósítsd meg, hogy amennyiben a felhasználó oda ír be szöveget, és megnyomja a "Code!" feliratú gombot, akkor dekódolódjon az oda írt szöveg a felső mezőbe!
Ehhez szükség van arra, hogy:
- Tudjuk, mikor melyik
JTextField-ben jártunk utoljára – használjFocusListener-t (vagyFocusAdapter-t), amely eltárolja, melyik mező kapott utoljára fókuszt (focusGained)! - Legyen dekódoló függvényünk (
caesarDecode), és a felelősségek szétválasztásának elve (Separation of Concerns) szerint a GUI osztály ne tudjon a kódolás belső matematikai módjáról (szervezd ki a kódolást/dekódolást egy külön segédosztályba, pl.CaesarCipher)!
BEMUTATANDÓ KÉPERNYŐKÉP
Készíts egy képernyőképet a FocusListener és a dekódoló logika kódjáról, valamint a kétirányú kódolás működéséről!
Fájlnév a projekt gyökerében: f6.png. A laborvezető a gépednél ellenőrzi!
iMSc Feladatok — Alacsony szintű grafika¶
i. Alapprogram megismerése¶
A kiindulási Eclipse projekt váza letölthető innen: low_graph_base.zip
Az alapprogram (low_graph_base.zip) egy egyszerű Swing-es rajzoló alkalmazás:
Main: létrehozza aJFrame-et, elhelyezi a gombokat és regisztrálja a listenereket.MyCanvas: a rajzoló felület, amely három képréteget kezel:bottom(a már véglegesített rajz),top(átlátszó réteg az egér húzása közbeni átmeneti alakzatoknak) ésimage(a képernyőre kirajzolandó összefésült kép).DrawésRectDraw: aDrawabsztrakt ősosztály kezeli az egérműveleteket (startx,starty,endx,endy), és meghívja a leszármazottakmakeDraw(Graphics g)metódusát (mint aRectDraw).MouseListenerDelegatorésButtonListener: aMouseListenerDelegatortovábbítja az egérműveleteket az aktuálisan kiválasztottDrawpéldánynak, amelyet aButtonListenercserél le gombnyomáskor asetDelegate(...)metódussal.
Importáld az alapprogramot az IDE-be, és próbáld ki a téglalaprajzolást!
ii. Vonalrajzolás (LineDraw)¶
Készíts a RectDraw osztály forráskódja alapján LineDraw osztályt (extends Draw), amivel vonalat lehet rajzolni!
- A vonal a téglalaphoz hasonlóan csak akkor véglegesítődjön, ha elengedtük az egérgombot.
- A vonal kezdete a lenyomáskor eltárolt érték (
startx,starty), vége az éppen aktuális kurzorpozíció (endx,endy) – használd ag.drawLine(startx, starty, endx, endy)metódust amakeDraw(Graphics g)-ben! - Az osztály egy példányát a
ButtonListeneractionPerformedmetódusában állítsd be a megfelelő gomb eseményének hatására (mld.setDelegate(new LineDraw()))!
iii. Körrajzolás (CircleDraw)¶
Készíts a RectDraw osztály forráskódja alapján CircleDraw osztályt (extends Draw), amivel kört lehet rajzolni!
- A kör a téglalaphoz hasonlóan csak akkor véglegesítődjön, ha elengedtük az egérgombot.
- A kör középpontja a lenyomáskor eltárolt érték (
startx,starty), a kör pereme pedig mindig legyen az aktuális kurzorpozíción (endx,endy)! - Segítség: a sugár $r = \sqrt{(\text{endx} - \text{startx})^2 + (\text{endy} - \text{starty})^2}$, a befoglaló négyzet bal felső sarka pedig $(\text{startx} - r, \text{starty} - r)$, szélessége és magassága $2r$ (
g.drawOval(startx - r, starty - r, 2 * r, 2 * r)). - Regisztráld a
CircleDrawpéldányt aButtonListeneractionPerformedmetódusában!
BEMUTATANDÓ KÉPERNYŐKÉP (iMSc 1. rész)
Készíts egy képernyőképet a LineDraw és CircleDraw osztályok kódjáról, valamint a rajzvászonról, amelyen látszanak a megrajzolt téglalapok, vonalak és körök!
Fájlnév a projekt gyökerében: f7.png. A laborvezető a gépednél ellenőrzi!
iv. Színváltás (ColorChanger)¶
Készíts színváltást lehetővé tevő komponenst (ColorChanger)!
- Valósítsa meg az
ActionListenerinterfészt, és legyen a"color"nevűJButton-hoz regisztrálva. - Ha a gombot megnyomják, jelenjen meg egy
JColorChooserdialógusablak (JColorChooser.showDialog(...)metódus), és a kiválasztott szín legyen az új rajzoló szín. - A rajzoló színt célszerű a
Drawosztály statikus attribútumaként (protected static Color color = Color.BLACK;) eltárolni, hogy aColorChangerkényelmesen hozzáférhessen, a rajzoló rutinok pedig beállíthassák (g.setColor(color)).
v. Görberajzolás (CurveDraw)¶
A fentiek alapján írj szabadkézi görberajzoló komponenst (CurveDraw extends Draw)!
- Ez a korábbi alakzatokkal ellentétben folyamatosan rajzoljon az egér húzása közben.
- Ehhez közvetlenül a
bottomrétegre kell tenni az ábrát, vagyis amakeDrawmellett felül kell definiálni aDrawmouseDraggedmetódusát is úgy, hogy arealDraw(arg0, canvas.getBottom())hívás után az előző pontot frissítse az aktuálisra (startx = endx; starty = endy;)! - Regisztráld a
CurveDrawpéldányt aButtonListeneractionPerformedmetódusában!
BEMUTATANDÓ KÉPERNYŐKÉP (iMSc 2. rész)
Készíts egy képernyőképet a ColorChanger és CurveDraw kódjáról, valamint a rajzvászonról, amelyen különböző színű alakzatok és szabadkézi görbék láthatók!
Fájlnév a projekt gyökerében: f8.png. A laborvezető a gépednél ellenőrzi!
Lokális beadási ellenőrzőlista¶
Pipáld ki az elkészült feladatokat (az állapotot a böngésződ automatikusan elmenti)! Az első 4 feladat teljesítése kötelező a labor elfogadásához:
-
f1.pngcaesarCodefüggvény megvalósítása és tesztelése. -
f2.pngCaesarFrameablak (400x110,"SwingLab",EXIT_ON_CLOSE). -
f3.pngKétpaneles Swing elrendezés (JComboBox,JTextField-ek,JButton,JLabel). -
f4.pngOkButtonActionListenerbelső osztály, gombnyomásra működő Caesar-kódolás.
-
f5.pngKeyAdapterkipróbálása és javításaDocumentListenersegítségével. -
f6.pngKétirányú kódolás/dekódolásFocusListenerés különválasztott kódoló logika használatával. -
f7.png(iMSc)LineDrawésCircleDrawosztályok megvalósítása és bekötése aButtonListener-be. -
f8.png(iMSc)ColorChanger(JColorChooser) ésCurveDraw(folyamatos szabadkézi rajzolás abottomrétegre). -
Java KódA megírt, fordítható és futtatható Java forráskódok megnyitva az IDE-ben (Neptun-kóddal).