JPanel wird versetzt auf JFrame gelegt

Sessiolog

Mitglied
Grüße!

Mich plagt folgendes kleines Problem: Ich möchte ein JPanel erzeugen welches als bemalbare Fläche auf ein JFrame gelegt wird. Ein Rechteck wird auf das Panel gezeichnet. Die StandartFarbe des Panels ist grün, sollte sich die Maus jedoch im Rechteck befinden, wird der Hintergrund rot gefärbt. Eigentlich ganz einfach, das Problem ist nur, dass die Maus-Rechteck "Kollision" viel zu spät erkannt wird, also erst, wenn die Maus fast schon in der Mitte des Rechteckes ist, wobei das doch eigentlich auf den Pixel genau passieren sollte. Das wird wohl an den Insets des Frames liegen, und an der Stelle komme ich nicht so ganz weiter. Vielleicht könnt ihr mir ja helfen, das JPanel mitsamt Rechteck korrekt zu platzieren? Ich bedanke mich im Voraus. 🙂

Java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.Graphics.*;
import java.util.*;

public class ButtonHighlight extends JFrame {
	boolean isExit = false;
	Insets ins;
	BPanel panel;
	Rectangle boundings;
	static Dimension dimension;
	
	public ButtonHighlight() {
		super("Fenster");
		setBounds(0,0,400,600);
		setVisible(true);
		boundings = new Rectangle(50,50,200,100);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		dimension = calcSizes();
		panel = new BPanel(this);
		add(panel);
		setVisible(true);
		
		addMouseMotionListener(new MouseMotionAdapter() {
			public void mouseMoved(MouseEvent e) {
				checkMove(e.getX(), e.getY());
			}
		});
	}

	public Dimension calcSizes() {
		ins = this.getInsets();
		int fWidth = getWidth() - (ins.left + ins.right);
		int fHeight = getHeight() - (ins.top + ins.bottom);
		//System.out.println(ins);
		return new Dimension (fWidth,fHeight);
	}

	public void checkMove(int x, int y) {
		isExit = boundings.contains(x,y) ? true:false;
	}

	public static void main (String [] args) {
		ButtonHighlight b = new ButtonHighlight();
	}
}

class BPanel extends JPanel {
	ButtonHighlight bHighlight;
	
	public BPanel(ButtonHighlight b) {
		this.bHighlight = b;
		setPreferredSize(new Dimension (ButtonHighlight.dimension));
	}
	
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D) g;
		g2.draw(bHighlight.boundings);
		if (!bHighlight.isExit) {
			g2.setColor(Color.GREEN);
			g2.fillRect(0,0,getWidth(), getHeight());
		} else {
			g2.setColor(Color.RED);
			g2.fillRect(0,0,getWidth(), getHeight());
			}
			
		g2.setColor(Color.BLACK);
		g2.draw(bHighlight.boundings);
		repaint();
	}
}
 
Was genau ist denn dein Problem? Ich hab das so verstanden, dass die Kollision von Rechteck und Mauszeiger erst erkannt wird, wenn der Zeiger fast in der Mitte des Rechtecks ist.
Um dein Problem zu lösen willst du nun dein Rechteck an die Kollision anpassen?

Ehrlich gesagt wundert es mich, dass das Ganze überhaupt funktioniert, denn du hast das GUI nicht in den InvokeLater eingebaut, womit es in den eigentlichen GUIthread versetzt würde. Swing verbietet es anderen Threads die Components zu verändern.

Bei mir sieht die Main Methode immer so aus:

Java:
public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				testFrame thisClass = new testFrame();
				thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				thisClass.setVisible(true);
			}
		});
	}


Dann müsstest du auch bei jeder Bewegung der Maus überprüfen, ob sie sich im Rechteck befindet und du müsstest ebenfalls überprüfen, ob ein Malen notwendig ist. Also paintComponent() aufrufen und nicht so, wie du es momentan hast, eine Endlosschleife erzeugen.
Die Endlosschleife kommt daher, dass du am ende von paintComponent() repaint() aufrufst, welches wieder paintComponent ausführt.

Ich würde es so machen, dass du das Panel das Interface MouseAdapter implementieren lässt und dann das Panel über jede Änderung der Maus informieren lässt, indem du es als MouseListener hinzufügst.

Außerdem wird die Hintergrundfarbe mit setBackground(Color) geändert und nicht mit fillRect(Rectangle). Das aber nur am Rande. 😉
 
Zuletzt bearbeitet:
Ich habe das gemacht, was du gesagt hast. Also das JFrame mit invokeLater(...) aufgerufen, die mouseMoved-Logik in die JPanel Klasse verschoben, und die Hintergrund Farbe mit setBackground(...) anstatt mit fillRect(...) verändert. Funktioniert so wie es soll. Insets subtrahieren und Dimensions erzeugen brauche ich nicht. Liegt vielleicht daran, dass der Code den ich schreiben wollte, aus einem ca.9 Jahre altem, nie aktualisiertem Buch stammt. Also aus einer Zeit, in der das alles wohl noch notwendig war.
Aus Gründen der Vollständigkeit, hier der funktionierende Code:

Java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.Graphics.*;
import java.util.*;

public class ButtonHighlight extends JFrame {
	BPanel panel;
	
	public ButtonHighlight() {
		super("Fenster");
		setBounds(0,0,400,600);
		add(new BPanel(this));
		setVisible(true);
	}

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				ButtonHighlight b = new ButtonHighlight();
				b.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				b.setVisible(true);
			}
		});
	}
}

class BPanel extends JPanel {
	ButtonHighlight bHighlight;
	boolean isExit = false;
	Rectangle boundings = new Rectangle(50,50,200,200);
	
	public BPanel(ButtonHighlight b) {
		this.bHighlight = b;
		
		addMouseMotionListener(new MouseMotionAdapter() {
			public void mouseMoved(MouseEvent e) {
				isExit = boundings.contains(e.getX(), e.getY()) ? true:false;
			}
		});
	}

	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D) g;
		g2.draw(boundings);
		if (!isExit) {
			setBackground(Color.GREEN);
		} else {
			setBackground(Color.RED);
			}
			
		g2.setColor(Color.BLACK);
		g2.draw(boundings);
		repaint();
	}
}

Danke für die Hilfe, Gucky! 🙂
 

Zurück
Oben