MouseMotionListener - wie Mausverfolgung und Neuzeichnen realisieren?

Diskutiere MouseMotionListener - wie Mausverfolgung und Neuzeichnen realisieren? im Java Basics - Anfänger-Themen Bereich.
A

affot

Hallo,

ich wollte mir eben eine Art "Vektorfeld" erzeugen in dem irgendwelche Punkte, Linien (wie auch immer) der Maus folgen, d.h. in deren Richtung zeigen oder sie "umzingeln". Um das konkrete Aussehen soll es hier nicht gehen, deswegen nicht weiter präzisiert...
Da kam in mir die Frage auf, wie man sowas mit dem MouseMotionListener üblicherweise realisiert. Das Feld soll ja neu gezeichnet werden sobald sich die Maus bewegt.
Da allerdings im Gegensatz zu bestimmten Aktionen wie neuskalierung etc. eine Mausbewegung ja nicht automatisch repaint() aufruft war mein erster Ansatz in der main()-Methode repaint in einer Dauerschleife auszuführen und sich in der paintComponent-Methode jedes Mal die Mausposition zu besorgen. Das funktioniert auch aber erscheint mir in vielerlei Hinsicht als sehr unelegante Lösung.
Eine Idee wäre hier in einer Dauerschleife die Mausposition abzufragen und repaint aufzurufen, wenn sich die Mausposition im Vergleich zu ihrer vorherigen Position verändert hat. Ist das der normale Weg für so etwas, oder geht man hier normalerweise ganz anders vor?

Also meine Idee:
Java:
public class PositionDetector implements MouseMotionListener {
    int x, y, xOld, yOld;
    boolean positionChanged;

    @Override
    public void mouseMoved(MouseEvent e) {
        this.x = e.getX();
        this.y = e.getY();
        if (this.xOld != x || this.yOld != y) {
            this.positionChanged = true;
            System.out.println("Position changed");
            this.xOld = x;
            this.yOld = y;
        } else {
            positionChanged = false;
            System.out.println("---");
        }
    }
Zugegeben, auch das halte ich für vollkommen unelegant, da die mouseMoved Methode ja eigentlich genau das aufzeigt, da muss ich ja nicht nochmal prüfen ob die position eine neue ist... Allerdings wollte ich noch ein boolean flag erzeugen, mit dem ich von außen abfragen kann ob sich was getan hat.

Java:
public class MouseDetectionTest {

    public static void main(String[] args) {
        DetectionPanel detectionPanel = new DetectionPanel();
        JFrame f = new JFrame("Fenster");
        f.setLayout(new GridLayout(2, 2));
        f.add(detectionPanel);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLocation(200, 200);
        f.setSize(300, 300);
        f.setVisible(true);
        while (true) {
            if (detectionPanel.positionDetector.positionChanged == true) {
                detectionPanel.repaint();
            }
        }

    }

}
Der Übersichtlichkeit wegen habe ich jetzt die Klasse DetectionPanel, abgeleitet von JPanel weggelassen. Hier wird einfach ein Panel erzeugt, an diesem der Positiondetector (Typ MouseMotionlistener) angemeldet und die paintComponent(Graphics g) Methode definiert, die einfach abhängig von der Mausposition Elemente einzeichnet.
Mit der (denke ich) uneleganten Lösung (repaint() in Dauerschleife) funktioniert es im Prinzip auch, diese zweite Lösung hier funktioniert nicht und ich verstehe gar nicht warum.
In der Konsole wird mir angezeigt wenn ich die Maus bewege, wenn ich das Fenster neu skaliere, dann wird auch neu gezeichnet. Wenn ich aber die positionChanged Variable auslese und dann manuell repaint() aufrufe funktioniert es nicht... Kann mir jemand sagen warum es nicht funktioniert? Und außerdem wie man so ein Szenario üblicherweise realisiert, ohne dass sich einem Java Entwickler die Nackenhaare aufstellen? :)
 
A

affot

