Labbinstruktioner för Java/Swing



Relevanta dokument
Två designmönster, MVC och Observer/Observable. Objektorienterad programvaruutveckling GU (DIT011)

Laboration 3 GUI-programmering

Swing. MER Java Foundation Classes (JFC) Hur lära sig? Vad är farorna. LayoutManagers. Exempel på några av komponenterna

Swing. MER Java Foundation Classes (JFC) Vad är farorna. Hur lära sig? LayoutManagers. Exempel på några av komponenterna

Lab5 för prgmedcl04 Grafik

Programmeringsteknik II - HT18. Föreläsning 6: Grafik och händelsestyrda program med användargränssnitt (och Java-interface) Johan Öfverstedt

Subklasser och arv Inledning till grafik (JFrame och JPanel). Något om interface. Objektorienterad programvaruutveckling GU (DIT011) Subklasser

Tentamen i Objektorienterad programmering

TDDE10 m.fl. Objektorienterad programmering i Java Föreläsning 7 Erik Nilsson, Institutionen för Datavetenskap, LiU

Föreläsnings 11 - GUI, Händelsestyrda program, MVC

Frivillig Java-swing-Graphics-lab Programmeringsteknik MN1 vt02

LiTHehack? Här? lithehack se koma Tisdagar och torsdagar

Dagens program. Programmeringsteknik och Matlab. Vad är arv? Vi ärver från GregorianCalendar. Kan vi bygga vidare på existerande klasser?

ID1004 Laboration 3, 5-6 November 2012

Föreläsning 5-6 Innehåll. Exempel på program med objekt. Exempel: kvadratobjekt. Objekt. Skapa och använda objekt Skriva egna klasser

Föreläsning 5-6 Innehåll

Att prova på en enkel Applet och att lära sig olika sätt att hämta data från tangentbordet. Du får även prova på att skapa din första riktiga klass.

Laboration 4: Game of Life

DAT043 - Föreläsning 7

Kort om klasser och objekt En introduktion till GUI-programmering i Java

Tentamen för kursen Objektorienterad programvaruutveckling GU (DIT010)

ITK:P1 Föreläsning 4. Grafiska gränssnitt i Java. AWT-komponenter

Lösningar till tentamen i EDAF25

Tentamen i Objektorienterad programmering

Frames, menyer och GUI-program

Exempel på användning av arv: Geometriska figurer

Föreläsning 15 (16) Historik (java.awt) Historik (javax.swing) Introduktion till Swing

Föreläsning 5 (6) Metoder. Metoder Deklarera. Metoder. Parametrar Returvärden Överlagring Konstruktorer Statiska metoder tostring() metoden javadoc

Föreläsning 3: Händelsestyrda program och användargränssnitt

LÖSNINGSFÖRSLAG Programmeringsteknik För Ing. - Java, 5p

Rita Egna Bilder, Timer

Detta dokument är ett exempel, cirka andra hälften av en tentamen för TDA545 Objektorienterad programvaruutveckling

DI-institutionen Sid 1 av 6 Hans-Edy Mårtensson Sten Sundin

Java-concept och Swing. Swing low, sweet chariot

Föreläsning 14: Grafik & mera händelsehantering

Grafiska användargränsnitt i Java. Föreläsning 7 Innehåll. Använda klasspaketet Swing. Klasspaket i Java. Grafiska användargränsnitt i Java

Dagens program. Programmeringsteknik och Matlab. Objektorienterad programmering. Vad är vitsen med att ha både metoder och data i objekten?

Properties. Användbara metoder som kan anropas i propertychanged:

Tentamen för kursen Objektorienterad programvaruutveckling GU (DIT010)

Programmering med Java. Grunderna. Programspråket Java. Programmering med Java. Källkodsexempel. Java API-exempel In- och utmatning.

Tentamen OOP

Introduktion till Datalogi DD1339. Föreläsning 2 22 sept 2014

EDAA20 Programmering och databaser. Mål komprimerat se kursplanen för detaljer. Checklista. Föreläsning 1-2 Innehåll. Programmering.

Lösningar till tentamen i EDAF25

