Universität Hamburg - Fachbereiche - Fachbereich Mathematik

Java-Kurs (9)

WiSe 01/02

Bodo Werner

Zurück zum Inhaltsverzeichnis.

9. Einführung in Grafik

Frame, implements (Interface), ActionListener, paint(), Graphics, setSize(), setVisible(), setLocation(), setColor(), Color, blue, red, yellow, drawLine(), Panel, Button, TextField, Label, setLayout(), GridLayout(), add(), setForeground(), setBackground(), addActionListener(), actionPerformed(), ActionEvent, getActionCommand(), getText(), repaint()

Ziel dieses Dokumentes ist es, die LeserIn in Stand zu setzen, eigene Grafiken in Anwendungsprogrammen (nicht in Applets) zu erzeugen. Es ist nicht notwendig, diesen Einführung zu verstehen, um innerhalb der Numerischen Mathematik die Grafik-Black-Box benutzen zu kö:nnen. Dennoch ist das erste Beispiel so elemntar angelegt, dass es von allen verstanden werden sollte. Dagegen richtet sich das zweite Beispiel vor allem an diejenigen, die "hinter die Kulissen blicken" wollen.
Die von Java zu Grafikzwecken zur Verfügung gestellten öffentlichen Klassen (auch Interfaces) mit ihren Daten und Methoden sind ausgesprochen mächtig. Sie werden durch import java.awt.* importiert (AWT=Abstract Windowing Toolkit). Um diese geschickt anzuwenden, muss man möglichst viele der Bezeichner dieser bereitgestellten Klassen, Interfaces, Methoden und Daten mitsamt ihrer Bedeutung genau kennen. Um deren Funktionen zu erproben, bleibt einem häufig nichts anderes übrig, als zu experimentieren. Ich werde im folgenden alle die Bezeichner von Klassen, Methoden und Attributen blau kennzeichnen, die ich den Java-Paketen entnommen habe.

Die hier verwendete Java-Basisklasse heißt Frame. Sie ermöglicht mit ihrem Konstruktor und ihren Methoden die Erzeugung und Ausgestaltung eines Grafikfensters.

Ich werde im folgenden erklären, wie man ein Grafikfenster, in dem eine Kurve gezeichnet werden soll, konstruiert und ausgestaltet. Das einfachste Beispiel wird eine Unterklasse von Frame namens Grafik0 sein. Hier werden Sie lernen, wie man ein Grafikfenster einer bestimmten Höhe und Breite erstellt (setSize()), dessen Hintergrundfarbe festlegt (setBackground()) und eine Kurve in einer bestimmten Farbe zeichnet (paint()), indem winzige Geradenstücke aneinandergereiht werden (drawLine()). In einem zweiten Beispiel (Grafik1) wird die Kurve von einem Parameter dt abhängen, den man interaktiv mit Hilfe einer Schaltfläche, die Teil des Grafikfensters ist, ändern und anschließend eine neue Zeichnung durch einen Mausklick veranlassen kann. Die Java-Hilfsmittel sind hier schon komplexer, insbesondere muss ein Interface namens ActionListener implementiert werden, das mit seinen Methoden gestattet, auf Mausklicks zu reagieren.

Das Herz aller Grafikklassen ist die Methode paint(), die die Struktur


public void paint(Graphics g){..........}
besitzt und deren Block stets ausgeführt wird, wenn ein Objekt einer Unterklasse von Frame in einem main()-Block einer Applikationsklasse initialisiert wird.

Dabei werden i.a. Methoden der Klasse Graphics bzw. dessen Objekte wie die selbsterklärenden Methoden setColor() oder drawLine() benutzt werden. Das konkret "gezeichnete" Grafikobjekt wird oben mit g bezeichnet und ist ein Objekt der Klasse Graphics. Der Farbigkeit dienen "Farbkonstenten" der Klasse Color, wie z.B. Color.blue.

9.1 Die Unterklasse Grafik0 von Frame

Die hier beschriebene Klasse kann im Quellcode ( Grafik0.java) in üblicher Weise heruntergeladen werden. Dies sollte die LeserIn tun, die Klasse sodann kompilieren und ausführen.

Unsere Klasse Grafik0 (s.u.) wird durch


