Sovelmia esitellään käyttäen Javan versiota 1.0.2, koska Netscape® Communicator 4.04 -selain (käytössä mm. laitoksen Linux-järjestelmässä) ei osaa Javan versioita 1.1.*.
Luonteva ajattelutapa tällaisten ohjelman laadinnassa on ns. tapahtumaohjattu (event driven) ohjelmointi: ohjelmaan rakennetaan algoritmit reagoimaan erilaisiin käyttäjän aiheuttamiin ärsykkeisiin.
Javassa jokainen sovelma perii Applet-luokasta joukon metodeita tapahtumien käsittelemiseen. Edellisistä kappaleista tuttuun tapaan nuo perityt metodit ovat tyhjiä ja niitä korvataan (override) tarpeen mukaan omilla. Applet itse perii ko. metodit luokalta Component.
Tapahtuma esitetään oliona - kuinkas muuten - luokan Event ilmentymänä. Event-luokka kapseloi sisäänsä konekohtaiset tavat toteuttaa graafisen käyttöliittymän tapahtumat.
Sovelma perii seuraavat tapahtumankäsittelymetodit (asiat ovat melko toisin Javan versiossa 1.1, perusidea on silti samantapainen):
Nämä tapahtumankäsittelijät saavat parametrinaan ainakin Event-olion ja kaikki palauttavat totuusarvon: true ilmaisee, että tapahtuma on loppuunkäsitelty, false puolestaan, että tapahtuma halutaan välittää jatkokäsittelyyn (mahdollisiin ulompiin "containereihin").
Kokeile
Tämän sovelman tapahtumankäsittelijät paint-metodia lukuunottamatta vain tulostavat ilmoituksen suorituksestaan Java-konsolille ja selaimen tilariville. (Tässä esimerkissä totuusarvoiset tapahtumankäsittelijät palauttavat arvon false. Se merkitsee, että tapahtumat saadaan käsiteltyä loppuun vasta kun on käyty suorittamassa myös ulommat samannimiset - tässä tapauksessa tyhjät -tapahtumankäsittelijät.):
import java.applet.Applet; import java.awt.*; public class Tapahtuu extends Applet { // testitulostuksiin: private void TulostaSinneJaTanne(String ilmoitus) { System.out.println(ilmoitus); // Java-konsolille showStatus(ilmoitus); // selaimen tilariville } public void init() { TulostaSinneJaTanne("init-tapahtuma"); } public void paint(Graphics g) { TulostaSinneJaTanne("paint-tapahtuma"); g.drawRect(0, 0, size().width - 1, size().height - 1); g.drawString("Katso konsolilta mitä tapahtuu!", 5, 20); g.drawString("Katso tilariviltä mitä tapahtuu!", 5, 40); } public void start() { TulostaSinneJaTanne("start-tapahtuma"); } public void stop() { TulostaSinneJaTanne("stop-tapahtuma"); } public void destroy() { TulostaSinneJaTanne("destroy-tapahtuma"); } public void update(Graphics g) { TulostaSinneJaTanne("update-tapahtuma"); } public boolean mouseUp(Event e, int x, int y) { TulostaSinneJaTanne("mouseUp-tapahtuma: ("+x+","+y+")"); return false; } public boolean mouseDown(Event e, int x, int y) { TulostaSinneJaTanne("mouseDown-tapahtuma: ("+x+","+y+")"); return false; } public boolean mouseDrag(Event e, int x, int y) { TulostaSinneJaTanne("mouseDrag-tapahtuma: ("+x+","+y+")"); return false; } public boolean mouseMove(Event e, int x, int y) { TulostaSinneJaTanne("mouseMove-tapahtuma: ("+x+","+y+")"); return false; } public boolean mouseEnter(Event e, int x, int y) { TulostaSinneJaTanne("mouseEnter-tapahtuma: ("+x+","+y+")"); return false; } public boolean mouseExit(Event e, int x, int y) { TulostaSinneJaTanne("mouseExit-tapahtuma: ("+x+","+y+")"); return false; } public boolean keyDown(Event e, int x) { TulostaSinneJaTanne("keyDown-tapahtuma: merkki " +(char)x+", merkkikoodi "+x); return false; } }Ylläolevassa esimerkissä ei update-tapahtumaa aiheudu lainkaan! Sen voivat muut tapahtumankäsittelijät halutessaan aikaansaada metodilla repaint. Update-tapahtumankäsittelijä saa selaimelta parametriksi saman "piirustusalustan" kuin paint-metodi, joka piirtää koko kuva.alan uudelleen. Update-metodin tehtävänä on täydentää olemassaolevaa näkymää.
Jo luvussa 2.1 tutustuimme sovelmaan SyoJuustoa (SyoJuustoa.html, SyoJuustoa.java) jonka avulla hiiri voi nappulaansa painaen syödä juustoa:
Metodi mouseDown kutsuu metodia repaint, joka puolestaan aikaansaa tapahtuman update. mouseDown-metodi palauttaa arvon true, koska tapahtuma katsotaan loppuunkäsitellyksi. Sovelmaan on tarkoituksella otettu mukaan metodi mouseEnter, joka ei kutsu metodia repaint.
Varmista Java-konsolilta, että mouseEnter ei todellakaan johda update-tapahtumaan! Kokeile myös, mitä tapahtuu, kun sovelmaa peitetään ja paljastetaan.
Koodi:
import java.applet.Applet; import java.awt.*; public class SyoJuustoa extends Applet { private int x, y; // testitulostuksiin: private void TulostaSinneJaTanne(String ilmoitus) { System.out.println(ilmoitus); // konsolille showStatus(ilmoitus); // selaimen tilariville } public void paint(Graphics g) { TulostaSinneJaTanne("paint-tapahtuma"); setBackground(Color.yellow); g.drawRoundRect(0, 0, size().width - 1, size().height - 1,20,20); } public void update(Graphics g) { TulostaSinneJaTanne("update-tapahtuma"); g.fillOval(x,y,10,10); } public boolean mouseDown(Event e, int x, int y) { TulostaSinneJaTanne("mouseDown-tapahtuma: ("+x+","+y+")"); this.x = x; this.y = y; repaint(); return true; } public boolean mouseEnter(Event e, int x, int y) { TulostaSinneJaTanne("mouseEnter-tapahtuma: ("+x+","+y+")"); return true; } }
Alla on kolme kuvaeditoria UnohtavaPiirtaja, MuistavaPiirtaja ja AikaMuistavaPiirtaja (UnohtavaPiirtaja.html, UnohtavaPiirtaja.java, MuistavaPiirtaja.html, MuistavaPiirtaja.java AikaMuistavaPiirtaja.html, AikaMuistavaPiirtaja.java)
Nämä sovelmat ovat melko hitaita Netscapen versiosta 3.01 alkaen, vanhemmat olivat paljon nopeampia. Sovelmien algoritmit eivät ole välttämättä kovin nerokkaita!
Nämäkin sovelmat tulostavat testi-ilmoituksia. (Testitulostuslauseet on poistettu tämän kappaleen esimerkeistä. Ohjelmatiedostoissa ne ovat.)
Piirrä jotakin kaikkiin ja seuraa tapahtumia Java-konsolilta. Poista tavalla jos toisellakin kuvat näkyvistä ja palauta ne takaisin. Eroja käyttäytymisessä?
UnohtavaPiirtaja toimii avan samoin kuin äskeinen SyoJuustoa-sovelma: tällä kertaa mouseDrag-metodi asettaa luokan kenttiin pisteen koordinaatit ja aiheuttaa update-tapahtuman kutsumalla metodia repaint.
UnohtavaPiirtaja-sovelma (UnohtavaPiirtaja.java) ei mitenkään itse säilytä tietoa piirretystä kuvasta. Niinpä kuvaa uudelleen näytettäessä ei paint-metodi voi tietää, millainen vanha kuva oli. Kuvan muistamiseen tarvitaan jonkinlainen tietorakenne.
Yksi mahdollisuus on tallettaa kaikista kuvapisteistä tieto, onko pisteeseen piirretty vai ei. Samaan tapaan voitaisiin säilyttää tieto vaikkapa pisteen väristä.
MuistavaPiirtaja-sovelmassa (MuistavaPiirtaja.java) on taulukko:
private boolean[][] piste;jonka init-metodi asettaa tyhjäksi:
piste = new boolean[size().width][size().height]; // oletusalkiot == falsepaint-metodi maalaa kuvan pisteittäin:
public void paint(Graphics g) { g.drawRect(0, 0, size().width-1,size().height-1); for (int i=0; i<size().width; ++i) for (int j=0; j<size().height; ++j) if (piste[i][j]) g.drawRect(i,j,1,1); }mouseDrag-metodi päivittää tietorakennetta. Se joutuu myös ottamaan huomioon, että sovelmalle voi aiheutua mouseDrag-tapahtumia kuva alan ulkopuolelta(!), siksi indeksivirheeseen varaudutaan:
public boolean mouseDrag(Event e, int x, int y) { try { // kokeile dragata ulos kuvasta! this.x = x; this.y = y; piste[x][y] = true; repaint(); } catch (ArrayIndexOutOfBoundsException vikaa) { } return true; }
MuistavaPiirtaja-sovelmassa "pikselitaulukko" tyhjätään (so. luodaan uusi) vain init-metodissa. Siksi sovelmalla on hyvä muisti: kuva piirretään uudelleen peittymisen, kuvakkeena käynnin, sivulla muualle siirymisen, "Back-Forward"-toimen, ..., jälkeen.
Jos kuvan halutaan säilyvän vain peittymisten ja sivulla siirtyilyjen jälkeen, stop-metodi voidaan ohjelmoida tyhjäämään tietorakenne.
Sovelma AikaMuistavaPiirtaja (AikaMuistavaPiirtaja.java) on muuten kuin MuistavaPiirtaja, mutta sillä on metodi:
public void stop() { piste = new boolean[size().width][size().height]; // oletusalkiot == false }
Ohjelma on seuraavanlainen(testitulostusvälineet on poistettu, tiedostosta ne löytyvät) [ohjelmaa on korjattu lokakuussa 1998, drawPolygon on korvattu metodilla drawPolyline]:
import java.applet.Applet; import java.awt.*; public class VektoriPiirtaja extends Applet { private final int MAXLKM = 500; private int[] x = new int[MAXLKM]; private int[] y = new int[MAXLKM]; private int lkm; // Tietorakenne on pisteparien joukko kahdessa // taulukossa ja lukumäärämuuttuja. public void init() { lkm = 0; } public void start() { lkm = 0; } // Säilytetään kuva vain peittymisen yli. public void paint(Graphics g) { g.drawRect(0, 0, size().width-1, size().height-1); g.drawPolyline(x, y, lkm); } public void update(Graphics g) { g.drawPolyline(x, y, lkm); } // Piirretään ja päivitetään drawPolygon-metodilla // (jälkimmäisessä tehdään turhaa työtä, mutta // nyt kuva säilyy täsmälleen samanlaisena, vrt. // edelliset esimerkit). public boolean mouseDrag(Event e, int x, int y) { this.x[lkm] = x; this.y[lkm] = y; if (lkm < MAXLKM-1) { ++lkm; repaint(); } return true; } // Tietorakenne (ja kuva) kasvatetaan niin isoksi kuin // resurssit sallivat. Sen jälkeen kuvan piirtely loppuu. public boolean mouseDown(Event e, int x, int y) { return mouseDrag(e, x, y); } // Kuvaa voi piirtää "klikkaillenkin". // Käytetään hyväksi jo ohjelmoitua metodia. }
Sovelma OtusKarkaa (OtusKarkaa.html, OtusKarkaa.java) on tietokonepeli, jolla otusta yritetään kiinni. Tarkkaile otuksen mielialojen vaihteluita.
Sovelman OtusKarkaa2 (OtusKarkaa2.html, OtusKarkaa2.java) otus ei karkaa niin kauas, kiusoittelee peijakas:
Kehitelty versio karkaavasta otuksesta löytyy sivulta http://www.cs.Helsinki.FI/~wikla/JavaArt/CatchMe.html