MouseMotionListener - wie Mausverfolgung und Neuzeichnen realisieren?

affot

Mitglied
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? 🙂
 
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();
            }
        }
 
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
 
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?
 
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?
 
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...
 
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?
 
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.
 

Neue Themen


Zurück
Oben