# Ein wenig zeichnen



## atticus2006 (3. Nov 2009)

Hallo,

ich möchte ein kleines Programm schreiben, welches aus einem Menü besteht und durch anklicken verschiedene Gegenstände auf die Zeichenfläche malt. Wurde das Objekt gemalt, dann erscheint auch ein Button und mit diesem kann ich zwischen zwei Farben für das Objekt hin und her schalten.

Ich möchte gerne im linken Bereich ein Dreieck, im mittleren Bereich ein Krei und in den rechten Bereich ein Rechteck. Dazu habe ich die folgenden drei Klassen, welche von JPanel erben:


```
class ZeichneDreieck extends JPanel {
	public void paintComponent(Graphics g) {
		if (dreieckblau)
			g.setColor(Color.RED);
		else
			g.setColor(Color.BLUE);
		Polygon poly = new Polygon();
		poly.addPoint(10,10);
		poly.addPoint(100,10);
		poly.addPoint(50,110);
		poly.addPoint(10,10);
		g.fillPolygon(poly);
}


class ZeichneKreis extends JPanel {
	public void paintComponent(Graphics g) {
		if (kreisblau)
			g.setColor(Color.RED);
		else
			g.setColor(Color.BLUE);
		g.fillOval(10,10,100,100);
}


class ZeichneRechteck extends JPanel {
	public void paintComponent(Graphics g) {
		if (rechteckblau)
			g.setColor(Color.RED);
		else
			g.setColor(Color.BLUE);
		g.fillRect(10,10,100,150);
}
```

Doch wie mache ich es jetzt, dass die Koordinaten nicht fest vorgegeben sind, sondern sich an der jeweiligen Position auf dem Frame orientieren?
Außerdem werden mir die drei Dinge auch nicht angezeigt...

Kann mir jemand helfen?


----------



## KrokoDiehl (3. Nov 2009)

Zum Einen brauchst du eine Zeichenfläche, auf die deine Komponenten gelegt werden müssen. Ein einfaches _JPanel _mit _null_-Layout sollte es hier tun.
Diesem Panel musst du deine Zeichenobjekte hinzufügen (via 
	
	
	
	





```
add()
```
) und dabei deren _bound_s setzen.

Wie du diese setzt, ist im Grunde nur Mathematik. Du hast die Größe der Zeichenfläche (Breite und Höhe) und kannst dann selbst ausrechnen, wie und wo du deine Objekte hinhaben willst.

Damit kannst du in deinen Zeichenobjekten das Polygon z.B. schon direkt im Konstruktor berechnen. Jedesmal ein neues in 
	
	
	
	





```
paintComponent()
```
 erstellen ist eher unschön.

In Pseudocode stelle ich mir das wie folgt vor:

```
JPanel zeichenFlaeche = new JPanel(null);
myFrame.add(zeichenFlaeche, BorderLayout.CENTER); //bzw. entsprechendes

// ... beim Hinzufügen:
Rectangle groesse = new Rectangle(10, 10, zeichenFlaeche.getWidth() / 3, zeichenFlaeche.getHeight() / 2);
ZeichneDreieck dreieck = new ZeichneDreick(groesse);
zeichenFlaeche.add(groesse); //wichtig, sonst werden sie nicht gezeichnet

//... Dreieck:

class ZeichneDreieck extends JPanel
{
    protected Polygon meineForm = null;
    public ZeichneDreieck(Rectangle groesse)
    {
        meineForm = new Polygon();
        // ... Polygon aufbauen

        this.setBounds(groesse); //wichtig
    }
```

Ich denke das sollte als Denkanstoß genügen


----------



## atticus2006 (3. Nov 2009)

Hallo,

schon einmal vielen Dank für deine Nachricht.

Reicht es denn eine einzige Zeichenfläche zu bauen, oder muss man drei Zeichenflächen benutzen?

Das Rechteck groesse dient also zur Begrenzung des Dreiecks und nicht etwas zum Zeichnen des Dreiecks selbst, oder?


----------



## KrokoDiehl (3. Nov 2009)

Eine Zeichenfläche reicht, so wie ich dein Vorhaben verstehe.
Und ja, das 
	
	
	
	





```
setBounds()
```
 sagt dem Zeichenobjekt, wie groß es ist. Es ist quasi der Erstatz zum _LayoutManager_, wobei die x- und y-Angaben nur der Positionierung innerhalb der Zeichenfläche dienen.
Innerhalb des Zeichenobjekts kannst du mit 
	
	
	
	





```
getWidth()
```
 und 
	
	
	
	





