KTH 2D1310: Programmeringsteknik
2D1311: Programmeringsteknik med PBL
Nada

Grafikanpassning av program

Nedan beskrivs en generell metod som underlättar när man vill grafikanpassa ett textbaserat program. En förutsättning för att det hela ska fungera är att det textbaserade programmet är välstrukturet och därmed lätt att bygga ut. Det bör inte finnas några klassmetoder utöver main().

Först följer en kort introduktion, därefter en punktlista hur man steg för steg omvandlar ett textbaserat program till ett grafikbaserat.

Kort introduktion

Ett textbaserat program körs från terminalfönstret med anropet java KlassNamn. Detta kommando anropar (klass)metoden main(). main() körs rad för rad, och andra metoder anropas i sin tur. När sista raden i main() har exekverats är programmet klart. Endast de metoder som anropas från main() - eller från metoder anropade av main() - kommer att köras.

Ett grafikbaserat program är uppbyggt på ett annat sätt. Programmet består av två faser.

  1. Initieringen. Skapar och ger värden till variabler, speciellt alla de grafiska komponenter (t.ex knappar, textfält) som ingår i programmet.
  2. Vänta / ta hand om händelser. När något händer (t.ex någon trycker på en knapp) genererar detta ett beteende hos programmet, en eller flera metoder anropas.
Skillnaden mot det textbaserade programmet är att det inte finns någon koppling mellan dessa delar. Den metod som initierar alla variabler körs rad för rad, men när sista raden har utförts tar inte programmet slut, utan istället lägger det sig och väntar. När något händer anropas en händelsemetod, som avgör hur programmet ska reagera. Det finns inga anrop från intieringsmetoden till händelsemetoden.

Java innehåller ett antal klasser som är grafiska fönster. För att göra ett grafikbaserat program låter du den egna klassen ärva (extends) från en sådan klass. Två sådana klasser är

Java har också klasser som är lyssnare, dvs som kan ligga och vänta på att något ska hända. För att låta den egna klassen lyssna utvidgar (implements) du klassen med en lyssnare. Det innebär att klassen får tillgång till lyssnarens egenskaper, men samtidigt lovar att innehålla en eller flera metoder. ActionListener lyssnar på grafiska komponenter (t.ex knappar), och kräver (instans)metoden actionPerformed(). Denna metod behöver inte göra någonting, men måste finnas så att lyssnaren har något att anropa när det händer något.

Steg för steg - hur du omvandlar

Som exempel på ett program i text- och grafikversion finns en enkel Kalkylator. I anslutning till flera av punkterna nedan finns hänvisningar till de olika versionerna - T(ext), F(rame) och A(pplet) samt H(TML) - tillsammans med ett radnummer.

