Swing Darstellung von JComponents in Swing

andi_help

Mitglied
Hallo,
ich habe ein kleines Problem mit Swing undkomme nicht auf die Lösung des einfachen Problems.
Ich will in einem JFrame zwei JComponenten als Kreise darstellen. Leider wird mir nur immer einer angezeigt, der andere nicht. Könnt ihr mir dabei helfen?

Der Source-Code ist

Java:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JComponent;
import javax.swing.JFrame;

class Circle extends JComponent{

	int x, y;
	
	Circle(int x, int y){
		this.x=x;
		this.y=y;
		setVisible(true);
	}
		
	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2d = (Graphics2D)g;		
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
		RenderingHints.VALUE_ANTIALIAS_ON);     
		g2d.drawOval(x, y, 30, 30);
		repaint();
	}
}		

public class Rahmen extends JFrame{

	Rahmen(){
		setVisible(true);
		setSize(400, 400);
		setLocation(100, 100);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	public static void main(String[] args) {
		Rahmen rahmen=new RAamen();
		Circle x=new Circle(50, 50);
		Circle x1=new Circle(250, 250);
		rahmen.add(x);
		rahmen.add(x1);
	}
}

Vielen Dank schon mal!!
 
Zuletzt bearbeitet von einem Moderator:
JFrame hat standardmäßig nen borderlayout. add ohne Angabe von contraints fügt die Komponente ins Center Fes borderlayout und dort kann nur eine komponente liegen.
 
Soll heißen, dass du besser

Java:
// rahmen.setLayout(new BorderLayout());   [standardmäßig]
rahmen.add(x, BorderLayout.NORTH);
rahmen.add(x1, BorderLayout.CENTER);

schreibst. Oder für eine gleichmäßige Verteilung:

Java:
rahmen.setLayout(new GridLayout(2, 1));
rahmen.add(x);
rahmen.add(x1);

Denn beim BorderLayout wird die Komponente standardmäßig in die Mitte gelegt, wenn du keine Richtugn angibst, und verdeckt damit in diesem Fall, den Kreis darunter 😉


Nur so nebenbei:
Ich will ja nicht in deine Code-Struktur eingreifen, aber Kindklassen von Component haben automatisch die Eigenschaft "Location". Die könntest du hier statt den extra Integer-Werten x und y benutzen.
Also:
Java:
Circle x = new Circle();     // Eigenschaften x und y sind gestrichen und paintComponent nutzt getLocation()
Circle x1 = new Circle();

x.setLocation(new Point(50, 50));
x1.setLocation(new Point(250, 250);

Gruß, Thomas
 
Vielen Dank!

Ich sehe schon, ich muss da noch etwas ändern, da eigentlich ein Kreis dort erscheinen soll, wo man geklickt. Jedoch soll man auch noch auswählen können, welche Form man anfügt, sprich, wenn F2 gedrückt, dann kann man Kreise einfügen, wird dann F3 gedrückt, dann werden Rechtecke gezeichnet.
Kreise und Rechtecke sollen jeweils Komponenten sein..mal sehen.

Gruss,
Andi
 
Ich glaube, dass du, wenn du keine eigenen Listener oder so in deine Kreise/Rechtecke einbringst. Die Klasse "JComponent" da überflüssig ist. Mach lieber eine eigene Klasse "Circle" (Ohne Erbung) mit x, y und allem weiteren und füg ihr ne eigene Methode namens "paint(Graphis g)" oder so zu, die du dann im paintComponent von dem Panel aufrufst 😉

Mein Tipp als Grafikprogramm-Experte (ich schreib grad ein riesiges Projekt - ähnlich Paint.Net)...


Gruß, Thomas
 
super! hat geklappt..
Wenn ich jetzt noch erreichen, dass mein Kreis an der Position x,y gezeichnet (x,y sind Koordinaten eines Mausklicks auf das Panel), dann darf ich Circles wieder zu einer JComponent machen und die Koordinaten mittels eines MouseListeners innerhalb der Klasse Circles ermitteln..Ist meine Idee korrekt?

Vielen Dank in jedem Falle schon mal.
 
nein nein. den mouselistener hat in dem fall das panel. wenn du dir kreise zu komponemts machst, musst dudenen auch einen geben, da sie an ohren stellen den listener des panels überdecken, was nochmal umständlicher wär. das würde nur dann sinnachen, wenn du sie auch per ziehen bzw mouseDragged verschieben willst. wüsste gern nebenbei, wie es abstelle, dass eine komponente den listener einer darunter liegenden verdeckt. (tut sie doch oder?)

gruß thomas
 
Die Kreise sollten dann noch verschoben werden können. Dann brauche ich zwei MouseListener, einen für meine JComponent und einen für mein JPanel.
Wie ich das dann hinkriege weiß ich leider noch nicht.
 
Im Grund genommen braucht du nur einen Listener auf dem Panel. Der sollte, wenn du drauf klickst und einen Kreis "zur Seite schiebst" per Berechnung alles in mouseDragged erledigen 😀 Ich würde ehrlich gesagt, trotzdem ohne Komponente arbeiten, da das schnell Probleme geben könnte, beim Verschieben etc. Geh lieber zu eigenen simplen Klassen über. Da lernt man auch schnell was 🙂

Fang erst mal am besten an, deine Klasse iwie in so eine Überklasse zu stecken:

Java:
public abstract class Shape{        // Eigene Klasse (Vorsicht Namenszwilling von der richtigen Klasse Shape)
    public int x = 0;
    public int y = 0;
    
    public Shape(){
        this(0, 0);
    }
    
    public Shape(int x, int y){
        this.x = x;
        this.y = y;
    }

    public Rectangle getRectangle(){
        return new Rectangle( x - 10, y - 10, x + 10, y + 10);
    }
    
    public void paint(Graphics g){
        ((Graphics2D)g).setStroke(new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0));
        g.setColor(Color.black);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    }
}

Durch dieses abstrakte Klasse klassifizierst du schonmal deine beiden Zeichentypen. In denen Kindklassen (z.B. mit Namen Circle und Rectangle) muss du eig nur noch die paint-Methode überschreiben (+super.paint(g) aufrufen natürlich) und die entsprechende Zeichenart schreiben muss(drawRect(), drawEllipse()). Für ein kleines Problem, reicht dieser Aufbau locker. Wenn es aber professionell werden soll, kommen natürlich noch clone-, clear-, getter-, setter- und ähnliche Methoden dazu 😉 Die paint-Methoden rufst du dann in paintComponent vom JPanel auf und soweit wäre das schonmal gelöst.

Zum Verschieben: Im MouseListener des Panels könntest du nun, per
Code:
shape.getRectangle().contains(evt.get(), evt.getY())
rausfinden, ob die Maus darin ist und per Berechnung bei mouseDragged die Differenz nehmen und x/y entsprechend verändern. Das könnte ich dir ruhig geben, da ich denke, dass du es schnell verstehst. Wenn eigene Klassen zu umständlich sind, kannst du es auch mit den Klassen Rectangle und Ellipse2d.Double versuchen.

Ich hoffe, ich spreche nicht wieder zu kompliziert (ich schau nochmal alles kurz durch). Helfe dir gerne mit Code, da ich denke, dass du es nicht einfach kopierst, sondern auch anschaust und verstehst.

Gruß, Thomas

PS: Das hier behandelt erstmal nur die Ordnung der Komponenten und deren Zeichnung. Das er erstmal bei einem Klick ins leere da einfach eine neue Form erstellt, ist dann echt nur noch Pipifax 😛
 
Vielen Dank für die Hilfe. Das hat echt gut funktioniert.
Auf die Idee der abstrakten Klasse wäre ich nicht gekommen. Das dann abzuleiten und zu zeichnen ist dann ja einfach..
Zum Verschieben also dann am besten den MouseListener so adaptieren, dass ich prüfe, ob der Mousezeiger in einem Objekt ist, oder?

Gruss,
Andi
 
Genau. Also am besten in mousePressed in eine globale boolean-Variable per
Code:
Rectangle.contains(Point p)
bei Rechtecken speichern, bei Kreisen Entfernung messen mittels des Satzes des Pythagoras, um die Entfernung zum Mittelpunkt zu ermitteln.
Außer dieser boolean (könnte z.B.
Code:
boolean mouseInObject = false
heißen), brauchst du eine Point-Variable, die die Position bei mousePressed speichert (nennen wir sie mal
Code:
Point posA
), und bei mehreren Objekten brauchst du noch iwie einen Verweis auf das gewählte Objekt. Entweder speicherst du das Selektierte Teil "selbst" in einem Objekt deiner abstrakten Klasse Shape (nennen wir es mal
Code:
Shape choosedObject
) oder du speicherst eine Integer als Index, vorausgesetzt, du speicherst alle Objekte in einer ArrayList<Shape>. Dann brauchst du später nur
Code:
shapeList.get(choosedIndex)
aufrufen, um das selektierte Objekt zu bekommen 😉

Der zweite Schritt geschieht in mouseDragged, wo du dann zu Punkt posA die Differenz berechnest und entsprechend die Position des selektierten Objektes veränderst. Nicht vergessen, danach posA zu aktualisieren (mit dem neuen Punkt gleichsetzen).

Das ganze ist vll. ein bisschen zu genau und du hättest das warsch. genauso gut hinbekommen. Aber ich dachte, weil ich in meinem Programm auf diese Weise arbeite, spar ich damit deinen Grips 😉

Gruß, Thomas
 
Ich habe jetzt mal den ersten Schirtt nachvollzogen, und für die Objekt ne Array_Liste<Shape> angeleg. Über die Funktion addCircle werden jetzt neue Objekte eingefügt.
Der MouseListener mag nicht so. Nach der Definition des Punktes PosA iteriere ich über die ArrayList und lasse nach dem Punkt suchen, für den Objekt den Punkt enthält. Leider funktioniert die Abfrage mit contains() nicht. Hast Du ne Idee warum?

Meine aktuelle JPanel Syntax ist dies:

Java:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;

import javax.swing.JPanel;

public class MyPanel extends JPanel implements MouseListener{
	