```
getHeight()
```
 abfragen wie groß dein Zeichenobjekt ist (in Pixeln) und deine Polygonpunkte (oder sonstige Geometrie) entsprechend ausrichten.


----------



## atticus2006 (4. Nov 2009)

Hallo,

noch einmal vielen Dank. Wie ist es mir denn jetzt Möglich die Farbe des Dreiecks zu verändern. Da ich ja nicht mehr das (Graphics g) habe, kann ich ja auch leider nicht mehr g.setColor(Color.RED) aufrufen.


----------



## KrokoDiehl (4. Nov 2009)

> Da ich ja nicht mehr das (Graphics g) habe, kann ich ja auch leider nicht mehr g.setColor(Color.RED) aufrufen.


...doch, die 
	
	
	
	





```
paintComponent()
```
-Methode musst du dennoch überschreiben, wenn dein Panel eigene Figuren malen soll.


----------



## atticus2006 (5. Nov 2009)

Leider habe ich die Aufgaben falsch verstanden, denn es sollen mehrere Objekte (Kreise, Quadrate, Dreiecke) gezeichnet werden können. Man wählt also über ein Menü z.B. Dreieck aus und jedes Mal, wenn man auf die Zeichenfläche klickt, wird ein kleines Dreieck gezeichnet. Anschließend kann man ein Objekt auswählen um für dieses die Farbe zyklisch zu ändern mittels eines Buttons (für Dreiecke, Quadrate und Kreise jeweils einen separaten Button).
Nun stehe ich natürlich ein wenig auf dem Schlauch, da ich zum Beispiel nicht weiß, wie ich das Dreieck auf Mausklick zeichnen kann und später dann auch noch selektieren kann.


----------



## KrokoDiehl (5. Nov 2009)

Nun.
Du hast drei Klassen für je eine geometrische Figur, die sich selbst zeichnen kann. Über einen _MouseListener _an der Zeichenfläche kannst du rausbekommen, wohin der Benutzer geklickt hat. Diesen Punkt kannst du dann als Mittelpunkt, oder als oben/rechten Punkt für die jeweilige Figur nehmen:

```
// bitte als Pseudocode beachten!

//... Ereignisbehandlung
Point p = event.getPoint();
Dreieck d = new Dreieck(p);
zeichenFlaeche.add(d);


//... Bsp. für Dreieck
class Dreieck extends JPanel
{
    public Dreieck(Point anker)
    {
        super();
        setBounds(anker.x, anker.y, 50, 50); //Bsp für Std-Größe des Dreiecks
        //Dreieckpunkte berechnen
    }

    protected void paintComponent(Graphics g)
        //...
}
```


----------



## atticus2006 (5. Nov 2009)

Hallo,

ich würde vorschlagen, dass wir uns zunächst auf das Rechteck stürzen, weil dieses doch ein wenig einfacher ist...


```
class Rechteck extends JPanel {
	    public Rechteck(Point anker) {
	        super();
	        setBounds(anker.x, anker.y, 50, 50);
	    }
	 
	    protected void paintComponent(Graphics g) {
	    	g.setColor(Color.BLUE);
	    	g.fillRect(10, 10, 5, 10);
	    }
	}
```


```
public void mouseClicked(MouseEvent arg0) {
		Point p = arg0.getPoint();
		Rechteck r = new Rechteck(p);
		drawPanel.add(r);
		
	}
```

Nun müsste doch eigentlich immer wenn ich auf meine Zeichenfläche klicke ein Rechteck gezeichnet werden, aber leider passiert überhaupt nichts. In kann so viel klicken, wie ich will, aber leider passiert nichts... Woran kann das liegen?


----------



## KrokoDiehl (5. Nov 2009)

```
protected void paintComponent(Graphics g) {
            g.setColor(Color.BLUE);
            g.fillRect(10, 10, 5, 10);
        }
```
Dein Rechteck-Panel hat die Größe 50x50 (s. Konstruktor). Innerhalb der Komponente (also in der o.g. Methode) greifst du nur noch auf lokale Koordinaten zu. Sprich, wenn du willst dass dein ganzes Panel ausgefüllt wird, muss der Aufruf wie folgt aussehen:

```
g.fillRect(0, 0, this.getWidth(), this.getHeight());
```
Wenn dein drawPanel noch irgendwo ein null-Layout bekommt

```
drawPanel = new JPanel(null);
oder
drawPanel.setLayout(null);
```
sehe ich sonst keine Probleme.


----------



## atticus2006 (5. Nov 2009)

Moin,