Om du följer anvisningarna ska programmet gå att kompilera efter varje steg. Gör det!

  1. Tänk efter och rita upp hur du vill att programmet ska se ut. Markera av vilken klass varje grafikkomponent är. Försök även gruppera de olika komponenterna. Varje grupp kommer senare bli en instans av klassen Panel. skiss
    indelning

  2. Ta reda på vilken klass som är huvudklass. Huvudklassen är den klass som innehåller de metoder som gör/styr allt. I till exempel ett spel skulle denna klass utgöra/beskriva det fysiska spelet. (Detta behöver inte vara den klass som innehåller main().)

  3. Om du vill ha en Applet, skapa en ".html"-fil och skriv HTML-kod. Det som måste vara med är
     <HTML><BODY>
     <APPLET CODE="KlassNamn.class" 
             WIDTH=100 HEIGHT=100>
     </APPLET>
     </BODY></HTML>
     
    H
  4. Se till att få tillgång till de grafiska komponenterna:
    • import java.awt.*; // Standardkomp.: Frame, Button, ...
    • import javax.swing.*; // Swingkomp.:JFrame,JButton, ...


    A:2
    F:1

  5. Låt huvudklassen ärva, antingen Applet
    • import java.applet.*;
    • public class KlassNamn extends Applet ...


    A:1
    A:6

    eller Frame
    • public class KlassNamn extends Frame ...



    F:5

  6. Lägg till (instans)metoden init() i huvudklassen. Tills vidare behöver denna inte göra någonting. A:45
    F:61

  7. Om du har en Applet, ta bort main() helt eftersom denna inte används. A:8
    Om du har en Frame, lägg till (klass)metoden main() i huvudklassen. Låt main() skapa en instans av huvudklassen och anropa init(), packa ihop allt samt ta fram fönstret:
    • KlassNamn programmet = new KlassNamn();
    • programmet.init();
    • programmet.pack(); // kommer från klassen Frame
    • programmet.show(); // kommer från klassen Frame

    F:7


    F:9-15
    Om du vill att fönstret ska gå att stänga kan du lägga till följande magiska kod:
    • import java.awt.event.*;
     programmet.addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
             System.exit(0);
         }});
     


    F:2

    F:17-22
  8. Lägg till grafikkomponenter till this (ditt Applet/Frame-objekt):
    • Låt de komponenter som andra metoder (än init()) - t.ex en lyssnare - skall ha tillgång till bli instansvariabler.
    • Instansiera och lägg till respektive komponent, t ex:
      Button startknapp = new Button("Starta");
      add(startknapp); // samma som this.add(startknapp)


    A:18-21
    F:34-37

    A:53+62
    F:70+79

  9. Gör allt synligt:
    • setVisible(true); // samma som this.setVisible(true)


    A:107, F:124

  10. Prova att ändra layouten:
    • Försök gruppera komponenterna. Varje grupp blir en Panel.
    • Ändra så komponenterna läggs till i rätt panel.
    • Bestäm LayoutManagers för hela programmet samt varje panel.




    A:54, F:63

  11. Vänta med att lägga ned mer energi på utseendet till dess att programmet fungerar som det ska.

  12. Utvidga huvudklassen till att vara en ActionListener
    • import java.awt.event.*;
    • public class KlassNamn extends ... implements ActionListener ...
    • Lägg till (instans)metoden actionPerformed(...). Kolla hjälpen (Java API) för ActionListener om du inte vet hur metodhuvudet till actionPerformed() ska se ut!


    A:3, F:2
    A:6, F:5

    A:114, F:131

  13. Se till att de grafikkomponenter som ska kunna känna av en händelse får instansen av huvudklassen (this) som ActionListener:
    • (T ex) knapp1.addActionListener(this);



    A:82, F:99

  14. Lägg in spårutskrifter i actionPerformed(). Nedan är exempel på hur ActionEvent-objektet "e" som Java skickat med till actionPerformed() utnyttjas för att få reda på vilken "action" som inträffade.
    • (T ex) System.out.println(e.getActionCommand());
    • (T ex) label1.setText(e.getActionCommand());

  15. Modifiera eventuellt vilken "action" en speciell komponent ger upphov till:
    • (T ex) button1.setActionCommand("AlltOk");


    A:60, F:77

  16. Grafikprototypen är klar! Bygg vidare genom att lägga till ytterligare komponenter och metoder som anropas i metoden actionPerformed() till dess du är nöjd. Följande punkter ger tips om fortsatt anpassning.

  17. Separera alla uppmaningar till användaren från efterföljande inläsning. Om du t ex tidigare haft en metod liknande ställFrågaOchLäsSvar() bör du efteråt endast ha ställFråga(). Svaret bestämmer användaren själv när och om hon vill ge och ger det då genom att fylla i grafiska textfält, trycka på knappar etc. vilket gör att metoden actionPerformed() anropas. actionPerformed() har till uppgift att ta hand om svaret.

  18. Metoder som tidigare var deklarerade med throws IOException ska vara utan detta om metoden inte längre innehåller några readLine().

  19. Se till att programmet vid uppstart endast kör "ett varv" av en tidigare eventuell huvudslinga. Observera att det sista som ska hända är att användaren ska uppmanas göra något (sedan är det användarens tur att med någon "action" få programmet att fortsätta).

  20. Se till att källkoden i actionPerformed() endast gör saker motsvarande ett varv ur en tidigare eventuell huvudslinga. Precis som vid uppstarten ska det sista vara att uppmana användaren att göra något (igen). Försök hålla källkoden i actionPerformed() lättläst genom att utnyttja metodanrop till andra metoder.

  21. Om din actionPerformed() ska hantera flera olika "actions" från användaren utnyttjar du ActionEvent-objektet "e" enligt exemplen nedan.
    • genom att kolla på vilket kommando som inträffade:
      if (e.getActionCommand().equals("AlltOk")) ...
    • genom att kolla på vilken grafisk komponent som utnyttjades av användaren:
      if (e.getSource() == button1) ...
      Observera att variabeln button1 då måste finnas så att actionPerformed() kan komma åt den (d v s button1 ska vara en instansvariabel).




    A:118, F:135


^ Upp till kurssidan.


Sidansvarig: <panda@nada.kth.se>
Senast ändrad 8 september 2002
Tekniskt stöd: <webmaster@nada.kth.se>