Eigene Grafikklasse erstellen

Jetzt wollen wir eigene Grafikobjekte erstellen. Einen Pfeil, und dann Pfeile in verschiedenen Ausrichtungen. Um einen Pfeil zu zeichnen könnten wir wie folgt vorgehen:

package start;

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Pfeilbeispiel extends JFrame {

  public static void main(String[] args) {
    
  Pfeilbeispiel fenster = new Pfeilbeispiel();
  
  fenster.setVisible( true );
  fenster.setBounds( 0, 0, 900, 500 );
  fenster.setAlwaysOnTop( true );
  fenster.setDefaultCloseOperation( EXIT_ON_CLOSE );
  fenster.setLocationRelativeTo( null );

        int[] x = {50,50,90,90,150,90,90,50}; // 8 x-Koordinatenpunkte, Pixel horizontal von links
        int[] y = {55,85,85,110,70,30,55,55}; // 8 y-Koordinatenpunkte, Pixel vertikal von oben


  JPanel panel = new JPanel() {
    public void paint(Graphics g) {
      super.paint(g);
      g.setColor(Color.black);
      g.drawPolyline(x,y,8);
    }
  };

  panel.setBounds(0, 0, 1000, 500);
  panel.setBackground(Color.white);
  fenster.add(panel);
  
  }
}

Hier erstellen wir zwei int-Arrays und speichern darin die x/y Koordinaten (x für die horizontale, y für die vertikale. Immer in Pixel vom Fensterrand oben bzw. links). Ein Array ist ein Speicherbereich der mehrere Werte speichern kann.
Im JPanel zeichnen wir dann mit drawPolyline das Polygon. Die Anweisung erwartet in den ersten beiden Angaben jeweils ein Array für x und y. Die letzte Angabe beinhaltet die Punktanzahl die gesetzt wird.

Falls wir nun einen Pfeil zeichnen wollen, müssten wir jedesmal die Koordinaten direkt in den Quellcode kopieren, je nachdem in welche Richtung der Pfeil zeigen soll. Dies wollen wir vereinfachen.

Wir wollen nun folgendes erreichen:
Wir wollen eine Klasse schreiben die alle Pfeile beinhaltet und von der wir schließlich einfach mit:
Pfeil linkerPfeil = new Pfeil();
ein Objekt bilden können, auf das wir dann einfach in der paint-Methode zugreifen können. z.B. so:
g.drawPolyline( linkerPfeil.getX, linkerPfeil.getY, linkerPfeil.getPunkte );

oder noch besser wir wollen gar nicht erst die paint-Methode überschreiben sondern das Objekt soll uns ein JPanel liefern das wir nur noch auf unser Fenster setzen:
Pfeile pfeil = new Pfeile();
panel.add(pfeil.LINKS);
panel.add(pfeil.RECHTS);

Ausserdem sollten wir noch die Möglichkeit haben auch noch die Farbe und Position des Pfeils bestimmen zu können, also z. B. pfeil.LINKS(Position, Farbe).

Zunächst wollen wir die einfachste Lösung umsetzen und dann die Klasse nach und nach  komplexer werden lassen.

Wir erstellen eine neue Klasse mit Namen Pfeilhelfer und setzen folgenden Quellcode ein:

package start;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.JPanel;

public class Pfeilhelfer {
  
 private int[] x = {50,50,90,90,150,90,90,50};
 private int[] y = {55,85,85,110,70,30,55,55};

  public Pfeilhelfer() {}
  
  public int[] getXArr() {
    return this.x;	
  }
  
  public int[] getYArr() {
    return this.y;	
  }
  
}

Die Klasse enthält die beiden Arrays für die x und y Koordinaten sowie einen Standard-Konstruktor und zwei sogenannte Getter die gewährleisten das die Daten gekapselt sind. Was das genau bedeutet erkennen wir wenn wir die Einstiegsmethode schreiben.

Da bei größeren Programmen für Objekte meist eigene Dateien angelegt werden, wollen wir auch so vorgehen und die Einstiegsmethode zusammen mit dem Frame in eine eigene Datei schreiben.
Wir legen eine neue Klasse mit Namen Tafel2 an und geben folgenden Code ein:

package start;

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;


public class Tafel2 extends JFrame {