also sp richtig was passiert lieder immer noch nicht. Ich poste mal die relevanten Stellen meines bisherigen codes.


```
JFrame frame = new JFrame();
JPanel drawPanel = new JPanel(null);
```


```
public void makeFrame() {
	frame.setTitle("Bunte Knöpfe");
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	frame.setSize(500, 300);
	frame.add(BorderLayout.CENTER, drawPanel);
	frame.setVisible(true);
}
```


```
class Rectangle extends JPanel
{
    public Rectangle(Point anchor)
    {
        super();
        setBounds(anchor.x, anchor.y, 50, 50);
    }
 
    protected void paintComponent(Graphics g) {
    	g.setColor(Color.BLUE);
    	g.fillRect(0, 0, this.getWidth(), this.getHeight());
    }
}
```


```
public void mouseClicked(MouseEvent arg0) {
	Point p = arg0.getPoint();
	Rectangle r = new Rectangle(p);
	drawPanel.add(r);
	
}
```

Aber es passiert beim Klicken leider gar nichts...


----------



## KrokoDiehl (5. Nov 2009)

Damit das mal zu einem Ende kommt... 

Das folgende Programm läuft bei mir. Ich habe auch erst gemerkt, dass ein 
	
	
	
	





```
repaint()
```
 nach dem Klick fehlte. Ohne das sieht man das Rechteck erst nach Vergrößern des Fensters.
Ansonsten habe ich in deinen Code-Stellen nicht gesehen, ob der _MouseListener _auch der Zeichenfläche hinzugefügt wird.

Also, das folgende Beispiel tut bei mir, ohne jedoch Anspruch auf Schönheit zu haben 

```
public class Test
{
	static class Rechteck extends JPanel
	{
		public Rechteck(Point p)
		{
			super();
			this.setBounds(p.x, p.y, 50, 50);
		}
		
		@Override
		protected void paintComponent(Graphics g) 
		{
			g.setColor( Color.blue );
			g.fillRect(0, 0, this.getWidth(), this.getHeight());
		}
	}
	
	public static void main(String[] args) 
	{
		final JPanel pane = new JPanel(null);
		pane.setBackground( Color.white );
		pane.setPreferredSize(new Dimension( 200, 200));
		pane.addMouseListener(new MouseAdapter() 
		{
			@Override
			public void mouseClicked(MouseEvent event) 
			{
				Rechteck r = new Rechteck(event.getPoint());
				pane.add(r);
				pane.repaint( r.getBounds() );
			}
		});
		
		final JFrame frame = new JFrame("Zeichnen");
		frame.add(pane, BorderLayout.CENTER);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		SwingUtilities.invokeLater(new Runnable()
		{
			@Override
			public void run() 
			{
				frame.pack();
				frame.setVisible(true);
			}
		});
	}
}
```


----------



## atticus2006 (5. Nov 2009)

Hallo,

vielen Dank. Hatte tatsächlich nur vergessen den MouseClick-Listener zu starten. Jetzt klappt alles und ich kann schon das Rechteck und die Kreise zeichnen, nur an den Dreiecken harpert es noch, da ich noch nicht weiß, wie ich die einzelnen Punkte in Abhängigkeit meines Mausklicks berechnen muss. Gibt es dafür einen TricK?







Wenn ich zunächst nur Kreise zeichne, habe die keinen Hintergrund, sobald ich aber ein Rechteck gemalt habe, erscheint bei den danach gezeichneten Kreisen blaue Ecken, warum?


----------



## icarus2 (5. Nov 2009)

Du bekommst den Punkt S(x; y) durch den MouseListener (die Koords, wo gelickt wurde).
Der Punkt S ist dabei der Schwerpuntk des Dreiecks.
Die Variable w steht für die Breite des Dreiecks (sie ist ein konstanter Wert, den du eingeben musst).
Die Variable h steht für die Höhe im Dreieck.
Die Punkte A, B, C stellen die Eckpunkte des Dreiecks dar.

Wenn das Dreieck gleichseitig ist (ich gehe davon aus, dass das bei dir der Fall sein wird, dann gilt folgendes):

h = (w^2 - (w / 2)^2)^(1/2)

Eckpunkte:
A(x ; y - 2/3 * h)
B(x - w/2 ; y + h / 3)
C(x + w/2 ; y + h / 3)

Zur Methode paintComponent:
Die Methode paintComponent(...) sollte immer folgende Struktur haben:
[Java]        
	@Override
	public void paintComponent(Graphics g){
		super.paintComponent(g);

		//...
	}
[/Java]
Es muss unbedingt die paintComponent(...) Methode der Superklasse aufgerufen werden.