Händelsestyrda program

Föreläsning 13 Innehåll

Objektorienterad Programkonstruktion. Föreläsning 7 24 nov 2015

Föreläsning 12. Föreläsning 12. Rörliga figurer Klassen Timer Undantag Något om applets. Rörliga appletsfigurer Klassen Timer Undantag

Arv. Fundamental objekt-orienterad teknik. arv i Java modifieraren protected Lägga till och modifiera metoder med hjälp av arv Klass hierarkier

Laboration 1 - Grunderna för OOP i Java

Lösningar till Fiktiv Tentamen på kursen. 2D4135 Objektorienterad programmering, design och analys med Java vt2004. Teoridel

Tentamen, EDAA20/EDA501 Programmering

Objektorienterad Programkonstruktion. Föreläsning 3 9 nov 2015

Objektorienterad Programkonstruktion. Föreläsning 6 23 nov 2015

2I1049 Föreläsning 5. Objektorientering. Objektorientering. Klasserna ordnas i en hierarki som motsvarar deras inbördes ordning

Laboration 15 Grafiskt användargränssnitt

Lösningsförslag tentamen FYTA11 Java

Projekt 2 XL. Observer-mönstret

Objektorienterad Programkonstruktion. Föreläsning 3 7 nov 2016

Kungliga Tekniska Högskolan Ämneskod 2D4134 Nada Tentamensdag maj - 19 Tentamen i Objektorientering och Java Skrivtid 5 h

Föreläsning 8 - del 2: Objektorienterad programmering - avancerat

TDDC30 Programmering i Java, Datastrukturer och Algoritmer Lektion 3

Malmö högskola 2007/2008 Teknik och samhälle

Föreläsning 4. Klass. Klassdeklaration. Klasser Och Objekt

Uppgiften är att beskriva en kvadrat i ett Java program. En första version av programmet skulle kunna se ut så här:

Lite om felhantering och Exceptions Mer om variabler och parametrar Fält (eng array) och klassen ArrayList.

Grundläggande programmering, STS 1, VT Sven Sandberg. Föreläsning 18

F8 - Arv. ID1004 Objektorienterad programmering Fredrik Kilander

Modelsvar för Tentamen för Objektorienterad programvaruutveckling, TDA545

Arv och polymorfi. Lite terminologi; Basklass eller superklass: En klass som fungerar som bas för vårt arv. Vi skapar nya klasser utifrån den.

public och private Obs: private inte skyddar mot access från andra objekt i samma klass.

Inkapsling tumregler. Åtkomstmodifikatorer, instantiering, referenser, identitet och ekvivalens, samt klassvariabler. public och private

Tentamen i Objektorienterad programmering E

Objektorientering. Objekt och metoder. Objektorientering. Viktiga begrepp. Klass. Objekt. Deklarativ programmering

Objektorienterad Programmering DAT043. Föreläsning 6 30/1-18 Moa Johansson (delvis baserat på Fredrik Lindblads material)

Malmö högskola 2008/2009 CTS

1 Uppgift 1. a) Skapar ett Company-objekt med hjälp av den överlagrade konstruktorn. Du kan själv välja värden på instansvariablerna.

Lösningsförslag till omtentamen för TDA540 Objektorienterad Programmering

EDAA20 Programmering och databaser. Mål komprimerat se kursplanen för detaljer. Om att lära sig programmera. Föreläsning 1-2 Innehåll.

JAVA Mer om klasser och objektorientering

Denna vecka. Idag. Grafiskt användarsnitt. Vi kommer att se

Bankkonto - övning. Övning 2 Skriv en metod, geträntan, som returnerar räntan.

KARLSTADS UNIVERSITET 12/8/09 informatik & datavetenskap Johan Öfverberg, Kerstin Andersson Laboration 4, ISG A04 och DVG A08 HT-09

Monday, November 16, Senaste Labben

Malmö högskola 2007/2008 Teknik och samhälle

UML. Översikt UML. Relationer mellan klasser. A är ett aggregerat av B:n. Kontor aggregat av Enheter. 12 olika diagramtyper, bl.a.