  public static void main(String[] args) {

    Tafel2 zeichnen = new Tafel2();
    zeichnen.setDefaultCloseOperation( EXIT_ON_CLOSE );
    zeichnen.setBounds( 0, 0, 500, 500 );
    zeichnen.setLocationRelativeTo( null );
    zeichnen.setAlwaysOnTop( true );
    zeichnen.setLayout(null);
    zeichnen.getContentPane().setBackground(Color.white);;
    zeichnen.setTitle("Zeichnen in Java");	
    zeichnen.setVisible( true ); 

    Pfeilhelfer pfeil = new Pfeilhelfer();
    
    JPanel panel = new JPanel() {
      public void paint(Graphics g) {	
        super.paint(g);
        g.drawPolyline(pfeil.getXArr(),pfeil.getYArr(),8);
      }
    };
    panel.setBounds(100, 100, 200, 150);
    panel.setBackground(Color.WHITE);
    zeichnen.add(panel);
  } 		
}

Wir sehen das wir nun nach dem bilden des Pfeilhelferobjekts, anschließend in der paint-Methode bequem auf das X- und Y-Array des Pfeils zugreifen können. Die Methode liefert einen Rückgabewert den wir mit return festgelegt haben. Wenn wir in der Pfeilhelfer-Klasse das private beim initialisieren der X- und Y-Arrays weglassen, können wir beim Methodenaufruf von pfeil in der Liste sowohl das X wie auch das Y-Array direkt ohne den Getter einlesen. Dies ist jedoch nicht ratsam da wir nicht wollen das jemand den Pfeil verändert. Schließlich könnte man dann auch schreiben:
pfeil.x[0] = 700;
das die erste X-Koordinate auf 700 setzen würde, was wir definitiv nicht wollen. Die Koordinaten sind ja fehlerfrei und sollen so bleiben. Das Schlüsselwort private versetzt uns also in die Lage, festzulegen welche Daten nur zum Abruf bereitstehen und bei welchen es Sinn macht sie zu verändern. Würden wir wollen das man die Koordinaten verändern kann, würden wir noch eine Setter-Methode hinzufügen.
Als nächstes können wir natürlich für jeden Pfeil eine extra Klasse schreiben. Aber wir können natürlich auch alle in eine schreiben. Wir haben dann praktisch eine „Pfeilfabrik“ mit der wir unterschiedliche Pfeile herstellen können:

Pfeilfabrik.java

package start;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.JPanel;

public class Pfeilfabrik {
  
  private int[] rx = {50,50,90,90,150,90,90,50};
  private int[] ry = {55,85,85,110,70,30,55,55};
  
  private int [] lx = {150,150,110,110,50,110,110,150};
  private int [] ly = {55,85,85,110,70,30,55,55};
  
  private int anzahl = 8;
  
  public Pfeilfabrik() {}
  
  public int[] rechtsX() {
    return this.rx;	
  }
  
  public int[] rechtsY() {
    return this.ry;	
  }
  public int[] linksX() {
    return this.lx;	
  }
  
  public int[] linksY() {
    return this.ly;	
  }
  
  public int getAnzahl() {
    return this.anzahl;
  }
  
}

Im Endeffekt nicht viel komplexer als die vorherige Klasse, nur das es jetzt die Daten für zwei verschiedene Pfeile enthält und dementsprechende getter. Natürlich müssen wir auch die Tafel2.java anpassen:

package start;

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Tafel2 extends JFrame {

  public static void main(String[] args) {

    Tafel2 zeichnen = new Tafel2();
    zeichnen.setDefaultCloseOperation( EXIT_ON_CLOSE );
    zeichnen.setBounds( 0, 0, 500, 500 );
    zeichnen.setLocationRelativeTo( null );
    zeichnen.setAlwaysOnTop( true );
    zeichnen.setLayout(null);
    zeichnen.getContentPane().setBackground(Color.white);
    zeichnen.setTitle("Zeichnen in Java");	
    zeichnen.setVisible( true ); 

    Pfeilfabrik pfeil = new Pfeilfabrik();
    
    JPanel panel = new JPanel() {
      public void paint(Graphics g) {	
        super.paint(g);
        g.drawPolyline(pfeil.linksX(),pfeil.linksY(),pfeil.getAnzahl());
      }
    };
    panel.setBounds(100, 100, 200, 150);
    panel.setBackground(Color.WHITE);
    zeichnen.add(panel);
  } 		
}

Je nachdem ob wir nun pfeil.linksX oder pfeil.rechtsX eingeben gibt das Objekt pfeil andere Koordinaten zurück.

Anspruchsvoller wird das ganze wenn wir zu Beginn angekündigt, unsere Pfeilfabrik gleich ein JPanel zurückliefern lassen:

Klasse Pfeilfabrik:

package start;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.JPanel;

public class Pfeilfabrik extends JPanel {
  
int [] rx = {50,50,90,90,150,90,90,50};
int [] ry = {55,85,85,110,70,30,55,55};

int [] lx = {150,150,110,110,50,110,110,150};
int [] ly = {55,85,85,110,70,30,55,55};

int [] ox = {80,110,110,135,95,55,80,80}; 
int [] oy = {130,130,90,90,30,90,90,130}; 

int [] ux = {80,110,110,135,95,55,80,80};
int [] uy = {30,30,70,70,130,70,70,30};

int[] x;
int[] y;

int anzahlpunkte = 8;

Point p;

Color farbe;

public Pfeilfabrik () {
  this.farbe = Color.BLACK;
}

public Pfeilfabrik (Color c) {
  this.farbe = c;
}



public Pfeilfabrik RECHTS(Point p) {
  Pfeilfabrik pfeil = new Pfeile(this.farbe);
  pfeil.x = this.rx;
  pfeil.y = this.ry;
  pfeil.p = p;
  return pfeil;
}

public Pfeilfabrik LINKS(Point p) {
  Pfeilfabrik pfeil = new Pfeile(this.farbe);
  pfeil.x = this.lx;
  pfeil.y = this.ly;
  pfeil.p = p;
  return pfeil;
}

public Pfeilfabrik OBEN(Point p) {
  Pfeilfabrik pfeil = new Pfeile(this.farbe);
  pfeil.x = this.ox;
  pfeil.y = this.oy;
  pfeil.p = p;
  return pfeil;
}

public Pfeilfabrik UNTEN(Point p) {
  Pfeilfabrik pfeil = new Pfeile(this.farbe);
  pfeil.x = this.ux;
  pfeil.y = this.uy;
  pfeil.p = p;
  return pfeil;
}


public void paint(Graphics g) {
  super.paint(g);
  this.setBounds(this.p.x, this.p.y, 200, 150);
  g.setColor(this.farbe);
  g.drawPolyline(x,y,anzahlpunkte);	
}


}

extends JPanel bewirkt das unsere Klasse alle Eigenschaften und Methoden von JPanel erbt. Es ist quasi schonmal ein JPanel plus die Eigenschaften und Methoden die wir eben hinzufügen.
Was den Konstruktor betrifft, verwenden wir hier sogenannte überladene Konstruktoren. Da wir schon bei der Erstellung des Objekts die Farbe aller späteren Pfeile angeben wollen, übergeben wir dem Konstruktor eine Farbe (Zeile 36). Für den Fall das wir keine Farbe übergeben, ruft der Compiler den Standardkonstruktor auf in dem wir schwarz als Standardfarbe definieren ( Zeile 32).

Für jeden Pfeil schreiben wir eine Methode welcher wir ein Point-Objekt übergeben das die Koordinaten der Pfeilposition enthält. Wir rufen den zweiten Konstruktor auf und erstellen ein neues Objekt dessen x und y Variablen wir die entsprechenden Arrays des Pfeils zuweisen. Schließlich geben wir dieses Objekt zurück. Da unser Objekt die Klasse JPanel geerbt hat, erhalten wir ein JPanel in dessen paint-Methode unser Pfeil gezeichnet wird (Zeile 75).

Klasse Pfeilbeispiel:

package start;

import java.awt.Color;
import java.awt.Point;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Pfeilbeispiel extends JFrame {

 public static void main(String[] args) {
    
 Pfeilbeispiel fenster = new Pfeilbeispiel();
  
 fenster.setVisible( true );
 fenster.setBounds( 0, 0, 900, 500 );
 fenster.setAlwaysOnTop( true );
 fenster.setDefaultCloseOperation( EXIT_ON_CLOSE );
 fenster.setLocationRelativeTo( null );
  
 Color c;
 c = new Color(100,200,50); 
 Point p = new Point(500,100);
  
 JPanel panel = new JPanel();
 panel.setBounds(0, 0, 1000, 500);
 panel.setBackground(Color.DARK_GRAY);

 fenster.add(panel);	

 Point p1 = new Point(0,0);

 Point p2 = new Point(200,0);

 Pfeilfabrik pfeil = new Pfeilfabrik(Color.BLUE);

 panel.add(pfeil.LINKS(p1));
 panel.add(pfeil.RECHTS(p2));
 panel.add(pfeil.OBEN(new Point(0,150)));
 panel.add(pfeil.UNTEN(new Point(200,150)));
 }
}

Hier werden am Ende unsere vier Pfeile gebildet. In den letzten beiden Zeilen übergeben wir den Point direkt.

Jetzt habt ihr euch aber einen Kaffee verdient. Genießt ihn.

Als nächstes wollen wir uns dann etwas mit Buttons und deren Überwachung beschäftigen.

Zusammenfassung

  • Ein Array enthält mehrere Variablen gleichen Typs
  • mit dem Befehl return können wir festlegen welches Ergebnis eine Methode zurückliefert
  • Ein Point-Objekt enthält zwei Koordinaten: x und y.