*Edit:
Ich habe das ganze übrigens nicht getestet. Aber ich denke es müsste funktionieren. Die x und y Werte, die ich angegeben habe, sind bereits auf das Koordinatensystem von Java umgerechnet.


----------



## atticus2006 (5. Nov 2009)

Danke, das mit dem Aufruf von 
	
	
	
	





```
super.paintComponent(g)
```
 hat geholfen, aber nun hänge ich noch ein wenig beim Dreieck. Wie greife ich denn auf die vom MouseListener übergebenen Werte innerhalb meiner Klasse 
	
	
	
	





```
Dreieck()
```
 drauf zu? Ich habe ja nicht, wie beim Konstruktor die Möglichkeit die Points zu erhalten, oder sehe ich den Wald vor lauter Bäumen schon wieder nicht...


----------



## icarus2 (5. Nov 2009)

Du speicherst am besten eine Instanz der Klasse Point als Instanzvariable.

[Java]
public class TriangleDrawer extends JFrame implements MouseListener {

	Point s = new Point();
    JPanel panel;

    //Constructor
	public TriangleDrawer(){
		//....
	}

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable(){
			@Override
			public void run(){
				new TriangleDrawer();
			}
		});

	}

	@Override
	public void mouseClicked(MouseEvent e) {}

	@Override
	public void mouseEntered(MouseEvent e) {}

	@Override
	public void mouseExited(MouseEvent e) {}

	@Override
	public void mousePressed(MouseEvent e) {}

	@Override
	public void mouseReleased(MouseEvent e) {

		s.x = e.getX();
		s.y = e.getY();

		panel.repaint();

	}

}
[/Java]

Anschliessen kannst du in der Methode paintComponent(..) auf das das Point-Objekt zugreiffen.


----------



## atticus2006 (5. Nov 2009)

Irgendwie zeichnet er mir jetzt gar nichts mehr, wenn ich ein Dreieck haben möchte...


```
class Triangle extends JPanel {

		public Triangle(Point p) {
			super();
			this.setBounds(p.x, p.y, 25, 25);
		}

		public void paintComponent(Graphics g) {
			super.paintComponent(g);
			g.setColor(Color.GREEN);
			Polygon poly = new Polygon();
			int h = 25;
			poly.addPoint(s.x, s.y - (2/3)*h);
			poly.addPoint(s.x-25/2, s.y+h/3);
			poly.addPoint(s.x + 25 / 2, s.y + h / 3);
			poly.addPoint(s.x, s.y - (2 / 3) * h);
			g.fillPolygon(poly);
			super.paintComponent(g);
			System.out.println(h);

		}
	}
}
```


----------



## icarus2 (5. Nov 2009)

Nimm das zweite super.paintComponent(g) raus. In der Methode werden Aufräumarbeiten erledigt, wodurch die Dreiecke wieder gelöscht werden.

So:
[Java]
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.GREEN);
            Polygon poly = new Polygon();
            int h = 25;
            poly.addPoint(s.x, s.y - (2/3)*h);
            poly.addPoint(s.x-25/2, s.y+h/3);
            poly.addPoint(s.x + 25 / 2, s.y + h / 3);
            poly.addPoint(s.x, s.y - (2 / 3) * h);
            g.fillPolygon(poly);
            System.out.println(h);

        }
[/Java]


----------



## atticus2006 (5. Nov 2009)

Leider werden trotzdem keine Dreiecke gezeichnet...
Die Kreise und die Rechtecke funktionieren ohne Probleme, aber die Dreiecke wollen einfach nicht.


----------



## icarus2 (5. Nov 2009)

Hier habe ich etwas Code, bei dem die Dreiecke gezeichnet werden:

[Java]
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder;


public class TriangleDrawer extends JFrame implements MouseListener {

	Container c;
	JPanel panel;
	Point s;

	private final static int TRIANGLE_WIDTH = 200;
	private static int triangleHeight;