public class Grafik0 extends Frame
aus der Klasse Frame abgeleitet, d.h. ist eine Unterklasse von Frame. So würde mittels
 

Frame w = new Frame("Leeres Grafikfenster");
w.setsize(400,300); w.visible(true); 
innerhalb der main()-Methode einer Applikationsklasse ein leeres Fenster der Größe 400x300 Pixel mit einer Titelleiste, die den Text Leeres Grafikfenster enthält, erzeugt. Die Klasse Frame hat also einen Konstruktor, der einen String übergibt, der den Titel der Titelleiste definiert. setSize() und visible() sind offensichtlich Methoden der Klasse Frame bzw. seiner Objekte (wie hier w).

In unserer Klasse Grafik0 wird der Applikationsblock


   static public void main( String [] args ){
      Grafik0 obj=new Grafik0(
        //Konstruktor uebergibt String:
        "x=2.1*cos(2pi*t)+0.1*sin(2pi*10*t);"+ 
        "y=2.1*sin(2pi*t)-0.1*cos(2pi*10*t);"+ 
        "0<=t<=1, Schrittweite dt.  B.W. 19.9.00");
    }//Ende main()
die Erzeugung eines Fensters mit Zeichnung auslösen. Die Titelleiste zeigt die Formel der parametrisierten Kurve (was sich dahinter verbirgt, wird in der Vorlesung kurz erklärt).

Unsere Klasse Grafik0 sieht wie folgt aus:


import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class Grafik0 extends Frame{
     //DATEN:  
     double dt=0.001; //Zeichenschrittweite
     int H=700, B=800; //Fensterhoehe, -Breite
    //Ende DATEN
    //KONSTRUKTOR:
    public Grafik0 (String title ) {
      super( title ); // Fenstertitel uebergeben
      // Schliessbarkeit des Fensters ermoeglichen:
      addWindowListener(
            new WindowAdapter() {
	       public void windowClosing( WindowEvent w ) {
	       setVisible(false); dispose(); System.exit(0);
	       }//Ende windowClosing()
	    }//Ende des Konstruktors WindowAdapter()
          );//Ende addWindowListener()
      //Hintergrundfarbe des Fensters setzen:
      setBackground(Color.lightGray);
      //Groesse des Fensters setzen: 
      setSize(B,H);
      //Fenster in die linke obere Ecke positionieren:
      setLocation( 0, 0 );
      //Fenster sichtbar machen:
      setVisible( true );
    }//Ende KONSTRUKTOR Grafik0

    //METHODEN:
    public double[] sincos(double t){
	double[] x=new double[2];
        //Nur hier steckt Mathematik:
	x[0]=2.1*Math.cos(2*Math.PI*t)+0.1*Math.sin(2*Math.PI*20*t);
	x[1]=2.1*Math.sin(2*Math.PI*t)-0.1*Math.cos(2*Math.PI*20*t);
	return x;
    }//Ende sincos()
    public short[] skalierung(double x, double y){
        double xMax=3, xMin=-xMax, yMax=3, yMin=-yMax;
        short[] i = new short[2];
	i[0]=(short) (B/10 +B*0.9*(x-xMin)/(xMax-xMin));
        i[1]=(short) (H/10 +H*0.9*(y-yMin)/(yMax-yMin));
        return i;
    }//Ende skalierung()
    //paint() legt fest, was gezeichnet wird:
    public void paint (Graphics g) {	
	//Farbe der auszugebenden Grafik setzen
	g.setColor(Color.red);
	double t=0.0;
	double[] x=sincos(t);
        short[] i1=new short[2];
	short[] i0=skalierung(x[0],x[1]);
	// Linien zeichnen
	while (t<=1) {
	    t+=dt; 
            x=sincos(t);
            i1=skalierung(x[0],x[1]);
	    g.drawLine(i0[0],i0[1],i1[0],i1[1]);
	    i0[0]=i1[0];i0[1]=i1[1];
	}//Ende while
    }//Ende paint()

    static public void main( String [] args ){
      Grafik0 obj=new Grafik0(
        "x=2.1*cos(2pi*t)+0.1*sin(2pi*20*t);"+ 
        "y=2.1*sin(2pi*t)-0.1*cos(2pi*20*t);"+ 
        "0<=t<=1,  B.W. 11.8.01");
    }//Ende main()
} // Ende class Grafik0
Die entscheidende Zeile ist die Zeile in der Methode main(), in der ein Objekt namens obj der Klasse Grafik0 deklariert und initialisiert wird. Da Grafik0 eine Unterklasse von Frame ist, wird ein Grafikfenster der im Konstruktor vorgesehenen Größe (setSize()) und Hintergrundfarbe (setBackground()) und Position (setLocation()) geöffnet und der paint()-Block abgearbeitet, der eine durch t parametrisierte Kurve mit Hilfe der Methode drawLine() zeichnet. Etwas kryptisch ist der paint()-Parameter g als Objekt der Klasse Graphics, welche alle grafischen Methoden wie drawLine(), setColor() definiert. Die 4 Parameter von drawLine() sind jeweils 2 Pixel-Paare, die die Koordinaten im Grafikfenster bestimmen, wobei der Ursprung die obere linke Ecke ist und die Pixel-Ordinate nach unten, die Pixel-Abszisse nach rechts zeigt. Die Methode skalierung() dient der Umrechnung von den mathematischen Koordinaten in die Pixel-Koordinaten. Die Grafikfenstergröße wird ebenfalls in Pixel gemessen, wobei die Auflösung des Bildschirms eingeht.

