Frivillig Java-swing-Graphics-lab Programmeringsteknik MN1 vt02 Denna laboration är frivillig och syftar till att låta dig lära mer om grafik i java. Labben är ganska grundlig och går igenom vad du skall göra steg för steg. Den skall ej redovisas. 1 Katten igen Du skall nu arbeta med Katt.java och KattRitare.java som du använde i första inlämningsuppgiften. Detta lilla program ritar ut en katt i ett fönster genom att definiera klasserna KattRitare som ärver av JPanel och Katt som ärver av JFrame. KattRitare överlagrar metoden paintcomponent. Denna metod anropas så fort komponenten behöver ritas. Genom att ändra i denna kan man bestämma vad som skall ritas ut. Det var detta du gjorde förut. Som parameter till paintcomponent ges ett objekt g av klassen Graphics. Denna klass beskriver den rityta objektet har förfogande över i fönstret, och innehåller metoder för att rita saker där. Eftersom KattRitare ärver från JPanel är det en swing-komponent, och vi kan hantera den som en del av ett grafiskt användaregränssnitt. 1.1 Katt För att vi skall kunna titta på katten måste den ligga i ett fönster. Detta är vad Katt är. Denna klass ärver från JFrame som beskriver ett fönster. Kompilera och kör Katt.java, testa att ändra storlek på fönstret. Vad händer? Öppna nu upp Katt.java i emacs. I konstruktorn finner du följande rader: Container c = getcontentpane(); l = new KattRitare(); c.setbackground(color.white); c.setlayout(new FlowLayout() ); setsize(300,300); c.add(l); 1
setvisible(true); setdefaultcloseoperation(exit_on_close); Några av dessa rader skall vi titta närmare på. Den första raden använder en metod ärvd från JComponent för att ta reda på fönstrets sk. content pane. Alla top-nivå komponenter i swing har sådana, detta är ett objekt av typen Container som har som uppgift att hålla reda på underkomponenter. Raden c.setbackground(color.white); sätter bakgrundsfärgen i fönstret. Prova att ändra denna till någon annan färg. Syns något? Prova att ändra fönstrets storlek. Varför blir inte hela bakgrunden vit. Jo därför att en del av den upptas av komponenten l av klassen KattRitare som definierar sin bakgrund som vit. Titta i KattRitare.java! Raden setsize(300,300); sätter fönstrets storlek. Prova att ändra denna! Vad händer om du gör den större? Mindre? Vad händer ifall du kommenterar bort raden? c.add(l); lägger till vår KattRitare-komponent till fönstret. Om du tar bort raden läggs komponenten inte till fönstrets content pane, och syns alltså inte. Prova detta. setvisible(true); gör fönstret (din JFrame) synlig, ett swing-fönster är inte synligt, även om det har skapats. Man måste säga till att det skall visas explicit. Ta bort raden och se! 1.2 KattRitare Du har ju redan (i inlupp 1) varit inne i paintcomponent för KattRitare och ändrat på kattens utritningsprogramkod, så förhoppningsvis har du ett hum om hur den fungerar, eller så kan du med din nya programmeringserfarenhet lista ut vad de flesta satser gör. Vi skall dock kolla lite på konstruktorn i klassen KattRitare: setbackground(color.white); setpreferredsize(new Dimension(300,300) ); Den första raden känner du nog igen nu, den sätter bakgrundsfärgen. Du kan prova att ändra den om du vill. Den andra raden, setpreferredsize(new Dimension(300,300) );, anger komponentens storlek. Som du ser är det inte samma metod setsize som användes i 2
Katt. Detta eftersom Katt som ärvt av JFrame är ett fönster, och denna storlek kan vi sätta eftersom det är en topnivå-komponent. Underliggande komponenter däremot, som tex JPanel kan ibland göras beroende av storleken hos de komponenter de ligger i, och då kan man inte sätta någon fast storlek. Men man kan ange vilken storlek man skulle vilja att komponenten hade. Prova att ändra! Vad händer? 2 En katt som ändrar färg... Vi skall nu utöka vår katt lite genom att få kattens ögon att blinka i olika färger. För att göra detta måste vi ha ett attribut i vår katt-klass som talar om vilka färger som kattens ögon kan ha, samt ett som säger vilken färg som gäller just nu. Vi måste också ha en metod för att hämta färgen från katten, så att KattRitare kan rita ut denna. Vi börjar med attributen. En array är en bra datastruktur för att lagra en tabell av objekt. Tex olika färger. Skapa ett attribut Color[] colors; i katt-klassen. För att tala om vilken av alla färger i arrayen som skall användas gör måste vi ha ett heltal som talar om vilken index i arrayen som skall användas. Detta attribut skapas med int colorindex;. Nu kan vi gå in i konstruktorn i Katt och initalisera våra nya attribut. Lägg till följande: colors = new Color[3]; colors[0] = Color.blue; colors[1] = Color.green; colors[2] = Color.yellow; colorindex = 0; Den första raden skapar en array som kan inehålla tre objekt av klassen Color. Sedan lägger vi in färgerna blått, grönt och gult i tur och ordning. Till sist sätter vi colorindex värde till 0. Nu behövs det en metod för att ta reda på vilken färg som är den aktuella. Dvs vilken färg som colorindex pekar på. Skriv alltså en metod som ser ut enligt följande: public Color getcurrentcolor() return colors[colorindex]; 3
Denna returnerar en Color genom att ta den färg i arrayen som colorindex indexerar till. Just nu förändras ju inte colorindex någonstans, så den kommer alltid att vara 0 och den blå färgen kommer alltid att returneras, men det skall vi ändra på senare. Förstår du varför den blå färgen alltid returneras? Hur kan man ändra så att den gula alltid returneras? Din katt skall nu se ut såhär: import java.awt.*; import javax.swing.*; public class Katt extends JFrame private KattRitare l; Color[] colors; int colorindex; public Katt() Container c = getcontentpane(); l = new KattRitare(); c.setbackground(color.white); c.setlayout(new FlowLayout() ); setsize(300,300); c.add(l); setvisible(true); setdefaultcloseoperation(exit_on_close); colors = new Color[3]; colors[0] = Color.blue; colors[1] = Color.green; colors[2] = Color.yellow; colorindex = 0; public Color getcurrentcolor() return colors[colorindex]; public static void main(string[] arg) Katt k1 = new Katt(); 2.1 KattRitare För att förändra kattens ögon måste vi nu också ändra i KattRitare, så att detta ritas ut. 4
I paintcomponent använder g den färg som tidigare angivits med dess setcolor metod. Kolla vilken färg detta är! Nu skall vi, precis som i inlupp 1 och lägga in en ny sats som sätter färgen. Detta måste göras före vi ritar ut ögonen. I stället för att sätta ögonens färg till ett konstant värde vill vi ju fråga vår Katt viken ögonfärg den har för närvarande (kattens ögonfärg skall ju ändras). För att göra detta måste KattRitare känna till det Katt-objekt som äger den. KattRitare måste alltså innehålla ett attribut av klassen Katt. Lägg till satsen Katt katten; Nedanför de andra attributen i KattRitare. Nu kan KattRitare referera till ett objekt av klassen Katt. Men vi måste kunna sätta attributet också. Detta kan göras med en set-metod, men även genom konstruktorn. Nu skal du göra det genom att låta KattRitares konstruktor ta in ett objekt av klassen Katt och sätta attributet katten till detta. Gör om den standardkonstrktor som finns i KattRitare till en alternativ konstruktor som tar in ett Katt-objekt i stället. Sedan måste den naturligtvis sätta katten till detta. Det ser ut som följer: public KattRitare(Katt k) setbackground(color.white); setpreferredsize(new Dimension(300,300) ); // Sätter katten = k. katten = k; Nästa steg är att i paintcomponent ta reda på vilken färg kattens ögon har och sätta denna. Här finns det följande rader som ritar ut kattens ögon: g.filloval(60, 60, 10, 10); g.filloval(90, 60, 10, 10); // ögon Precis ovanför dessa behöver vi alltså sätta kattens ögonfärg. Hur tar man reda på denna? Jo, vi har ju redan i Katt definierat en metod som tar reda på detta, nämligen getcurrentcolor. Du kan alltså nu lägga till raden: g.setcolor( katten.getcurrentcolor() ); ovanför de satser som ritar kattens ögon. katten.getcurrentcolor() returnerar ju färgen som kattens ögon har och sedan sätts denna genom metoden setcolor hos obejktet g. Du skulle lika gärna kunna ha gjort: Color c = katten.getcurrentcolor(); g.setcolor( c ); 5
Förstår du varför dessa satser är likadana? I den senare mellanlagras färgen i en temporär variabel, c, medan den resulterade färgen skickas direkt in i setcolor i den förra. KattRitare.java borde nu se ut ungefär som nedan: import java.awt.*; import javax.swing.*; public class KattRitare extends JPanel Font f = new Font(Serif", Font.ITALIC, 18); Katt katten; public KattRitare(Katt k) setbackground(color.white); setpreferredsize(new Dimension(300,300) ); // Sätter katten = k. katten = k; public void paintcomponent(graphics g) super.paintcomponent(g); g.setcolor(color.black); g.drawrect(50, 50, 60, 60); // huvud g.drawrect(80, 225, 140, 5); // svans g.setcolor(color.white); g.filloval(20, 110, 120, 120); // vitt inne i kroppen g.setcolor(color.black); g.filloval(75, 75, 10, 10); // nos g.drawoval(20, 110, 120, 120); // rita kropp, kontur // Sätter kattens ögonfärg. g.setcolor( katten.getcurrentcolor()); g.filloval(60, 60, 10, 10); // ögon g.filloval(90, 60, 10, 10); g.setcolor(color.black); g.drawline(50, 50, 60, 30); // öron g.drawline(60, 30, 70, 50); g.drawline(110, 50, 100, 30); g.drawline(100, 30, 90, 50); g.setcolor(color.red); g.drawarc(60, 80, 40, 20, 180, 180); // mun g.setcolor(color.black); g.setfont(f); g.drawstring(mjava", 200, 50); 6
2.2 Tillbaka till Katt.java Nu har vi gjort förändringar i KattRitares konstruktor. Men denna används ju i Katt, därför kommer inte programmet att kompilera (eftersom Katt använder KattRitares standardkonstruktor). I stället för att använda KattRitares standardkonstruktor i Katts konstruktor skall vi använda den alternativa konstruktorn. Men denna tar ett Katt-objekt som inparameter, dvs vi måste på något sätt skicka in en referens till det Kattobjekt som konstruktorn hör till. Objektet måste alltså skicka in en referens til lsig själv, ungefär som att ge ut ett visitkort. Detta går med det reserverade ordet this. Om du inte kommer ihåg exakt hur this används, så kan man säga att this alltid refererar till det objekt man befinner sig i. För att tex använda ett attribut inuti en klass kan man antingen skriva endast attributnamnet, eller så kan man skriva this.attributnamn. Du skall alltså ta bort satsen l = new KattRitare(); och i stället lägga till l = new KattRitare(this);. Om du nu kompilerar klasserna och kör Katt så kommer katten att visas med blå ögon. Men de blinkar ju inte! Varför då? Jo, även om KattRitare frågar Katt om vilken färg den har, och får reda på att den är blå, så finns det ingenting som ändrar colorindex i Katt under programmets gång. Den kommer alltså alltid att vara 0 och indexera till första färgen i arrayen colors! 2.3 Animation För att kattens ögonfärg skall ändras kontenueligt behövs en metod som kör igång och sedan hela tiden ändrar colorindex så att den indexterar till olika färger. Denna metod måste även säga åt KattRitare att rita om sig, så att den nya färgen ritas. Skriv nu en metod i Katt som heter blink och som är deklarerad enligt: public void blink() while( true ) colorindex++; if( colorindex >= colors.length ) colorindex = 0; l.repaint(); 7
Eftersom uppdateringen av färger skall ske om och om igen så länge progammet är igång skall den ju innehålla en while-loop. Denna while loop håller på så länge villkoret innanför paranteserna är sant. Eftersom där står true som är det reserverade ordet för sant, och därför alltid är sant, så kommer det som står innanför while-loopen att utföras i evighet (eller så länge programmet kör). Så vad händer i loopen då? Jo först ökas colorindex med 1. Men eftersom colorindex skall indexera i en array som är av en viss storlek är det ju viktigt att den börjar om på 0 (det första elementet) när den har nått slutet. Detta åstadkoms med if-satsen. Där kollar man om colorindex är större än colors.length (som ju är arrayens storlek), i så fall är ju indexet större än arrayen och då skall det börja om från början igen. Alltså sätts colorindex till 0. Därefter måste man säga åt KattRitaren att rita om sig, annars sysn ju inte förändringen. Detta görs med metoden repaint som finns deklarerad i alla swingkomponenter (KattRitare ärver ju från komponenten JPanel och är alltså en komponent), alltså: l.repaint();. När metoden körs kommer den alltså att gå in i en loop som den inte kan ta sig ur där ändra den färg som ögonen skall ha och sedan rita om. Detta kommer att hålla på tills någon avbryter programmet. Nu är det bara ett par små saker kvar att göra. För det första måste ju metoden anropas någonstans ifrån. Detta görs i main genom att lägga till k1.blink(); (efter Katt k1 = new Katt();). Kompilera och kör Katt. Nu skall du se att ögonen blinkar. Tycker du att de blinkar för fort, eller för ojämt. Det är för att datorn försöker köra igenom loopen så snabbt den bara kan. Man kan lägga in en sats som försöker vänta en viss tidsrymd i loopen så går den långsammare. Detta görs med XThread.delay( 100 ); som finns i extrapaketet. (Det betyder att du även behöver lägga till import extra.*; längst uppe i Katt.java). 100 som står i parentesen anger hur många millisekunder programmet skall vänta innan det kör vidare. Du kan ändra detta till ett annat värde för att ändra hastigheten. Lägg nu till delay-satsen i loopen. Kompilera och kör Katten. Testa med att ändra antalet värdet i delay-satsen. Nu är din Katt färdig. Den borde se ut såhär: import java.awt.*; import javax.swing.*; import extra.*; 8
public class Katt extends JFrame private KattRitare l; Color[] colors; int colorindex; public Katt() Container c = getcontentpane(); l = new KattRitare(this); c.setbackground(color.white); c.setlayout(new FlowLayout() ); setsize(300,300); c.add(l); setvisible(true); setdefaultcloseoperation(exit_on_close); colors = new Color[3]; colors[0] = Color.blue; colors[1] = Color.green; colors[2] = Color.yellow; colorindex = 0; // Returnerar nuvarande färgen. public Color getcurrentcolor() return colors[colorindex]; // Loopar och blinkar. public void blink() while( true ) colorindex++; // Om indexet är utanför arrayen. Börja om. if( colorindex >= colors.length ) colorindex = 0; // Be KattRitare att rita om sig. l.repaint(); // Vänta ett slag. XThread.delay( 100 ); public static void main(string[] arg) Katt k1 = new Katt(); //Börja blinka. k1.blink(); 9
Till sist. Experimentera med Katt och KattRitare. Förstår du hur de fungerar och är relaterade till varandra? 10