	public TriangleDrawer(){

		triangleHeight = (int) (Math.sqrt(Math.pow(TRIANGLE_WIDTH, 2) - Math.pow(TRIANGLE_WIDTH/2, 2)));
		s = new Point();

		//GUI
		c = getContentPane();

		panel = new JPanel(){

			@Override
			public void paintComponent(Graphics g){
				super.paintComponent(g);

				Polygon p = new Polygon();
				p.addPoint(s.x, s.y - 2 * triangleHeight / 3);
				p.addPoint(s.x - TRIANGLE_WIDTH / 2, s.y + triangleHeight / 3);
				p.addPoint(s.x + TRIANGLE_WIDTH / 2, s.y + triangleHeight / 3);

				g.drawPolygon(p);
			}

		};
		panel.setLocation(0, 0);
		panel.setBorder(new TitledBorder("Panel"));
		panel.setSize(this.getSize());

		c.add(panel);

		addMouseListener(this);

		setSize(500, 500);
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable(){
			@Override
			public void run(){
				new TriangleDrawer();
			}
		});

	}

	@Override
	public void mouseClicked(MouseEvent e) {}

	@Override
	public void mouseEntered(MouseEvent e) {}

	@Override
	public void mouseExited(MouseEvent e) {}

	@Override
	public void mousePressed(MouseEvent e) {}

	@Override
	public void mouseReleased(MouseEvent e) {

		s.x = e.getX();
		s.y = e.getY();

		panel.repaint();

	}

}

[/Java]

*Edit
Die Dreiecke sind in meinem Beispiel leider nicht gleichseitig. Ich habe da wohl irgendwo einen kleinen Fehler gemacht. Aber den wirst du bestimmt finden.

*Edit 2
Code funktioniert jetzt einwandfrei.


----------



## atticus2006 (5. Nov 2009)

Stimmt, dein Code funktioniert, aber leider funktioniert die Integration in mein Projekt immer noch nicht. Muss denn der MouseListener von der Klasse selbst implementiert werden? Ich habe das jetzt einfach in dem anderen MouseListener und die Werte werden auch übergeben, was ein kurzes System.out.println(s.x + " " + s.y) verrät, aber leider erscheint kein grünes Dreieck...


----------



## icarus2 (5. Nov 2009)

Hmm, zeige mal etwas Quellcode. Schau mal was du für Werte hast in der Methode paintComponent(...). Bist du sicher, dass du überall mit den gleichen Objekten arbeitest?


----------



## atticus2006 (6. Nov 2009)

Hier also der relevante Code:


```
private final static int TRIANGLE_WIDTH = 200;
	    private int triangleHeight = 0;
	    

		public Triangle(Point p) {
			super();
			this.setBounds(p.x, p.y, 50, 50);
		}

		public void paintComponent(Graphics g) {
			
			int triangleHeight = (int) (Math.sqrt(Math.pow(TRIANGLE_WIDTH, 2)
					- Math.pow(TRIANGLE_WIDTH / 2, 2)));

			super.paintComponent(g);

			g.setColor(Color.GREEN);

			Polygon p = new Polygon();
			p.addPoint(s.x, s.y - 2 * triangleHeight / 3);
			p.addPoint(s.x - TRIANGLE_WIDTH / 2, s.y + triangleHeight / 3);
			p.addPoint(s.x + TRIANGLE_WIDTH / 2, s.y + triangleHeight / 3);

			g.fillPolygon(p);

		}

	}
```


```
public void mouseClicked(MouseEvent mouseevent) {
		
		

		if (triangle) {
			
	        s.x = mouseevent.getX();
	        s.y = mouseevent.getY();

	        System.out.println(s.x + "  " + s.y);
			
			Triangle t = new Triangle(mouseevent.getPoint());
			drawPanel.add(t);
			drawPanel.repaint(t.getBounds());
		}
       }
```


----------



## KrokoDiehl (6. Nov 2009)

[JAVA=8]
this.setBounds(p.x, p.y, 50, 50);
[/code]
Hier sagst du, dass dein Triangle insgesamt eine Fläche von 50x50 Pixeln zur Verfügung haben soll, mehr nicht. D.h. deine Definiton von 
	
	
	
	





```
TRIANGLE_WIDTH = 200;
```
 kann gar nicht angewendet werden.

In der 
	
	
	
	





```
paintComponent()
```
-Methode musst du Bezug nehmen auf die Größe deines Panels. Du hast beim Zeichnen nur die Fläche zur Verfügung, die du mit 
	
	
	
	





```
setBounds()
```
 angibts:
Von Punkt 
	
	
	
	





```
(0, 0)
```
 (links oben) bis 
	
	
	
	





```
(getWidth(), getHeight())
```
 (in dem Fall 
	
	
	
	





```
(50, 50)
```
). Dies ist das lokale Koordinatensystem des Triangle-Panels.
D.h. dass dein berechnetes Polygon in 
	
	
	
	





```
paintComponent()
```
 vermutlich außerhalb dieses Bereichs liegt und daher nicht gezeichnet wird / werden kann.

Die Berechnung vom Dreieck-Polygon solltest du in den Triangle-Konstruktor auslagern. Es bei jedem 
	
	
	
	





