Objekte mit gedrückter Maustaste verschieben

Hallo,

ich hab da ein Problem mit dem verschieben von Grafischen Objekten. Und zwar füge ich per Klick mehrere Kreise einem Panel hinzu, nun möchte ich alle Kreise bei gedrückter Maustaste verschieben. Dazu berechne ich die Differenz der Mauskoordinaten und aktualisiere damit die Koordinaten der Kreise. Zumindest glaub ich das ich das mach, bin ich mir mittlerweile gar nicht mehr so sicher.
In der X-Achse funktioniert es soweit auch außer das wenn ich einen neuen Kreis hinzufüge, dieser beim verschieben mit allen anderen Kreisen an der Vertikalen ausgerichtet wird. Wenn ich die Punkte der Y-Achse aktualisiere und einen Kreis verschiebe, fallen die Kreise zusammen.

Kann sich das jemand mal ansehen und mir weiter helfen, denn ich komme nicht mehr voran und so langsam schleicht sich die Demotivation ein.

import java.awt.*;
import java.awt.event.*;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Ellipse2D.Double;
import javax.swing.*;

public class MoveAllTheCircle extends MouseAdapter
{
    private Point[] points = new Point[50];
    private int usedPoints = 0;

    CirclePanel circlePanel = new CirclePanel();

    public static void main(String[] arg)
    {
        SwingUtilities.invokeLater( new Runnable()
        {
            @Override
            public void run()
            {
                new MoveAllTheCircle();
            }
        });
    }
    
    public MoveAllTheCircle()
    {
        JFrame frame = new JFrame("MovingCircle3");
        frame.setSize(new Dimension(300,400));
        frame.setLocation(250,250);
        frame.setResizable(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        Container content = frame.getContentPane();

        circlePanel.setBackground(Color.gray);
        circlePanel.setFocusable(true);
        circlePanel.addMouseListener(this);
        circlePanel.addMouseMotionListener(this);

        content.add(circlePanel);
        frame.setVisible(true);
    }
    
    public void mousePressed(MouseEvent evt)
    {
        if(usedPoints < points.length)
        {
            points[usedPoints] = evt.getPoint();
            ((CirclePanel)evt.getSource()).repaint();
            usedPoints++;
        }
    }
    
    public void mouseDragged(MouseEvent evt)
    {
        int x, y;
        x = evt.getX();
        y = evt.getY();

        for(int i = 0; i < usedPoints; i++)
        {
            points**.x = (x - points**.x) + points**.x;
            //points**.y = (y - points**.y) + points**.y;
        }
        circlePanel.repaint();
    }

    class CirclePanel extends JPanel
    {
        public CirclePanel()
        {
            repaint();
        }
        public void paintComponent(Graphics circle)
        {
            super.paintComponent(circle);
            Graphics2D circle2d = (Graphics2D) circle;
            circle2d.setColor(new Color(255,0,0));
            for(int i = 0; i < usedPoints; i++)
            {
                Point point = points**;
                circle2d.fill(new Ellipse2D.Double(point.x-10,point.y-10,20,20));
            }
        }
    }
}

Evtl. hilfts schon, wenn du statt “irgendwas.repaint()” schlicht “repaint()” oder “frame.repaint()” schreibst.

points**.x = (x - points**.x) + points**.x;

so eine Zeile ist … erstmal schlecht.


import java.awt.*;
import java.awt.event.*;
import java.awt.Graphics2D;
import javax.swing.*;

public class MoveAllTheCircle extends MouseAdapter {

	private Point[] points = new Point[50];
	private int usedPoints = 0;

	private CirclePanel circlePanel = new CirclePanel();

	private Point lastPos = null;

	private final int RADIUS = 10;

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

	public MoveAllTheCircle() {
		JFrame frame = new JFrame("MovingCircle3");
		frame.setSize(new Dimension(300, 400));
		frame.setLocation(250, 250);
		frame.setResizable(true);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		Container content = frame.getContentPane();

		circlePanel.setBackground(Color.gray);
		circlePanel.setFocusable(true);
		circlePanel.addMouseListener(this);
		circlePanel.addMouseMotionListener(this);

		content.add(circlePanel);
		frame.setVisible(true);
	}

	public void mousePressed(MouseEvent evt) {
		//Ggf. Verbesserung, indem index gespeichert wird und in mouseDragged genutzt wird. 
		int index = getIndexOfNearestPoint(evt.getPoint());
		if (index != -1 && evt.getButton() == MouseEvent.BUTTON3) {
			lastPos = evt.getPoint();
		} else if (evt.getButton() == MouseEvent.BUTTON1
				&& usedPoints < points.length) {
			points[usedPoints] = evt.getPoint();
			circlePanel.repaint();
			usedPoints++;
		}
	}

	public void mouseReleased(MouseEvent evt) {
		lastPos = null;
	}

	public int getIndexOfNearestPoint(Point p) {
		if (usedPoints == 0)
			return -1;

		int minIndex = 0;
		for (int i = 1; i < usedPoints; i++) {
			if (p.distance(points**) < p.distance(points[minIndex])) {
				minIndex = i;
			}
		}
		if (p.distance(points[minIndex]) > RADIUS) {
			return -1;
		}

		return minIndex;
	}

	public void mouseDragged(MouseEvent evt) {
		if (lastPos != null) {

			Point current = evt.getPoint();

			int dx = lastPos.x - current.x;
			int dy = lastPos.y - current.y;

			int movIndex = getIndexOfNearestPoint(lastPos);
			lastPos = current;
			Point lastPos = points[movIndex];
			points[movIndex] = new Point(lastPos.x - dx, lastPos.y - dy);

			circlePanel.repaint();
		}
	}

	class CirclePanel extends JPanel {
		
		private static final long serialVersionUID = 1L;

		public void paintComponent(Graphics g0) {
			super.paintComponent(g0);
			Graphics2D g = (Graphics2D) g0;
			g.setColor(new Color(255, 0, 0));
			for (int i = 0; i < usedPoints; i++) {
				Point point = points**;
				/*
				 * g.fill(new Ellipse2D.Double(point.x - RADIUS, point.y -
				 * RADIUS, RADIUS*2, RADIUS*2));
				 */
				g.fillOval(point.x - RADIUS, point.y - RADIUS, RADIUS * 2,
						RADIUS * 2);
			}
		}
	}
}```

Die serialVersionUID hab ich immer drinne, weil ich ungern Code poste, indem "Warnungen" (von Eclipse) angezeigt werden. 

Die paintComponent-Methode hab ich n bisschen verändert, da die Objekterzeugung an der Stelle nicht sein muss. Eine weitere Verbesserungsmöglichkeit des Codes steht in dem Kommentar. 

Etwas unklar?

Ja, Sunshine hat das wichtigste schon gepostet, aber ich hatte schon angefangen, das mit möglichst wenigen Änderungen zu dem zu machen, was du wohl wolltest:

package tests;
import java.awt.*;
import java.awt.event.*;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Ellipse2D.Double;
import javax.swing.*;

public class MoveAllTheCircle extends MouseAdapter
{
    private Point[] points = new Point[50];
    private int usedPoints = 0;

    CirclePanel circlePanel = new CirclePanel();
    
    private Point lastMousePosition = new Point();

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

    public MoveAllTheCircle()
    {
        JFrame frame = new JFrame("MovingCircle3");
        frame.setSize(new Dimension(300,400));
        frame.setLocation(250,250);
        frame.setResizable(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       
        Container content = frame.getContentPane();

        circlePanel.setBackground(Color.gray);
        circlePanel.setFocusable(true);
        circlePanel.addMouseListener(this);
        circlePanel.addMouseMotionListener(this);

        content.add(circlePanel);
        frame.setVisible(true);
    }

    public void mousePressed(MouseEvent evt)
    {
        if(usedPoints < points.length)
        {
            points[usedPoints] = evt.getPoint();
            ((CirclePanel)evt.getSource()).repaint();
            usedPoints++;
        }
        lastMousePosition = evt.getPoint();
    }

    public void mouseDragged(MouseEvent evt)
    {
        int dx = evt.getX() - lastMousePosition.x;
        int dy = evt.getY() - lastMousePosition.y;
        for(int i = 0; i < usedPoints; i++)
        {
            points**.x += dx;
            points**.y += dy;
        }
        circlePanel.repaint();

        lastMousePosition = evt.getPoint();
    }

    class CirclePanel extends JPanel
    {
        public CirclePanel()
        {
            repaint();
        }
        public void paintComponent(Graphics circle)
        {
            super.paintComponent(circle);
            Graphics2D circle2d = (Graphics2D) circle;
            circle2d.setColor(new Color(255,0,0));
            for(int i = 0; i < usedPoints; i++)
            {
                Point point = points**;
                circle2d.fill(new Ellipse2D.Double(point.x-10,point.y-10,20,20));
            }
        }
    }
}

Weitere Punkte gäb’s sicher, z.B. schon eine List statt eines Point[] array und so, aber … soweit erstmal…

Achso, dachte nur der Nähste.

Danke für die schnellen Antworten und Lösungen.

@Sunshine
Warum ist diese Zeile schlecht?
points**.x = (x - points**.x) + points**.x;

da die Objekterzeugung an der Stelle nicht sein muss

Da magst du sicher Recht haben da es mit fillOval auch funktioniert, aber leider bin ich an einer Aufgabenstellung gebunden und dort heißt explizit “Verwenden Sie Ellipse2D.Double um Kreise als Objekte zu erzeugen.”

@Marco13
Warum ist eine List besser geeignet als ein Point Array?
Ich sehe jetzt kein Offensichtlichen Grund dafür. Ich will ehrlich sein ich hab mit List bisher auch noch nicht gearbeitet, ich habe immer Ein- oder Zweidimensionale Arrays verwendet. Point Array war diesmal auch erst die erste Verwendung.

Nochmals Vielen Dank für die schnellen Lösungen, ich werde mich da mal durchwühlen und wenn noch Fragen sind dann melde ich mich wieder.

Na, das ist keine Verschiebung sondern mehr ein … “setzen”. Die Klammer darf ja ignoriert werden, also
points**.x = (x - points**.x) + points**.x; == points**.x = x;

[QUOTE=t_2;26333]
Da magst du sicher Recht haben da es mit fillOval auch funktioniert, aber leider bin ich an einer Aufgabenstellung gebunden und dort heißt explizit “Verwenden Sie Ellipse2D.Double um Kreise als Objekte zu erzeugen.”[/QUOTE]

Okay, ggf. könnte sich dann ein Array bzw. eine List aus Ellipse2D.Double anbieten?

Da sagtest glaube ich auch schon im vorherigen Thread, dass das die Aufgabenstellung verlangt, aber nur zur Vollständigkeit:

Man hat einfach diese “Krücke” mit pointsUsed nicht mehr drin und man hat eben eine dynamische Größe.

Listen sind relativ einfach zu benutzen:
Erstellen: List<Point> pointList = new ArrayList<Point>();
Element hinzu: pointList.add(new Point(5,1));
Element löschen: pointList.remove(aPoint);
Auf n. Element zugreifen: pointList.get(n)
aktuelle Größe: pointList.size()

Ja, das war schon das wichtigste zur Liste - allgemein sind Listen oft praktischer als Arrays, weil man die gesamte Infrastruktur des Collections-Frameworks drauf loslassen kann, aber das nur nebenbei.

Auch nur nebenbei: Diese Objekterzeugungen sind unproblematisch. Zugegeben, ich bin auch noch in der Java 1.3-Denke, und versuche gelegentlich, Objekterzeugungen zu vermeiden, und finde auch, man sollte zumindest drüber nachgedacht haben, aber … : Durch die Escape-Analysis und Method Inlining sind solche Objekterzeugungen wie die der Ellipse2D.Double heute absolut kein Problem mehr. Selbst ziemlich “zeitkritische” Zeichen-Bibliotheken verwenden das so (statt irgendwelcher u.U. ziemlich krampfiger Krücken, wie alle Shapes in Listen zu speichern o.ä.).