	public ArrayList<Shape> Array_Shape; 
	public int x, y;
	Point A;
	
	MyPanel(){
		Array_Shape=new ArrayList<Shape>();
		addMouseListener(this);
	}
		
	@Override
	public void paintComponent(Graphics g){
		super.paintComponents(g);
		Graphics2D g2d=(Graphics2D) g;
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		Circle circ1=addCircle(40, 50);
		circ1.paint(g);
	}
	
	public Circle addCircle(int a, int b){
		Circle circ1=new Circle(a,b);
		Array_Shape.add(circ1);
		return circ1;
	}

	@Override
	public void mouseClicked(MouseEvent e) {
		A=new Point(e.getX(), e.getY());
		int chosenObject=0;
		for (Shape sh: Array_Shape){
			if (sh.contains(A)){
				chosenObject=indexOf(sh);
			}
		}
		
	}
}
Vielen Dank, Andi
 
Zuletzt bearbeitet von einem Moderator:
als erstes solltest du mal in paintComponent das addcircle entfernen. der erzeugt nun bei jedem neuzeichnen einen weiteren kreis. und glaub das wird unglaublic oft aufgerufen... ruf das lieber für sagen wir mal 2 objekte im konstruktor auf.
Außerdem solltest du mal zeigen wie bei dir contains aussieht, wenn du nun eine eigene klasse shape wirklich gemacht hast. und benutz mal lieber mousePressed ;-) und zum zeichnen musst du henau wie in deiner contains-abfrage eine for-schleife benutzen für jedes objekt
 

Zurück
Oben