```
paint()
```
 neu berechnen zu lassen ist unnötig, weil immer das gleiche rauskommt.
Außerdem hast du im Konstruktor nach dem
	
	
	
	





```
setBounds()
```
-Aufruf direkt den verfügbaren Bereich deines Triangle-Panels:

```
protected Polygon triangle = null;

public Triangle(Point p)
{
    super(null);    
    this.setBounds(p.x, p.y, TRIANGLE_WIDTH, TRIANGLE_WIDTH);
    // nun hast du einen verfügbaren Bereich von (0, 0) bis (200, 200)
    triangle = new Polygon();
    triangle.addPoint(0, 0); // oben links
    triangle.addPoint(TRIANGLE_WIDTH, 0); //oben rechts
    triangle.addPoint(TRIANGLE_WIDTH/2, TRIANGLE_WIDTH); //unten Mitte
}

protected void paintComponent(Graphics g)
{
    g.setColor( Color.blue );
    g.fillPolygon(triangle);
}
```


----------



## atticus2006 (7. Nov 2009)

So, bisher läuft alles. Ich kann also mit dem Menü die verschiedenen geometrischen Figuren auswählen und auf der Schaltfläche zeichnen. Nun soll es noch möglich sein, dass ich nachträglich eines der gezeichneten Objekte markiere und per Buutton die Farbe verändert nach einem bestimmten Farbzyklus zwischen drei Farben.

Ich habe derzeit nur leider keine Idee, wie ich jetzt wieder schauen kann, ob eines der Objekte markiert ist. Gibt es dafür einen Trick?


----------



## icarus2 (8. Nov 2009)

Also ich würde mal alle geometrischen Figuren in einer Liste speichern und dazu auch die Farbe des jeweiligen Objekts.

1. Möglichkeit:
Du schreibst eine Methode, die nach einem Mausklick die am nächsten gelegene Figur findet und diese "selektiert", oder irgendwie angezeigt wird, dass diese Figur den Fokus hast. Dann kannst du z.B. per Button-Klick dann die Farbe der selektierten Figur geändert wird. Würde wohl ein repaint() sein für das Neuzeichnen.

2. Möglichkeit:
Wieder eine Liste machen. Du erstellst eine JComboBox, in der alle Figuren durchnummeriert sind. Anschliessed kannst du eine Figur selektieren, indem du sie in der JComboBox selektierst. (Wäre vielleicht etwas einfacher als eine eigene Methode zu schreiben). Analog zu oben dann der Button-Klick.

3. Möglichkeit:
Wieder eine Liste machen. Anschliessend einen Button, mit dem du dich durch die Liste klicken kannst, wodurch immer die nächste Figur in einer Liste selektiert wird (dazu musst du halt einfach etwas neben die Figur zeichnen um zu zeigen welche Figur selektiert ist). Dann wieder analog der Buttonklick.

PS:
Sind nur Anregungen, wie man es möglicherweise machen könnte. Gibt natürlich noch viele andere Möglichkeiten, vor allem was die Darstellung angeht. Hoffe aber, dass es dir ein bisschen hilft.


----------



## atticus2006 (8. Nov 2009)

Hallo,

die Ideen sind auf jeden Fall schon einmal echt nicht schlecht. Ich denke, dass wohl die Möglichkeit 1 am Besten wäre, aber derzeit weiß ich noch nicht, wie ich das ganze in eine Liste packen kann, denn pro Figur muss ich ja speicher, wo sie liegt, was sie ist und welche Farbe sie hat, damit ich dann die nächste farbe auswählen kann.

Kannst du vielleicht beim generieren der Listeneinträge sagen, wie man diese zu wählen hat, dass es effektiv ist?


----------



## icarus2 (8. Nov 2009)

Eine Idee:
Erstelle für jede gemoetrische Figur eine eigene Klasse, z.B. Dreieck, Kreis oder welche Figur das auch immer sein mag. Alle diese Klassen implementieren das Interface GeometricalFigure.

GeometricalFigure:
[Java]
import java.awt.Graphics;


public interface GeometricalFigure {

	void draw(Graphics g);

	void setSelectionState(boolean state);

	boolean isIselelected();

}
[/Java]

Anschliessend kannst du eine Liste erstellen mit folgenden Parametern:
[Java]
ArrayList<GeometricalFigure> list = new ArrayList<GeometricalFigure>();
[/Java]

Nun hat jedes Element in deiner Liste die 3 Methoden des Interface zur Verfügung. Auch wird dadurch die Methode paintComponent(Graphics g) einfach:
[Java]
@Override
	public void paintComponent(Graphics g){
		super.paintComponent(g);

		for(GeometricalFigure fig : list){
			fig.draw(g);
		}
	}
[/Java]


In der draw(...)-Methode, die die Klassen implementieren müssen wegen dem Interface müssen dann halt nur noch Dinge stehen wie g.drawRect(...) und solche Dinge. Ich hoffe das hilft dir etwas.


*Edit:
Sry, hab den falschen Button geklickt, wollte nur Vorschau wählen... musst dich noch etwas Gedulden bis ich fertig bin ^^

*Edit 2:
So, jetzt aber ;-)


----------



## atticus2006 (8. Nov 2009)

Hmmh.

Dann müsste ich ja alle meine derzeitigen Klassen wieder umschreiben und ich habe dann ja immer noch das Problem zu überprüfen, ob ein jeweiliges Element angeklickt wurde.


----------



## icarus2 (8. Nov 2009)

Wenn du deine Klassen von Rectangle2D ableitest, dann kannst du mit der Methode intersects(..) sehr gut überprüfen, ob etwas angeklickt wurde.

Ich stelle mir das so vor:
Du hast einen MouseListener. Wenn die Maus geklickt wurde, wird dieser aufgerufen. Dort speicherst du den Punkt wo geklickt wurde. Du erstellst anschliessend ein kleines Rechteck um den Punkt (das Rechteck ist eine Instanz von Rectangle2D).  Anschliessend kannst du mit dasNeueRechtEck.instersects(rechtEckAusListe) überprüfen, ob eines angewählt wurde. Wenn ja bekommt dieses den Fokus... dafür hast ja entsprechende Methoden im Interface implementiert.

Weisst du was ich meine?


----------



## atticus2006 (8. Nov 2009)

Also, kurze Zusammenfassung: Ich habe jetzt ein Frame mit einem Panel, auf dem ich per Mausklick verschiedene Objekte einzeichnen kann. Welches Objekt gezeichnet wird entscheide ich über ein Menü. Dieses funktioniert auch jetzt ohne Probleme.

Nun möchte ich ein einzelnes Objekt markieren. Dazu könnte man den MouseListener um folgendes ergänzen, wenn die Maus geklickt wurde: 

```
public void mouseClicked(MouseEvent mouseevent) {
	Point s = new Point();
	s.x = mouseevent.getX();
	s.y = mouseevent.getY();
}
```

Doch wie geht es jetzt weiter. Wenn dieser Klick nun innerhalb eines Objektes war, dann soll dieses markiert werden und ein Button wird eingeblendet, über den man dann die Farbe des Objektes durch Klicken verändern kann...


----------



## icarus2 (8. Nov 2009)

Genau. Das Problem ist jetzt, dass deine anderen Klassen von Rectangle2D abgeleitet sein sollten. Weil dann könntest du jetzt aus dem Punkt ein kleines Rechteck (auch Rectangle2D-Objekt machen) und anschliessend in einer for-Schleife mit Hilfe der in der Rectangle2D geschriebenen Methode intersects überprüfen, welches Objekt denn angeklickt wurde. Das funktioniert aber nur, wenn die anderen Objekte in der Liste vom Typ Rectangle2D sind. Sonst funzt die Methode intersects nicht.


----------



## atticus2006 (8. Nov 2009)

Okay, könntest du denn vielleicht die Klasse 
	
	
	
	





```
Rectangle
```
 anpassen, dass es von der Klasse 
	
	
	
	





```
Rectangle2D
```
 abhängt?


```
class Rectangle extends JPanel {
		public Rectangle(Point p) {
			super();
			this.setBounds(p.x, p.y, 25, 25);
		}

		protected void paintComponent(Graphics g) {
			super.paintComponent(g);
			g.setColor(Color.BLUE);
			g.fillRect(0, 0, this.getWidth(), this.getHeight());

		}
	}
```


----------



## icarus2 (8. Nov 2009)

Ich habe jetzt einmal etwas gecodet:

Erst einmal die Hauptklasse:
[Java]
import java.awt.BorderLayout;
import java.awt.Container;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder;


public class Main extends JFrame {

	Container c;

	DrawingPanel drawingPanel;

	protected Main(){

		c = getContentPane();
		c.setLayout(new BorderLayout());

		drawingPanel = new DrawingPanel();
		drawingPanel.setBorder(new TitledBorder("Drawing Panel"));


		c.add(drawingPanel, BorderLayout.CENTER);


		setSize(600, 400);
		setLocationRelativeTo(null);
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


	}