Verrückt, wenn ich vor die While Schleife ein Thread.sleep(1) einfüge, dann funktioniert es wie ich es mir gedacht habe - wieso das? Was macht das für einen Unterschied? 🤯
Java:
        while (true) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (detectionPanel1.positionDetector.positionChanged) {
                detectionPanel1.repaint();
            }
        }
 
Thallius

Thallius

Ganz einfach. Weil sonst deine Schleife ohne Unterbrechung läuft und der Thread welcher das Neuzeichnen macht überhaupt nicht dran kommt. Du must sowas immer asynchron machen
 
mihe7

mihe7

Da kam in mir die Frage auf, wie man sowas mit dem MouseMotionListener üblicherweise realisiert. Das Feld soll ja neu gezeichnet werden sobald sich die Maus bewegt.
Ich verstehe die Frage ehrlich gesagt nicht. Der Listener bekommt mit, wenn sich die Maus bewegt und führt aus, was gewollt ist, z. B. ein repaint().
 
A

affot

Ganz einfach. Weil sonst deine Schleife ohne Unterbrechung läuft und der Thread welcher das Neuzeichnen macht überhaupt nicht dran kommt. Du must sowas immer asynchron machen
Ok, angenommen ist habe nur eine scheinbare Parallelität dann macht das für mich Sinn. Aber ich habe einen Prozessor mit 8 Kernen - sollte es sich hier nicht um reale Parallelität handeln in der ich den main() Thread nicht manuell schlafen legen muss?
Und ist das etwa der übliche Weg für so etwas? Den Thread manuell schlafen zu legen? Gibt es da nichts eleganteres?
 
A

affot

Ich verstehe die Frage ehrlich gesagt nicht. Der Listener bekommt mit, wenn sich die Maus bewegt und führt aus, was gewollt ist, z. B. ein repaint().
Aber wie kann ich im MouseListener denn repaint() auf dem Panel aufrufen? Ich habe den Listener doch am Panel angemeldet, d.h. das Panel kennt den Listener aber nicht umgekehrt.
Müsste ich hierfür dem MouseListener einen Konstruktor hinzufügen der das Panel als Attribut hinzufügt, damit der panel.repaint() aufruft?
 
A

affot

Ja logisch :) ... So hab ichs jetzt umgesetzt und dann funktioniert es auch ohne diese hässliche While-Schleife in der main()-Methode die mir alles andere blockiert...
 
A

affot

Also auch wenn ich das ursprüngliche Problem gelöst habe, ich frage mich immer noch, wieso der Main Thread den Thread blockiert, welcher das Panel neuzeichnet, wenn die Threads doch eigentlich auf unterschiedlichen Kernen real parallel ablaufen können. Hat hier noch jemand eine Erklärung parat? Wo liegt mein Denkfehler?
 
T

thecain

Dein Denkfehler liegt darin, dass du denkst, das du mehrere Threads verwendest
 
mihe7

mihe7

Verwendet er ja auch: main-Thread und EDT.

Ich vermute ein anderes Problem: Cache. Die Variable positionChanged ist nicht volatile, so dass ggf. alte Werte gelesen werden.
 
Thallius

Thallius

Verwendet er ja auch: main-Thread und EDT.

Ich vermute ein anderes Problem: Cache. Die Variable positionChanged ist nicht volatile, so dass ggf. alte Werte gelesen werden.
wenn der Main Thread in einer Endlosschleife steckt, dann kommt der edt aber eben nicht dran. Diese Wunschvorstellung von Java macht das dann schon wenn man mehrere Prozessoren haLate ich für ein Gerücht.
 
mihe7

mihe7

wenn der Main Thread in einer Endlosschleife steckt, dann kommt der edt aber eben nicht dran. Diese Wunschvorstellung von Java macht das dann schon wenn man mehrere Prozessoren haLate ich für ein Gerücht.
Der EDT muss drankommen, sonst würde das Fenster nach einer Größenänderung nicht neu gezeichnet.
 
A

affot

Tatsächlich, volatile funktioniert... Ist absolut logisch, aber da wäre ich jetzt echt nicht drauf gekommen, Danke dir! :)
 
Thema: 

MouseMotionListener - wie Mausverfolgung und Neuzeichnen realisieren?

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben