# Zeichnen verschiedener geom. Figuren in einem JPanel



## winterwanderer (13. Okt 2008)

Hallo, 
ich bin dabei ein 'Zeichnungsfenster' zu entwickeln, auf das verschiedene geometrische Figuren gezeichnet werden können (Polygone, Punkte, Kurven etc.)
Ich habe nun eine allgemeine Frage zur Entwicklung dieses Fensters.  
Ist es sinvoll, diese verschiedenen Figuren alle in der der paint-Methode des Fensters zu zeichnen?
Eine Idee wäre, alle zu zeichnenden unterschiedlichen Figuren in verschiedenen Listen zu speichern (z.b. Polygonliste, Punkteliste etc.) und dann in der paint-Methode alle Listen abzufragen und mit der entsprechenden Methode zu zeichnen. 
Besserer objektorientierter Stil scheint es mir allerdings, die einzelnen Figuren in eigenen Klassen von eigenen Methoden zeichnen zu lassen und dann die Komponenten mit add() auf das Fenster zu bringen. Ich müsste dann aber wohl den Hintergrund der einzelnen Komponenten stets transparent setzen und habe gelesen, dass dies oftmals Permormance Probleme nach sich zieht.
Ich bin noch ein ziemlicher Programmieranfänger und wäre für einen Tipp sehr dankbar.

Viele Grüße
winterwanderer


----------



## Marco13 (13. Okt 2008)

Ja, da gibt's verschiedene Möglichkeiten... Alles folgende sind keine Empfehlungen, sondern nur Inspirationen und mögliche Argumentationen:

1. Möglichkeit (Schlecht)

Für jede Figur gibt es eine Klasse mit einer passenden Methode zum Zeichnen. Die objekte liegen in verschiedenen Listen

```
class Circle { public void drawCircle(Graphics g) { ... } }
class Polygon { public void drawPolygon(Graphics g) {.... } }

class Painter extends JPanel
{
    List<Circle> circles = ...
    List<Polygon> polygons = ...

    public void paintComponent(Graphics g)
    {
        for (Circle circle : circles) circle.drawCircle(g);
        for (Polygon polygon : polygons) polygon.drawPolygon(g);
    }
}
```

Das ist unflexibel, un-objektorientiert, und sehr schlecht erweiterbar: Der "Painter" muss für jede Klasse extra um eine neue Liste erweitert werden...


2. Möglichkeit ("Üblich") 

Die Klassen implementieren ein gemeinsames Interface, das besagt, dass sie gezeichnet werden können. Alle zeichenbaren Objekte liegen in EINER Liste

```
interface Drawable { public void draw(Graphics g); }

class Circle implements Drawable{ public void draw(Graphics g) {.... } }
class Polygon implements Drawable { public void draw(Graphics g) { ....} }

class Painter extends JPanel
{
    List<Drawable> drawable = ...

    public void paintComponent(Graphics g)
    {
        for (Drawable drawable : drawables) drawable.draw(g);
    }
}
```

Das ist schon besser, weil man beliebige neue Klassen hinzufügen kann: WENN sie das interface implementieren, kann man sie einfach an den "Painter" schicken, und der zeichnet sie dann.

Allerdings könnte man argumentieren, dass die Klassen _eigentlich_ nichts darüber wissen sollten, WIE sie gezeichnet werden. D.h. eigentlich sollten sie sich (in gewissen Sinne Model-View-Controller-konform) nicht SELBST zeichnen. Man könnte das erreichen, indem man dadizierte Renderer anbietet...


3. Möglichkeit ("MVC-konform")

Für jede Klasse gibt es einen Renderer, der ein bestimmtes Interface implementiert

```
interface Renderer { public void draw(Graphics g); }

class Circle { getRadius, getCenter.... }
class CircleRenderer implements Renderer
{
    private Circle circle;
    public void draw(Graphics g) 
    { 
        g.drawOval(..circle.getCenter...circle.getRadius...);
    }
}

class Polygon { getPoint(int index).... }
class PolygonRenderer implements Renderer
{
    private Polygon polygon;
    public void draw(Graphics g) 
    { 
        ... 
    }
}

class Painter extends JPanel
{
    List<Renderer> drawable = ...

    public void paintComponent(Graphics g)
    {
        for (Renderer drawable : drawables) drawable.draw(g);
    }
}
```

Der Vorteil wäre, dass ein "Circle" dann wirklich nur ein abstraktes Datenmodell ist, das die Daten für einen Kreis speichert. WIE der WO hin gezeichnet wird, ist Sache des Renerers....


Aber spätestens, wenn du diese Objekte auch noch editieren willst, musst du dir sowieso (im Vorfeld, bevor du los-implementierst) noch VIIEEEL mehr Gedanken machen...


----------



## winterwanderer (13. Okt 2008)

Hallo Marco13,
erstmal danke für die sehr ausführliche Antwort. Sie hilft mir auf jeden Fall weiter. Mir war es klar, dass 
es nicht objektorientiert sein kann, alles in die paintComponent Methaode des Zeichenfensters hineinzupacken. 
Von daher wollte ich es eigentlich so machen, wie du es in der 2.Methode skizziert hast. Was mir allerdings nicht klar war, und auch noch nicht ganz klar ist, sind die folgenden Punkte:
1. Besitzen die einzelnen 'Drawable-Klassen' automatisch die gleiche Graphik-Umgebung wie mein JPanel. 
   Also entspricht z.B. automatisch der Punkt (0,0) in meiner 'Drawable-Klasse' Polygon dem Punkt (0,0) auf meinem  
   JPanel?
2. Wenn ich einer Drawable-Klasse z.B. einen geschlossenen Linienzug zeichne, muss ich dann den Hintergrund   
    transparent setzen, damit z.B. ein Punkt innerhalb des Linienzuges noch sichtbar ist oder wird bei einem Zeichnen 
    von Linien automatisch nur die Linie gezeichnet und der Hintergrund bleibt unverändert?

Wenn Zeit vorhanden ist, die Fragen noch zu beantworten, freue ich mich, wenn nicht, machts aber auch nichts. Ich werde das jetzt einfach mal ausprobieren und denke, dass ich nun schon weiterkomme.
Viele Grüße
winterwanderer


----------



## SlaterB (13. Okt 2008)

die Objekte in der Liste sind hier keine eigenen JComponents, die per add() hinzugefügt wurden,
sondern simple leere Klassen, die eine Methode
public void draw(Graphics g); 
implementieren

dass Code in einer Methode steht, ist für sich keine Magie, die irgendwelche Hintergründe setzt oder Ursprünge verschiebt,

was immer an Code in draw() steht hat exakt die selben Auswirkungen, wie wenn er direkt in 
paintComponent(Graphics g) 
von Painter stände


----------



## Marco13 (13. Okt 2008)

Also 1. Nein und 2. Wenn man z.B. drawCircle macht (und nicht fillCircle) bleibt der Hintergrund unverändert. Ob "draw" oder "fill" könnte z.B. durch ein boolean "filled" bestimmt werden, das man bei jedem Drawable(!????!) von außen mit setFilled(true/false) setzen kann...


----------



## winterwanderer (13. Okt 2008)

> die Objekte in der Liste sind hier keine eigenen JComponents, die per add() hinzugefügt wurden,
> sondern simple leere Klassen, die eine Methode
> public void draw(Graphics g);
> implementieren


Ja, das isses. Da lag mein Gedankenfehler. Irgendwie hab ich die ganze Zeit gedacht, ich müsste nun JComponents dazuaddieren und mir nun Gedanken machen, über Hintergrund, Layout etc. 




[/quote]


----------