	public static void main(String[] args) {

		SwingUtilities.invokeLater(new Runnable(){
			@Override
			public void run(){
				new Main();
			}
		});

	}

}
[/Java]

Dann die Panel-Klasse:
[Java]
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;

import javax.swing.JPanel;


public class DrawingPanel extends JPanel implements MouseListener, KeyListener{

	ArrayList<RectangleFigure> figures = new ArrayList<RectangleFigure>();

	Point clickCoords;
	Rectangle2D.Double clickArea;

	private static boolean ctrlPressed;

	public DrawingPanel(){
		clickCoords = new Point();
		clickArea = new Rectangle2D.Double();

		setFocusable(true);

		addKeyListener(this);
		addMouseListener(this);

	}

	@Override
	public void paintComponent(Graphics g){
		super.paintComponent(g);

		for(Drawable d : figures){
			d.draw(g);
		}

	}

	public void checkIntersection(Rectangle2D.Double clickedRectangle){

		for(int i = 0; i < figures.size(); i++){
			if(clickedRectangle.intersects(figures.get(i))){
				figures.get(i).setSelected(true);
				break;
			}else{
				figures.get(i).setSelected(false);
			}
		}

	}


	@Override
	public void mouseClicked(MouseEvent e) {
		//if ctrl is pressed, there won't be any figures added
		if(ctrlPressed){

			clickCoords.x = e.getX();
			clickCoords.y = e.getY();

			clickArea.x = clickCoords.x - 10;
			clickArea.x = clickCoords.y - 10;
			clickArea.width = clickArea.x + 20;
			clickArea.height = clickArea.x + 20;

			checkIntersection(clickArea);

		}else{
			figures.add(new RectangleFigure(e.getX(), e.getY(), 50, 50, false));
			repaint();
		}

	}

	@Override
	public void mouseEntered(MouseEvent e) {}

	@Override
	public void mouseExited(MouseEvent e) {}

	@Override
	public void mousePressed(MouseEvent e) {}

	@Override
	public void mouseReleased(MouseEvent e) {}

	@Override
	public void keyPressed(KeyEvent e) {

		if(e.getKeyCode() == KeyEvent.VK_ALT){
			ctrlPressed = true;
		}

	}

	@Override
	public void keyReleased(KeyEvent e) {

		if(e.getKeyCode() == KeyEvent.VK_ALT){
			ctrlPressed = false;
		}

	}

	@Override
	public void keyTyped(KeyEvent e) {}



}
[/Java]

2 Interfaces:
[Java]
public interface Selectable {

	void setSelectionState(boolean state);

	boolean isSelected();

}
[/Java]

[Java]

import java.awt.Graphics;

public interface Drawable {

	void draw(Graphics g);

}
[/Java]

Diese Interfaces werden von dieser abstrakten Klasse implementiert (die abstrakte Klasse wird gebraucht, um die ArrayList typsicher zu machen):
[Java]
import java.awt.geom.Rectangle2D;


public abstract class Figure extends Rectangle2D.Double implements Drawable, Selectable {

	private boolean selected;

	public Figure(){

	}

	public Figure(int x, int y, int width, int height){
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
	}


	public Figure(int x, int y, int width, int height, boolean selected){
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
		this.selected = selected;
	}

	public boolean isSelected() {
		return selected;
	}

	public void setSelected(boolean selected) {
		this.selected = selected;
	}
}
[/Java]

Und dann eine konkrete Klasse, die jeweils gebraucht wird. In diesem Fall ein Rechteck:
[Java]
import java.awt.Graphics;


public class RectangleFigure extends Figure {

	public RectangleFigure() {

	}

	public RectangleFigure(int x, int y, int width, int height) {
		super(x, y, width, height);

	}

	public RectangleFigure(int x, int y, int width, int height, boolean selected) {
		super(x, y, width, height, selected);

	}

	@Override
	public void draw(Graphics g) {

		g.drawRect((int)x, (int)y, (int)width, (int)height);

	}

	@Override
	public boolean isSelected() {
		return isSelected();
	}

	@Override
	public void setSelectionState(boolean state) {

		setSelected(state);

	}

}
[/Java]

So sollte es in etwas einigermassen sauber funktionieren. Schau es dir mal an und wenn etwas unkar sein sollte, dann frage ;-) Das Programm ist nicht fertig, sollte aber von der Klassenstruktur her nicht schlecht sein. Dadurch kannst du ohne Probleme beliebige Figuren hinzufügen. Natürlich fehlt die GUI so wie du sie möchtest und alles.


----------