Ich empfehle, einfach einige Experimente mit diesem Programm durchzuführen, indem kleine Änderungen vorgenommen werden.

9.2 Die Unterklasse Grafik1 von Frame

Die hier beschriebene Klasse kann im Quellcode ( Grafik1.java) in üblicher Weise heruntergeladen werden. Dies sollte die LeserIn tun, die Klasse sodann kompilieren und ausführen.

Unsere Klasse Grafik1 (s.u.) wird durch


public class Grafik1 extends Frame
implements 
  ActionListener{
  //......................  }
aus der Klasse Frame abgeleitet und implementiert ein Interface (das ähnlich wie eine Oberklasse Daten und Methoden vererbt) namens ActionListener. Dieses Interface ermöglicht es, mit seinen Methoden (u.a. actionPerformed()) auf Events ("Aktionen") wie einen Mausklick oder das Verändern eines Textfeldes im Grafikfenster zu reagieren.

Grafikfenster werden i.a. mit verschiedenen Arten von Schaltflächen versehen. Die Festlegung deren Gestalt ist eine Layoutaufgabe. Auch hier gibt es mächtige Java-Klassen und -Methoden, von denen ich nur Panel, Button, TextField, Label, setLayout(), GridLayout(), setForeground(), setBackground() erwähne. Diese werden im folgenden genauso wenig näher erläutert wie die Methode addWindowListener(), die das Schließen des Fensters ermöglicht. Bei eigenen Versuchen orientiere man sich einfach an den folgenden Beispielen.

Unsere Klasse Grafik1 sieht wie folgt aus:


import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class Grafik1 extends Frame 
    implements ActionListener{
      
    //DATEN: 
     double dt=0.3; //Zeichenschrittweite
    
    /*Der Inhalt des folgenden Textfeldes kann interaktiv veraendert
      werden - s. Methode actionPerformed()*/
    TextField tf1 =new TextField(""+dt);//Ein Textfeld wird konstruiert
    //Ende DATEN
    //KONSTRUKTOR:
    public Grafik1 (String title ) {
      super( title ); //Konstruktor von Frame
      
      //Schliessbarkeit des Fensters ermoeglichen:
      addWindowListener(
        new WindowAdapter() {
	    public void windowClosing( WindowEvent w ) {
	       setVisible(false); dispose(); System.exit(0);
	    }//Ende windowClosing()
	}//Ende des Konstruktors WindowAdapter()
      );//Ende addWindowListener()
       
      /*ein Panel besteht aus Labels, Textfeldern, Buttons.
       np1 wird am unteren Rand ("South") angebracht, enthält die
       Beschriftung "Neu zeichnen" uns soll auf einen Mausklick
       reagieren.
       np2 besteht aus einem Label mit Beschriftung "dt" und einem
       Textfield, das den Zahlenwert von dt enthält und das interaktiv
       verändert  werden kann. */
      Panel np1 = new Panel(); 
      Panel np2 = new Panel();
      //Ein Button wird konstruiert
      Button rc=new Button("Neu zeichnen");
      Label l1=new Label("dt=",Label.CENTER); 
      
      np1.setLayout(new GridLayout(1,1));//Eine Zeile, 1 Spalte)
      np1.setForeground(Color.red);  
      np1.add(rc); 
      add("South",np1);
      np2.setLayout(new GridLayout(1,1));
      np2.setBackground(Color.yellow); 
      add("North",np2); 
      np2.add(l1); 
      np2.add(tf1); 
      	
      rc.addActionListener(this); 
      /*addActionListener steht in der Klasse Button
      zur Verfuegung. Ihr Argument ist vom Typ ActionListener 
      - einem Interface, das von unserer Klasse Grafik1 
      implementiert wird und die Methode actionPerformed()
      zur Verfuegung stellt, die unten ueberlagert wird.  
      Durch diese Mathode reagiert das Programm auf das 
      Druecken des Buttons "Neu Zeichnen" */
      
      //Hintergrundfarbe des Fensters setzen:
      setBackground(Color.lightGray);
      //Groesse des Fensters setzen: 
      setSize(800,700);
      // Fenster in die linke obere Ecke positionieren:
      setLocation( 0, 0 );
      // Fenster sichtbar machen:
      setVisible( true );
    }//Ende KONSTRUKTOR Grafik1

    //METHODEN:
    public double[] sincos(double t){
	double[] x=new double[2];
        //Nur hier steckt Mathematik:
	x[0]=2.1*Math.cos(2*Math.PI*t)+
           0.4*Math.sin(2*Math.PI*5.03*t);
	x[1]=1.9*Math.sin(2*Math.PI*t)
           -0.6*Math.cos(2*Math.PI*5.03*t);
	return x;
    }//Ende sincos()

    public short[] skalierung(double x, double y){
        double xMax=3, xMin=-xMax, yMax=3, yMin=-yMax;
        short[] i = new short[2];
	i[0]=(short) (120 +600*(x-xMin)/(xMax-xMin));
        i[1]=(short) ( 50 +600*(y-yMin)/(yMax-yMin));
        return i;
    }//Ende skalierung()

    //paint() legt fest, was gezeichnet wird:
    public void paint (Graphics g) {	
	//paint() ist eine Methode der Oberklasse Frame:
	super.paint( g );
	// Farbe der auszugebenden Grafik setzen:
	g.setColor(Color.red);
	double t=0.0;
	double[] x=sincos(t);
        short[] i1=new short[2];
	short[] i0=skalierung(x[0],x[1]);
	// Linien zeichnen:
	while (t<=100) {
	    t+=dt; 
            x=sincos(t);
            i1=skalierung(x[0],x[1]);
	    g.drawLine(i0[0],i0[1],i1[0],i1[1]);
	    i0[0]=i1[0];i0[1]=i1[1];
	}//Ende while
    }//Ende paint()

    public void actionPerformed(ActionEvent e){
	String cmd;
	cmd=e.getActionCommand();
	if (cmd.equals("Neu zeichnen")){
	    dt=(new Double(tf1.getText())).doubleValue();
	    repaint(); //Methode von Frame
	}//Ende if
    }//Ende actionPerformed()
   
    static public void main( String [] args ){
      Grafik1 obj=new Grafik1(
        "x=2.1*cos(2pi*t)+0.4*sin(2pi*5.03*t);"+ 
        "y=1.9*sin(2pi*t)-0.6*cos(2pi*5.03*t);"+ 
        "0<=t<=100, Schrittweite dt.  B.W. 19.9.00");
    }//Ende main()

} // Ende class Grafik1
Ich werde in der Vorlesung zu dieser Klasse weitere Erklärungen geben. Diese werden verständlicher, wenn man das Programm ausführt.

Die Methode actionPerformed() reagiert auf einen Mausklick auf den mit "Neu Zeichnen" beschrifteten Button, indem mittels Aufruf von repaint() der Zeichenvorgang wiederholt wird, allerdings mit dem aktuellen, interaktiv veränderbaren Wert des Parameters dt, der mittels Methode getText() "abgelesen" und mittels der Methode doubleValue() der Klasse Double, dessen Konstruktor den String des Textfeldes tf übergibt, in eine double-Zahl verwandelt wird.


Weiter mit 10. Rekursionen