Programmeringsteknik II - HT18 Föreläsning 6: Grafik och händelsestyrda program med användargränssnitt (och Java-interface) Johan Öfverstedt 18-09-28 1
Förra gången: Arv och klasshierarkier Vi såg hur man kan uppnå åtminstone 2 saker med arv: 1) Återanvändande av kod 2) Abstraktion och dynamiskt beteende med hjälp utav dynamisk bindning 18-09-28 2
// Minimalt exempel: Dynamisk bindning public abstract class A { public abstract void dostuff(); public class B extends A { public void dostuff() { System.out.println("B did stuff"); public class C extends A { public void dostuff() { System.out.println("C did stuff"); 18-09-28 3
public static void main(string[] args) { A[] as = new A[2]; as[0] = new B(); as[1] = new C(); // A är basklassen, varje 'a' kan vara // av olika subklasser for(a a : as) { a.dostuff(); // Ger utskrift: // B did stuff // C did stuff 18-09-28 4
I det här fallet är det överflödigt att definera A som en abstrakt klass då den varken innehåller: 1) Instansvariabler 2) Implementerade metoder Ett bättre alternativ: interface 18-09-28 5
Ett interface är som en klass med enbart abstrakta metoder (utan implementering). En klass i Java ärver (extends) från exakt 1 klass men kan implementera (implements) många interface. Man kan alltså inte pussla ihop sina klasser genom att ärva från flera basklasser medan det är fullt möjligt med interface. Detta gör det lättare att dela upp programmets abstraktioner i lätthanterliga bitar och hålla isär icke relaterade begrepp. 18-09-28 6
// Minimalt exempel (version 2): Med interface public interface A { public abstract void dostuff(); public class B implements A { public void dostuff() { System.out.println("B did stuff"); public class C implements A { public void dostuff() { System.out.println("C did stuff"); 18-09-28 7
public interface A { public abstract void dostuff(); public interface Z { public abstract boolean hasthestuff(); public class B implements A, Z { @Override public void dostuff() { // From A System.out.println("B did stuff"); @Override public boolean hasthestuff() { // From Z return true; 18-09-28 8
interface är som ett kontrakt. När man säger att en klass "implements" ett interface lovar man att alla dess metoder kommer vara implementerade i klassen. Annars: kompileringsfel. Vi kan därmed skriva: Z z = new B(); System.out.println("" + z.hasthestuff()); och få utskriften "true". 18-09-28 9
Vi kan inte skriva z.dostuff(); Trots att z är ett objekt av typen B, så var variabeln z deklarerad som interface-typen Z och interfacet Z innhåller inte någon metod som heter dostuff. 18-09-28 10
Exempel på interface: Comparable<T>: Med metoden compareto som inför en totalordning för T. Set<T> Med metoderna contains, insert,... Map<Key, Value> Med metoderna containskey, containsvalue, insert,... List<T> Med metoderna add, get, size,... ActionListener (från AWT-ramverket) Med metoden actionperformed som hanterar händelser i ett program. 18-09-28 11
Några frågor om interface? 18-09-28 12
Grafik Hur skriver man grafiska program? Grafiska program är av naturen annorlunda än de flesta program ni har skrivit hittills i Programmeringsteknik I/II. Det går inte att rita på skärmen på samma sätt som man matar ut text i terminalen, (System.out.println("text")). Man måste tala om vart grafiken ska ritas och hur den ska visas. Man bygger vanligtvis grafiska program inom ett ramverk. I den här kursen kommer vi använda Java Swing. 18-09-28 13
Ett litet exempelprogram import java.awt.*; import java.awt.event.*; import javax.swing.*; public class GraphicsExample extends JPanel { public GraphicsExample() { super(); super.setpreferredsize( new Dimension(400, 400)); super.setbackground(color.white); 18-09-28 14
@Override public void paintcomponent(graphics g) { super.paintcomponent(g); //void setcolor(color color) g.setcolor(color.red); //void fillrect( // int x, int y, // int width, int height) g.fillrect(100, 100, 200, 200); paintcomponent fortsätter... 18-09-28 15
paintcomponent fortsätter här... g.setcolor(color.black); //void filloval( // int x, int y, // int width, int height) g.filloval(100, 100, 200, 200); //void drawline( // int x1, int y1, int x2, int y2) g.drawline(0, 0, 100, 100); 18-09-28 16
public static void main(string[] args) { // Create window JFrame window = new JFrame("Ex"); // Create panel GraphicsExample panel = new GraphicsExample(); // Add panel to window window.getcontentpane().add(panel); // Resize window to fit panel window.pack(); // Show window window.setvisible(true); 18-09-28 17
18-09-28 18
Koordinater anges från övre vänstra hörnet av rutan som är origo, här i en 400 x 400 stor ruta. (0, 0) 18-09-28 19
Viktigt om grafikprogrammering med Swing (1/4) Klassen Graphics är tillståndsstyrd (i motsats till tillståndslös). Tilldelar man en färg gäller den för alla anrop tills dess att en annan färg tilldelas. Detta är vanligt vid all form av grafikprogrammering. Detta är en av flera anledningar till att grafiska program som inte beter sig som förväntat är svåra att felsöka i. En annan anledning till detta är att man ofta måste förlita sig på visuell verifikation på att programmet fungerar. 18-09-28 20
Viktigt om grafikprogrammering med Swing (2/4) Den koordinat man anger när man ritar en figur kommer utgöra figurens övre vänstra hörn. Detta är ofta inte vad man vill, mittpunkten är ofta en bättre och mer intuitiv pivot-punkt. Kan lösas ganska lätt med transformationen: x' = x - (width/2) y' = y - (height/2) Så att man kan behålla mittpunkten som pivot-punkt i sin egen kod och bara omvandla till Swings förväntade format vid utritning av objektet. 18-09-28 21
Viktigt om grafikprogrammering med Swing (3/4) Koordinater och storlekar på figurer anges i heltal. Den minsta enhet i skärmrymden är en pixel. (Picture element) Vi kan därför inte rita en halv punkt på skärmen. Det är möjligt att använda interpolation och kantutjämning (antialiasing) för att approximera ritning av delpixlar men Graphics har inte stöd för det. (Det finns även en utbyggnad som heter Graphics2D som har stöd för det, titta i Turtle/World om ni vill se hur den klassen kan användas för tillgång till mer avancerade grafikprimitiver). 18-09-28 22
Viktigt om grafikprogrammering med Swing (4/4) För att skriva egen grafikkod ärver man från basklassen JPanel i Swing och skriver över (@Override) metoden paintcomponent(graphics g). Rutan måste sedan placeras i ett JFrame-objekt eller inuti ett annat JPanel-objekt. 18-09-28 23
Några frågor om grafikprogrammering? 18-09-28 24
Händelsestyrda program och användargränssnitt Händelsestyrda program med användargränssnitt är den typen av datorprogram som de flesta associerar med själva ordet "datorprogram". Terminologi: Gränssnitt heter interface på engelska och det har ingenting att göra med Javas interface. Jag ska försöka hålla mig till att säga gränssnitt men om jag råkar säga interface så får ni ropa! 18-09-28 25
Gemensamt för alla program ni har skrivit har varit att de har börjat exekvera en main-metod och sen har programmet körts uppifrån och ner. Ni har uttryckligen beskrivit för datorn vad som ska hända och i vilken ordning. Händelsestyrda program fungerar tvärtom. Ni lämnar över kontrollen över exekveringen till systemet och i gengäld lovar det att anropa ert program när en intressant händelse inträffar. Vilka händelser som är intressanta kan man styra i och med att man lämnar över kontrollen och även ändra dynamiskt som svar på andra händelser. 18-09-28 26
Lyssnare Ett objekt som får ta emot ett anrop till en speciell metod vid en händelse. Flera objekt kan lyssna på samma händelse. För att ett objekt ska bli en lyssnare måste den ofta registreras i objektet som upptäcker händelsen. Exempel i Swing: ActionListener 18-09-28 27
Viktiga klasser i Swing: JFrame - fönster JPanel - grafik och gruppering av komponenter Timer - generar periodvisa händelser JTextField - textruta med stöd för inmatning JButton - klickbar knapp GridLayout - Formattering som placerar komponenterna i ett rutnät FlowLayout - Formattering som placerar komponenterna rakt efter varandra 18-09-28 28
LIVE DEMO 18-09-28 29
Rekommenderad arbetsgång för bolluppgiften: - Skapa ett fönster och en egen implementering av JPanel genom arv. Se till att du kan rita en boll i rutan. - Skapa en boll-klass som representerar en boll. Se till att den kan flytta på sig och kollidera med kanter. - Lägg till ett antal bollar i programmet och styr deras uppdateringar via en timer. - Lägg till händelser vid krockar mellan bollar. Håll detta åtskilt från förflyttningar av bollar. - Gör användargränssnittsdelen som styr programmet. - Redovisa och bli godkänd. 18-09-28 31