Java: Utvecklingsverktyg, datatyper, kontrollstrukturer

TDDC30 Programmering i Java, Datastrukturer och Algoritmer Lektion 3

Mer om grafiska komponenter. Händelsestyrda program

Lektion Händelsehanterare

F4. programmeringsteknik och Matlab

SMD091 Lektion 9. Definition. Inkapsling. Lite repetition. Grafik. Gränssnitt Definition och Implementation. Sammansättning... Implementation.

Tentamen i Objektorienterad programmering E

Tentamen i EDAF25. 1 juni Skrivtid: Skriv inte med färgpenna enda tillåtna färg är svart/blyerts.

TENTAMEN PROGRAMMERINGSMETODIK MOMENT 2 - JAVA, 4P

TENTAMEN OOP

Repetitionsföreläsning 2: Quiz & problemlösning med swing Inget nytt material.

ID1004 Laboration 4, November 2012

2203$( Föreläsning ii - Mer om Java bla this och konstruktorer. Exempel: lampa

tentaplugg.nu av studenter för studenter

Transkript:

Labbinstruktioner för Java/Swing Grafik- och interaktionsprogrammering 2008 Martin Berglund <mabe02@kth.se> Allmänt Dessa instruktioner är på intet sett den enda möjliga lösningen på labben, tvärtom finns det många olika lösningar. Att följa instruktionerna är helt frivilligt och de representerar ett möjligt sätt att angripa problemet med en MVC-lösning. Frågor angående dessa instruktioner besvaras under bokad laborationstid. Författaren hoppas att de ska vara både enkla att förstå och lätta att följa. 1. Modellen Börja med att skapa en klass för vår bakomliggande modell. Vi döper klassen till MyModel. Låt klassen ha en privat medlemsvariabel av typen BufferedImage, vi döper variabeln till mybackend. Initiera mybackend i konstruktorn till MyModel och sätt storleken till ett lämpligt värde, exempelvis 500 pixlar i höjd, 500 pixlar i bredd. Som typ, använd BufferedImage.TYPE_INT_ARGB. Nu fyller vi på med lite metoder som gör att andra klasser också kan interagera med vår model. Skapa en metod getwidth() och en getheight() som returnerar bredden respektive höjden på vår modell (du kan returnera samma tal som du satte högre upp, men visst är det snyggare att returnera mybackend.getwidth() och mybackend.getheight() istället?). Därefter behöver vi ett sätt att rita ut vår modell på en yta, vilken som helst. Här är ett förslag: public void drawmodelonsurface(graphics g) g.drawimage(mybackend, 0, 0, null); Idén är att andra klasser skickar in sina Graphics-objekt till vår modell och modellen ritar ut sig själv på den. Slutligen behövs det något sätt att utifrån manipulera modellen. Eftersom det är vår mybackend som är vår data så kan vi lämna ut ett Graphics-objekt för att manipulera den. Så här skulle det kunna se ut: public Graphics2D getmodelmodifier() return (Graphics2D)myBackend.getGraphics(); Innan vi går vidare från modellen kan det vara bra att se till att den är helt vit till att börja med. Lägg till följande kod efter initieringen i konstruktorn så borde det fixa sig: Graphics g = mybackend.getgraphics(); g.setcolor(color.white); g.fillrect(0, 0, mybackend.getwidth(), mybackend.getheight());

2. Huvudfönstret Skapa en ny klass för huvudfönstret, kalla den MyFrame. Låt den nya klassen ärva av JFrame. Skapa en konstruktor till klassen och som första rad anropa super( GRIP-lab ); eller vilken text som du nu vill ha som titel på det nya fönstret. Lägg till main-metoden här, den skulle kunna se ut så här: public static void main(string []args) new MyFrame().setVisible(true); Om programmet nu körs så kommer vårt nya fönster att visas. Lägg därefter till en medlemsvariabel till klassen av typen JDesktopPane med namn mydesktop. En JDesktopPane är en GUI-komponent som är till för att lägga interna fönster på. Instansiera den i konstruktorn. Eftersom den ska fylla upp hela vårt huvudfönster slipper vi LayoutManagers och kan göra så här istället: setcontentpane(mydesktop); Så kan vi sätta storleken på huvudfönstret med metoden setsize(int width, int height) till något lämpligt, exempelvis 640 x 480. 3. Huvudmenyn Vi fyller på med en huvudmeny i konstruktorkoden till huvudfönstret och skapar lite menyalternativ, exempelvis så här: JMenuBar menubar = new JMenuBar(); JMenu filemenu = new JMenu("File"); JMenu sizemenu = new JMenu("Size"); JMenu colormenu = new JMenu("Color"); JMenu newtoolmenu = new JMenu("New tool"); JMenuItem newwindowitem = new JMenuItem("New window"); JMenuItem size1item = new JMenuItem("1"); JMenuItem size5item = new JMenuItem("5"); JMenuItem size10item = new JMenuItem("10"); JMenuItem colorreditem = new JMenuItem("Red"); JMenuItem colorblueitem = new JMenuItem("Blue"); JMenuItem colorblackitem = new JMenuItem("Black"); JMenuItem colorgreenitem = new JMenuItem("Green"); menubar.add(filemenu); menubar.add(sizemenu); menubar.add(colormenu); menubar.add(newtoolmenu); filemenu.add(newwindowitem); sizemenu.add(size1item); sizemenu.add(size5item); sizemenu.add(size10item); colormenu.add(colorreditem); colormenu.add(colorblueitem); colormenu.add(colorblackitem); colormenu.add(colorgreenitem); setjmenubar(menubar);

Då vill vi ha någonting som kan ta emot händelser från när användaren klickar på någon av dessa. Den som tar emot händelserna måste vara av typen ActionListener, och vi kan låta vår huvudklass sköta detta. Låt MyFrame implementera ActionListener och skapa metoden public void actionperformed(actionevent e) som implementeringen kräver. Länka sedan alla menyalternativ i konstruktorn till vår nydefinierade händelsehanterare. Exempelvis så här: newwindowitem.addactionlistener(this); size1item.addactionlistener(this); size5item.addactionlistener(this); size10item.addactionlistener(this); colorreditem.addactionlistener(this); colorblueitem.addactionlistener(this); colorblackitem.addactionlistener(this); colorgreenitem.addactionlistener(this); Vi måste kunna skilja på händelserna också, så ge de olika knapparna olika ActionCommand. Exempelvis: newwindowitem.setactioncommand("new window"); size1item.setactioncommand("size"); size5item.setactioncommand("size"); size10item.setactioncommand("size"); colorreditem.setactioncommand("color"); colorblueitem.setactioncommand("color"); colorblackitem.setactioncommand("color"); colorgreenitem.setactioncommand("color"); Sätt slutligen en print-sats i actionperformed som skriver ut vilket actioncommand som användes och testa så att programmet fungerar! 4. Vyer Skapa en ny klass för vårt interna fönster, döp den till MyInternalFrame. Ärv av JInternalFrame och skapa en konstruktor. Sätt första raden till super("vy", false, true, false, true); så får vi en snygg titel på våra interna fönster. För de övriga parametrarna hänvisar jag till officiell dokumentation. Större delen ska täckas av rit-ytan, men den gör vi bäst som en egen komponent, så skapa ännu en ny klass som vi döper till MyView. Låt den ärva av JComponent. Skapa en tom konstruktor. Vi ska använda vår modell som källdata för vyn, men låt oss först testa om komponenten fungerar. Vi överlagrar paint( ) eller paintcomponent( ) för att kunna styra över dess utseende när komponenten ska ritas ut på skärmen. I vår labb är det ingen större skillnad på vilken av de två vi överlagrar, men Sun anser att vi ska använda paintcomponent så låt oss göra det. Här är ett exempel som ritar lite skräp på komponentens yta: protected void paintcomponent(graphics g) Random random = new Random(); for(int y = 0; y < getheight(); y++)

for(int x = 0; x < getwidth(); x++) g.setcolor(new Color(random.nextInt(255), random.nextint(255), random.nextint(255))) ; g.drawrect(x, y, 1, 1); Vi vill nu att våra interna fönster ska ha en MyView-komponent som täcker hela deras yta. Vi får göra det med en BorderLayout. Lägg till det här till MyView-komponentens konstruktor: getcontentpane().setlayout(new BorderLayout()); getcontentpane().add(new MyView(), BorderLayout.CENTER); pack(); Innan vi kan testa måste vi kunna lägga till nya interna fönster till vårt huvudfönster. Vi återgår till actionperformed( ) i MyFrame och lägga till just den funktionaliteten: if(e.getactioncommand().equals("new window")) MyInternalFrame internalframe = new MyInternalFrame(); internalframe.setvisible(true); mydesktop.add(internalframe); Nu borde programmet vara klart att testas! Om du tycker att de interna fönstrena är för små kan du lägga till följande rad i MyViewkonstruktorn: setpreferredsize(new Dimension(200, 200)); men se till att ta bort den sen! 5. Koppla vyn till modellen Istället för skräp vill vi att MyView ska visa vad som finns i modellen. Dessutom vill vi att modellen ska bestämma storleken på MyView, så att de matchar. Börja med att skapa en medlemsvariabel till MyFrame som är privat, av typen MyModel och som får namnet mymodel. Instansiera den i konstruktorn. Vi vill kunna passa ner den till varje vy-komponent, så modifiera konstruktorn till MyInternalFrame så att den ta emot ett argument av typ MyModel. Ändra så koden som skapar nya MyInternalFrame-objekt så den skickar med vår medlemsvariabel: MyInternalFrame internalframe = new MyInternalFrame(myModel); Därefter i konstruktorn till MyInternalFrame, skicka vidare den till vår MyView: getcontentpane().add(new MyView(myModel), BorderLayout.CENTER); Modifiera konstruktorn I MyView så den tar emot en MyModel och spara undan den i en egen medlemsvariabel, exempelvis såhär: public class MyView extends JComponent private MyModel mymodel; public MyView(MyModel mymodel)

this.mymodel = mymodel; Nu ska vi ändra på paintcomponent-metoden så att den istället ritar ut vår modell. I steg 1 skapade vi en metod i modell-klassen som gör att den ritar ut sig på en annan yta. Låt oss använda den! protected void paintcomponent(graphics g) //Töm komponenten: g.setcolor(color.white); g.fillrect(0, 0, getwidth(), getheight()); //Skicka vidare till modellen mymodel.drawmodelonsurface(g); Så borde vi sätta komponentens storlek till samma som modellen. Vi lägger till i konstruktorn: setsize(mymodel.getwidth(), mymodel.getheight()); Testa igen! De interna fönstrena borde nu bli så stora som du satte modellen och vara helt vita, eftersom modellen är helt vit. Låt oss ändra på den saken! 6. Verktyg Låt oss börja med en gemensam basklass för verktyg som kan manipulera bilden. Vi döper den till MyTool. Låt den vara abstract med en konstruktor som är protected. Låt den ha följande medlemsvariabler: private MyModel mymodel; private int x; private int y; private int size; private Color color; och låt konstruktorn ta emot och sätta dessa värden. Skapa sedan get-metoder och set-metoder för dessa variabler, undantaget mymodel som bara behöver en get-metod, som kan vara protected (så ingen annan klass pillar på den). Skapa därefter en abstrakt metod geticon() som returnerar en Image, det får vara ikonen för verktyget. Låt metoden vara protected så kan ingen utomstående klass komma åt ikonen. Vi kan därefter skriva en metod som ritar ut vårt verktyg på en yta: public void painttoolonsurface(graphics g) g.drawimage(geticon(), x, y, null); För att kunna interagera med användaren måste vårt verktyg kunna ta emot händelser såsom när vi klickar och flyttar musen. Låt oss därför lägga till några fler abstrakta metoder: public abstract void mousedown(); public abstract void mouseup(); public abstract void mousedragged(); Vi vill att varje vy ska ha sin egen uppsättning verktyg på skärmen så lägg till en medlemsvariabel till klassen MyView av typen ArrayList<MyTool>, vi kan kalla den tools. Om det inte fungerar att definiera en ArrayList med generics ( < och > ) så beror det på att du kör en gammal version av Java (< 1.5) och du rekommenderas uppgradera eller anpassa

koden därefter. Instansiera variabeln i konstruktorn. Skapa en metod för att lägga till verktyg till vyn, exempelvis public void addtool(mytool newtool) som lägger till verktyget i listan. Så ska vi rita ut alla verktyg när vi ritar komponenten. Eftersom vi vill att verktygen ska ligga överst gör vi det sist i paintcomponent-metoden, såhär kanske: for(mytool tool: tools) tool.painttoolonsurface(g); Nu kan vi alltså lägga ut abstrakta verktyg på vår komponent men vi kanske fortfarande inte interagera med dem. 7. Händelser från användaren Låt oss börja med att fånga händelser från musen som rör vår vykomponent. Låt därför MyView implementera MouseListener och MouseMotionListener. Här skulle vi kunna låta en intern klass sköta indatahanteringen, det skulle bli mer MVC, men i vårt exempel kan vi låta MyView ta hand om det. Implementera alla metoder som är obligatoriska i och med de två interfacen men lämna de tomma tills vidare. Gör kopplingen mellan komponenten och sig själv som hanterare genom att lägga till i konstruktorn: addmouselistener(this); addmousemotionlistener(this); Användaren kan bara använda ett verktyg åt gången per vy så vi kommer att behöva en variabel som håller reda på vilken (om någon) som är den aktiva. Deklarera denna variabel som activetool av typen MyTool och gör den privat i klassen. Sätt den till null i konstruktorn. Låt oss då bestämma hur verktygen ska styras. Vänstra musknappen väljer verktyg (om inget verktyg är aktivt) eller ritar (om ett verktyg är aktivt) Högra musknappen släpper ett verktyg (om ett verktyg är aktivt) eller gör ingenting (annars) Så, vi måste kunna veta när användaren har klickat på ett verktyg. Låt oss bestämma oss för att alla verktyg har storlek 32x32 pixlar och skriva följande metod till MyView: private MyTool pickuptool(int mouseatx, int mouseaty) for(mytool tool: tools) if(mouseatx >= tool.getx() && mouseatx < tool.getx() + 32 && mouseaty >= tool.gety() && mouseaty < tool.gety() + 32) return tool; return null;

Så kan vi skriva till vår mouseclicked-metod: if(e.getbutton() == MouseEvent.BUTTON1 && activetool == null) activetool = pickuptool(e.getx(), e.gety()); if(activetool!= null) //Fick vi något? //Det här gömmer muspekaren! setcursor(gettoolkit().createcustomcursor( new BufferedImage(3, 3, BufferedImage.TYPE_INT_ARGB), new Point(0, 0), "null")); else if(e.getbutton() == MouseEvent.BUTTON3 && activetool!= null) activetool = null; setcursor(cursor.getdefaultcursor()); Vi kollar alltså vilken musknapp som har klickats och plockar upp respektive släpper verktyg. Då ska vi bara hantera förflyttningar och ritoperationer så är vi nästan färdiga! Om musen förflyttas över vyn när vi har ett verktyg valt så ska verktyget förflyttas med musen. Metoden mousemoved anropas när musen rör sig över komponenten utan att någon knapp är nertryckt och mousedragged anropas vid rörelser när någon knapp är det. I båda fallen vill vi uppdatera verktygets position så lägg till: if(activetool!= null) activetool.setx(e.getx()); activetool.sety(e.gety()); För mousedragged vill vi också säga till verktyget att en knapp är nertryckt så anropa också activetool.mousedragged() efter sety. Vi vill också meddela när vänstra knappen tryckts ner och när den släppts upp igen. Vi skulle kunna skriva mousepressed så här: if(e.getbutton() == MouseEvent.BUTTON1 && activetool!= null) activetool.mousedown(); och mousereleased så här: if(e.getbutton() == MouseEvent.BUTTON1 && activetool!= null) activetool.mouseup(); Vi är färdiga med vyns hantering av verktyg men än så länge har vi inga verktyg definierade! Låt oss fixa det. Vi skapar en enkel penna, gör en ny klass som ärver av MyTool och kalla den för, säg, MyPen. Definiera en konstruktor som tar emot samma argument som MyTool och passa vidare dem till via super-anropet. Exempelvis såhär: public MyPen(MyModel mymodel, int x, int y, int size, Color color) super(mymodel, x, y, size, color);

Överlagra därefter de abstrakta metoderna från MyTool. Vi börjar med att göra en (mycket) enkel ikon för vår penna. Lägg till en BufferedImage som en privat medlemsvariabel och instansiera den i konstruktorn, säg 32x32 pixlar. Använd objektets getgraphics och färga hela bilden gul (eller någon annan färg). I den överlagrade geticon returnerar du ikonvariabeln. Då ska vi bara fixa de tre övriga metoderna i klassen. I vår enkla penna behöver i inte bry oss om mouseup så den kan vara tom. Men på en position där vi klicka ner musknappen och en position dit vi dragit musen vill vi rita. Vi kan använda samma kod för båda, här är ett förslag: Graphics2D g = getmymodel().getmodelmodifier(); g.setcolor(getcolor()); g.setstroke(new BasicStroke(getSize())); g.drawrect(getx(), gety(), 1, 1); För att testa om vår penna fungerar så lägger vi till en statisk penna till MyViews konstruktor: tools.add(new MyPen(myModel, 50, 50, 3, Color.black)); Testa! 8. Observerare Ok, det var inte helt lyckat, eller hur? Komponenten ritas inte om när vi börjar interagera med den, vi måste minimera och återställa fönstret för att se vad som händer. Hur kan vi då lösa detta? Jo, en första enkel grej vi kan göra är att lägga till ett anrop till repaint() när vi vet att komponenten inte stämmer längre, förslagsvis i mousepressed, mousereleased, mousemoved och mousedragged. Men det här hjälper bara till hälften, om vi öppnar fler vyer så uppdaterar den bara sig själv. Låt oss därför att använda ett designmönster som heter Observer för att lösa detta. Mönstret finns färdigskrivet med hjälpklasser i Java, så låt oss använda dessa. Den första heter Observable och gör att andra kan prenumerera på uppdateringar från den här klassen. Låt vår modellklass MyModel ärva från Observable! Det är vår vy-komponent som beror på hur modellen ser ut, så låt den implementera interfacet Observer. Vi måste då implementera en metod update för att uppfylla interfacet Observer, så gör det. Den här metoden kommer att anropas när vår modell har förändrats, så lägg till en repaint() i den. Eftersom vi vill att våra MyView-objekt ska prenumerera på förändringar från MyModel så lägg till i konstruktorn: mymodel.addobserver(this); Nu ska vi bara lägga till en upplysning om när vi har ändrat på modellen. I en enkel version av designmönstret skulle det räcka med att använda metoden notifyobservers() för att updatemetoden hos alla prenumeranter ska anropas med i Java har de byggt in en mekanism som gör att man måste anropa setchanged() först innan det går. Metoden setchanged är tyvärr protected så vi kanske gör enklast i att skapa en ny metod till MyModel enligt: public void informobservers() setchanged(); notifyobservers(); Därefter kan vi anropa getmymodel().informobservers(); i vår Pen-klass (och alla andra verktyg vi definierar) efter varje gång vi ritat på modellen. Testa igen så ser det nog bättre ut!

9. Koppla in menyn Ta bort testpennan från MyView-konstruktorn, nu kopplar vi in den riktiga menyn istället. Så här skulle vi kunna lägga till en penna till verktygsmenyn: JMenuItem toolpenitem = new JMenuItem("Penna"); newtoolmenu.add(toolpenitem); toolpenitem.addactionlistener(this); toolpenitem.setactioncommand("new_pen"); Vi lägger till en motsvarande händelse i actionperformed( ): else if(e.getactioncommand().equals("new_pen") && mydesktop.getselectedframe()!= null) MyInternalFrame frame = (MyInternalFrame)myDesktop.getSelectedFrame(); frame.addtool(new MyPen(myModel, 24, 24, 1, Color.black)); fast addtool finns ju inte i MyInternalFrame utan i MyView, eller hur? Så skapa en sådan metod också i MyInternalFrame som bara passar vidare verktyget till vyn. Så att det nya verktyget ska synas när det läggs till, gör en repaint() i MyView-klassens addtool. För att vi ska kunna ändra på ett aktivt verktyg måste vi kunna hämta det från huvudfönstret. Skapa därför en get-metod för activetool i MyView och en metod med samma namn i MyInternalFrame som skickar vidare verktyget från vyn. Därefter kan vi skriva följande till actionperformed i MyFrame: else if(e.getactioncommand().equals("size") && mydesktop.getselectedframe()!= null) JMenuItem menuitem = (JMenuItem)e.getSource(); MyInternalFrame frame = (MyInternalFrame)myDesktop.getSelectedFrame(); MyTool tool = frame.getactivetool(); if(tool!= null) tool.setsize(integer.parseint(menuitem.gettext())); else if(e.getactioncommand().equals("color") && mydesktop.getselectedframe()!= null) JMenuItem menuitem = (JMenuItem)e.getSource(); MyInternalFrame frame = (MyInternalFrame)myDesktop.getSelectedFrame(); MyTool tool = frame.getactivetool(); if(tool!= null) if(menuitem.gettext().equals("red")) tool.setcolor(color.red); if(menuitem.gettext().equals("blue")) tool.setcolor(color.blue); if(menuitem.gettext().equals("black")) tool.setcolor(color.black);

if(menuitem.gettext().equals("green")) tool.setcolor(color.green); 10. Resten Nu är nästan allting färdigt, det behövs lite fler verktyg och andra små inställningar så programmet fungerar som du vill. Framför allt vill du nog fixa lite snyggare verktygsikoner. Klassen ImageIO kan du använda om du vill läsa in bilder från fil, annars är det inte särskilt svårt att rita en ikon med bara en Graphics2D. För ett exempel på ett lite mer avancerat (fast inte så mycket ) verktyg har jag bifogat koden för ett linjeverktyg. Att göra verktyg för elipser, rektanglar fungerar i princip på samma sätt. Prova gärna att göra verktyg som manipulerar bilden på lite ovanliga sätt, exempelvis ett inverteringsverktyg (inverterar pixelvärden där den ritar) eller ett blur-verktyg (jämnar ut pixelvärden där den ritar). Bilaga A: MyLineTool.java package grip08; //Byt ut till ditt paketnamn! import java.awt.basicstroke; import java.awt.color; import java.awt.graphics; import java.awt.graphics2d; import java.awt.image; import java.awt.image.bufferedimage; public class MyLineTool extends MyTool private Image icon; private boolean isdrawing; private int mouselastx; private int mouselasty; public MyLineTool(MyModel mymodel, int x, int y, int size, Color color) super(mymodel, x, y, size, color); icon = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB); Graphics g = icon.getgraphics(); g.setcolor(color.green); g.fillrect(0, 0, 32, 32); isdrawing = false; mouselastx = mouselasty = -1; @Override protected Image geticon() return icon; @Override

public void mousedown() if(mouselastx < 0 && mouselasty < 0) mouselastx = getx(); mouselasty = gety(); isdrawing = true; @Override public void mousedragged() @Override public void mouseup() if(mouselastx!= -1 && mouselasty!= -1) Graphics2D g = getmymodel().getmodelmodifier(); g.setcolor(getcolor()); g.setstroke(new BasicStroke(getSize())); g.drawline(mouselastx, mouselasty, getx(), gety()); isdrawing = false; mouselastx = mouselasty = -1; @Override public void painttoolonsurface(graphics2d g) //Rita verktyget som vanligt super.painttoolonsurface(g); //Rita vår temporära linje if(isdrawing) g.setcolor(getcolor()); g.setstroke(new BasicStroke(getSize())); g.drawline(mouselastx, mouselasty, getx(), gety());