Kihagyás

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:

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:

Caesar kódoló metódus
private String caesarCode(String input, char offset)

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 26 betű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 400x110 pixelesre (setSize(400, 110)), és állítsd be, hogy ne lehessen átméretezni (setResizable(false))!
  • Az ablak megjelenítéséhez a main fü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 egy JComboBox-ot (A–Z betűkkel), egy 20 karakter széles bemeneti JTextField-et, és egy "Code!" feliratú JButton-t.
  • Alsó JPanel (FlowLayout vagy BorderLayout): tartalmaz egy "Output:" feliratú JLabel-t és egy 20 karakter széles kimeneti JTextField-et.

Megvalósítási lépések:

  • A következő osztályokból érdemes a CaesarFrame osztályban private láthatóságú tagváltozókat létrehozni (a későbbi feladatok miatt): a felső és alsó JTextField (new JTextField(20)), a JButton és a JComboBox. A többi elemet (JPanel, JLabel) elegendő a konstruktorban létrehozni.
  • A JComboBox létrehozása előtt egy Object[] (vagy Character[]) tömböt kell feltöltened az angol ABC nagybetűivel ('A'–'Z'), és célszerű ezt átadni a JComboBox konstruktorának.
  • A CaesarFrame elrendezése legyen pl. GridLayout(2, 1) vagy BorderLayout (NORTH és SOUTH / 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 ActionListener interfé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 a JComboBox-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 caesarCode függvényt!
  • Segítség: a JTextField-nek vannak getText() és setText(String s) metódusai, a JComboBox-ban kiválasztott érték lekérdezéséhez a getSelectedItem() által visszaadott értéket kell karakterré ((Character) vagy (char)) kasztolni.
  • Ennek az osztálynak egy példányát állítsd be a JButton esemé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 InputFieldKeyListener nevű belső osztályt, ami a KeyAdapter osztályból származik le (extends KeyAdapter), és felülírja a megfelelő eseménykezelő függvényt (pl. keyTyped vagy keyPressed)!
  • 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.DocumentListener interfész megvalósítása szükséges (insertUpdate, removeUpdate, changedUpdate metódusok).
  • Tipp: el kell kérni a felső JTextField modelljét a getDocument().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:

  1. Tudjuk, mikor melyik JTextField-ben jártunk utoljára – használj FocusListener-t (vagy FocusAdapter-t), amely eltárolja, melyik mező kapott utoljára fókuszt (focusGained)!
  2. 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 a JFrame-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) és image (a képernyőre kirajzolandó összefésült kép).
  • Draw és RectDraw: a Draw absztrakt ősosztály kezeli az egérműveleteket (startx, starty, endx, endy), és meghívja a leszármazottak makeDraw(Graphics g) metódusát (mint a RectDraw).
  • MouseListenerDelegator és ButtonListener: a MouseListenerDelegator továbbítja az egérműveleteket az aktuálisan kiválasztott Draw példánynak, amelyet a ButtonListener cserél le gombnyomáskor a setDelegate(...) 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 a g.drawLine(startx, starty, endx, endy) metódust a makeDraw(Graphics g)-ben!
  • Az osztály egy példányát a ButtonListener actionPerformed metó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 CircleDraw példányt a ButtonListener actionPerformed metó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 ActionListener interfészt, és legyen a "color" nevű JButton-hoz regisztrálva.
  • Ha a gombot megnyomják, jelenjen meg egy JColorChooser dialógusablak (JColorChooser.showDialog(...) metódus), és a kiválasztott szín legyen az új rajzoló szín.
  • A rajzoló színt célszerű a Draw osztály statikus attribútumaként (protected static Color color = Color.BLACK;) eltárolni, hogy a ColorChanger ké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 bottom rétegre kell tenni az ábrát, vagyis a makeDraw mellett felül kell definiálni a Draw mouseDragged metódusát is úgy, hogy a realDraw(arg0, canvas.getBottom()) hívás után az előző pontot frissítse az aktuálisra (startx = endx; starty = endy;)!
  • Regisztráld a CurveDraw példányt a ButtonListener actionPerformed metó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.png caesarCode függvény megvalósítása és tesztelése.
  • f2.png CaesarFrame ablak (400x110, "SwingLab", EXIT_ON_CLOSE).
  • f3.png Kétpaneles Swing elrendezés (JComboBox, JTextField-ek, JButton, JLabel).
  • f4.png OkButtonActionListener belső osztály, gombnyomásra működő Caesar-kódolás.

  • f5.png KeyAdapter kipróbálása és javítása DocumentListener segítségével.
  • f6.png Kétirányú kódolás/dekódolás FocusListener és különválasztott kódoló logika használatával.
  • f7.png (iMSc) LineDraw és CircleDraw osztályok megvalósítása és bekötése a ButtonListener-be.
  • f8.png (iMSc) ColorChanger (JColorChooser) és CurveDraw (folyamatos szabadkézi rajzolás a bottom rétegre).
  • Java Kód A megírt, fordítható és futtatható Java forráskódok megnyitva az IDE-ben (Neptun-kóddal).