Sauberes Faden mit awt Graphics

javampir

Bekanntes Mitglied
Guten Tag alle zusammen,
hab mir jetzt in den Kopf gesetzt, meine Programme ein bisschen dynamischer aussehen zu lassen. Ich arbeite hierbei viel mit awt Graphics und möchte Farbübergänge (z.B. wenn der user mit der maus über ein Element geht) machen. Mein Testcode schaut so aus:
Java:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Gui extends JPanel implements MouseMotionListener {
    
    public static void main(String[] args) {
        new Gui();
    }
    
    JFrame f = new JFrame("Fade");
    int x = 20, y = 20, w = 100, h = 100;
    volatile Color col = Color.black;
    boolean in = false;
    
    public Gui() {
        super();
        setBackground(Color.white);
        addMouseMotionListener(this);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.setExtendedState(JFrame.MAXIMIZED_BOTH);
        f.setVisible(true);
    }
    
    private boolean over(int ix, int iy) {
        if(ix >= x && ix <= x + w && iy >= y && iy <= y + h) {
            return true;
        }
        return false;
    }
    
    private void fadeIn() {
        new Thread(new Runnable() {
            @Override
            public synchronized void run() {
                if(!in) {
                    int r = 0;
                    for(int i = 0; i < 127; i++) {
                        r += 2;
                        col = new Color(r, 0, 0);
                        repaint();
                        try {
                            Thread.sleep(30);
                        } catch (Exception ex) {}
                    }
                    in = true;
                }
            }
        }).start();
    }
    
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(col);
        g.fillRect(x, y, w, h);
    }
    
    @Override
    public void mouseMoved(MouseEvent e) {
        if(over(e.getX(), e.getY())) {
            fadeIn();
        }
    }
    
    @Override
    public void mouseDragged(MouseEvent e) {
        mouseMoved(e);
    }
}

allerdings hat man ein sehr unruhiges Flackern wenn man mit der Maus über das Element geht. Es sieht immer so aus, wie wenn es kurz dunkler werden würde, bevor es wieder hell wird.
Liegt es einfach an meiner sleep-zeit und meiner step-größe, hab ich irgendeinen groben performance-schnitzer oder gehts einfach nicht besser?
Gruß,
javampir
 
Hallo javampir,

so wie es aussieht startest du immer einen neuen thread, solange deine variable in noch nicht auf true gesetzt ist.
in wird jedoch erst auf true gesetzt, wenn der farb-setz-thread die gesamte for-schleife durchlaufen hat.
D.h., dass du jedes mal einen neuen thread startest, solange der erste thread noch nicht fertig ist, und jeder dieser gestarteten threads startet wieder bei der ersten farbe des fade-in vorgangs.

quick fix:

in = true;

bevor die for-schleife durchlaufen wird.

hoffe ich konnte dir helfen!
 
Die Erklärung von Qler ist richtig, die Lösung gefällt mir nicht. Synchronisation ist teuer. Java unterstützt out of the box nur eine Maus, deshalb (und aufgrund ein paar anderen Dingen) wird die Methode mouseMoved niemals parallel ausgeführt. Folgende Lösung ist deutlich besser:

Java:
@Override
public void mouseMoved(MouseEvent e) {
  if (over(e.getX(), e.getY()) && !in) {
    in = true;
    fadeIn();
  }
}

private void fadeIn() {
  new Thread(new Runnable() {
    @Override
    public void run() {
      int r = 0;
      for (int i = 0; i < 127; i++) {
        r += 2;
        col = new Color(r, 0, 0);
        repaint();
        try {
          Thread.sleep(30);
        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }
    }
  }).start();
}
 
Zuletzt bearbeitet von einem Moderator:
hi,
danke für eure Antworten, aber so richtig schön sieht es erst aus, wenn ich r immer nur um 1 erhöhe und dann 30ms warte (das ist zu lang, die animation ist dann länger als 7650ms = über siebeneinhalb sekunden). Ansonsten ist das problem wie vorher.
javampir
 
Dass der Fehler weiterhin auftritt ist ausgeschlossen. Nochmal der Code ...

Java:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Gui extends JPanel implements MouseMotionListener {

	public static void main(String[] args) {
		new Gui();
	}

	JFrame f = new JFrame("Fade");
	int x = 20, y = 20, w = 100, h = 100;
	volatile Color col = Color.black;
	boolean in = false;

	public Gui() {
		super();
		setBackground(Color.white);
		addMouseMotionListener(this);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.add(this);
		f.setExtendedState(JFrame.MAXIMIZED_BOTH);
		f.setVisible(true);
	}

	private boolean over(int ix, int iy) {
		if (ix >= x && ix <= x + w && iy >= y && iy <= y + h) {
			return true;
		}
		return false;
	}

	@Override
	public void mouseMoved(MouseEvent e) {
		if (over(e.getX(), e.getY()) && !in) {
			in = true;
			fadeIn();
		}
	}

	private void fadeIn() {
		new Thread(new Runnable() {
			@Override
			public void run() {
				int r = 0;
				for (int i = 0; i < 127; i++) {
					r += 2;
					col = new Color(r, 0, 0);
					repaint();
					try {
						Thread.sleep(30);
					} catch (Exception ex) {
						ex.printStackTrace();
					}
				}
			}
		}).start();
	}

	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		g.setColor(col);
		g.fillRect(x, y, w, h);
	}

	@Override
	public void mouseDragged(MouseEvent e) {
		mouseMoved(e);
	}
}

Meine Intention war es nicht die Animation zu ändern ... wenn sie dir zu lange dauert, musst du sie ändern :bahnhof:
 
hi,
ja, danke soweit hatte ich es verstanden und auch schon abgeändert. aber wie gesagt, es sieht immer noch unruhig aus, also etwas abgehackt und stufenweise. wenn ich die sleep-zeit heruntersetze, schmeißt er wahrscheinlich ein paar repaints raus, und wenn ich r nicht um 1 oder 2, sondern um, sagen wir mal 4, erhöhe hat man ein unangenehmes Rucken drin. Vielleicht wirkt es auch nur so, weil es eine relativ große zusammenhängende fläche ist und es würde bei linien nicht auffallen, aber schön sieht es trotzdem nicht aus.
Wenn noch jemand eine idee hat...
javampir
 

Zurück
Oben