# paintComponent überdeckt ...



## AEQUITAS (24. Sep 2009)

Hallo zusammen,

ich gebe die google-Suche und hier die Forensuche auf.

Ich suche seit mehreren Tagen nach einer Lösung. Und zwar geht es um die paintComponent Methode bei JComponents, speziell bei mir bei JMenuBar.

Und zwar will ich zum Test erstmal ein GradientPaint als Background einfügen, statt der Standardfarben. Wenn ich die paintComponent-Methode jedoch entsprechend überschreibe, wird das JMenu, was geadded wird nicht mehr anzeigt, nur wenn man mit der Maus drüber fährt, sprich es ist verdeckt.

Das gleiche passiert, wenn ich die paintComponent-Methode bei einem JTextField überschreibe. Hier wollte ich auch einen GradientPaint in den Background zeichnen, jedoch wird dann der Input und der Maustextcursor dadrin nicht mehr angezeigt.

Anbei der Code.

Ich hoffe, Ihr könnt mir weiterhelfen, danke schon mal!

Auszug aus der View:

```
JMenuBar topMenuBar = new GradientTopBar();
	    
	    // Create menuButton - File
	    JMenu menuFile = new JMenu("Datei");
	    menuFile.setBackground(Color.white);
	    
	    // Create menuItem
	    JMenuItem itemSettings = new vMenuItem("Einstellungen", "graphics/iconsTopMenu/menuSettings.png");
	    JMenuItem itemExit = new vMenuItem("Beenden", "graphics/iconsTopMenu/menuExit.png");
	    
	    menuFile.add(itemSettings);
	    menuFile.add(itemExit);
	    topMenuBar.add(menuFile);
```

Die Komponentenklasse:

```
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JMenuBar;

public class GradientTopBar extends JMenuBar {
	
	private Color startColor;
	private Color endColor;

	public GradientTopBar() {
		super();
		startColor = Color.white;
		endColor = Colors.Gray.color();
	}
	
	public GradientTopBar(Color startColor, Color endColor) {
		super();
		this.startColor = startColor;
		this.endColor = endColor;
	}

	public void setStartColor(Color startColor) {
		this.startColor = startColor;
	}

	public void setEndColor(Color endColor) {
		this.endColor = endColor;
	}

	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		GradientPaint gradientPaint = new GradientPaint( 0 , 0 , startColor , 0, super.getHeight(), endColor );
				
		Graphics2D g2d = (Graphics2D) g;
		g2d.setPaint(gradientPaint);
		g2d.setColor(Color.red);
		g2d.drawRect(0, 0, super.getWidth(), super.getHeight());
		g.dispose();
	}
}
```


----------



## KrokoDiehl (24. Sep 2009)

Hi.
Ich hab deinen Code nicht getestet, aber was mir direkt auffiel:

```
@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		GradientPaint gradientPaint = new GradientPaint( 0 , 0 , startColor , 0, super.getHeight(), endColor );
				
		Graphics2D g2d = (Graphics2D) g.create(); // ÄNDERUNG
		g2d.setPaint(gradientPaint);
		g2d.setColor(Color.red);
		g2d.drawRect(0, 0, super.getWidth(), super.getHeight());
		g.dispose();
	}
```
Vielleicht lags ja nur daran  Ansonsten sehe ich keine Absonderheit.


----------



## AEQUITAS (24. Sep 2009)

Das gibt es doch gar nicht ... daran lag es tatsächlich :lol: DANKE!!!!!!

Falls mir jetzt noch jemand sagen kann, wie man die Höhe der Leiste verändert bin ich restlos glücklich. ;-)

// Edit

Habe es mal schnell beim JTextField geändert, jedoch wird da der Zeiger und der Input immer noch nicht angezeigt. Bitte um Hilfe.

Hier der Codeauszug:


```
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JTextField;
import javax.swing.border.LineBorder;

public class DialogTextField extends JTextField {

	public DialogTextField() {
		super();
		super.setPreferredSize(new Dimension(340,20));
		super.setFont(new Font("sansserif", Font.PLAIN, 13));
		super.setForeground(new Color(80, 80, 80));
		super.setBackground(Color.white);
		super.setBorder(new LineBorder(Color.white));
		super.setOpaque(true);
		super.setEditable(true);
	}
	
	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
	    int panelHeight = super.getHeight();
		int panelWidth = super.getWidth();
		GradientPaint gradientPaint = new GradientPaint( 0 , 0 , Color.white, 0 , panelHeight , Color.LIGHT_GRAY);
		Graphics2D graphics2D = (Graphics2D) g.create();
		graphics2D.setPaint(gradientPaint);
		graphics2D.fillRect(0 , 0, panelWidth, panelHeight);
		graphics2D.dispose();
	}
	
}
```


----------



## Michael... (24. Sep 2009)

Wundert mich, dass es beim Menü funktioniert. Hätte schon erwartet, dass, wenn man etwas mit einem bunten Rechteck übermalt, man eben genau dieses Rechteck sieht und nicht dass was dahinter steckt.
Ich würde statt dessen ein JPanel nehmen, den Farbverlauf auf des Panel zeichnen und dann ein durchsichtiges JTextField auf dieses Panel setzen.


----------



## KrokoDiehl (24. Sep 2009)

Zum Thema Größe: 
	
	
	
	





```
setPreferredSize()
```
 hilft sehr oft. Bei wirklich eigenen Dingen kann man auch 
	
	
	
	





```
getPreferredSize()
```
 überschreiben.

Bei den anderen weiß ich nicht, was du mit "Zeiger" und "Input" meinst...


----------



## AEQUITAS (24. Sep 2009)

KrokoDiehl hat gesagt.:


> Zum Thema Größe:
> 
> 
> 
> ...


Mit Zeiger meine ich die Linie die aufblingt, wenn du dich in einem Textfeld oder hier im Formular befindest und mit Input der Text der im Textfield steht. Der wird einfach überdeckt. Das mit den JPanels wäre eventuell eine Möglichkeit, ich finds aber irgendwie unschön.

Also wenn ich ein JTextField neu Zeichne, dann "male" ich automatisch über den Text?


----------



## Michael... (24. Sep 2009)

AEQUITAS hat gesagt.:


> Also wenn ich ein JTextField neu Zeichne, dann "male" ich automatisch über den Text?


Wenn Du super.paintComponent(g) aufrufst wird zeichnet zunächst mal die Superklasse, im Falle eines Textfelds vermutlich erst den Hintergrund, dann den Text, ... wie das mit dem blinkenden Cursor geregelt ist - keine Ahnung. Und wenn Du jetzt auf dem Graphikobjekt weiter zeichnest wird das was bisher gezeichnet wurde übermalt. Das was zuletzt gezeichnet wird ist im "Vordergrund" (sonst würde das ganze ja keinen Sinn machen) - Du aber willst ja nur den Hintergrund ändern.


----------



## KrokoDiehl (24. Sep 2009)

Hm.. hat _JTextField _ein
	
	
	
	





```
paintChildren()
```
? Falls ja, kannst du das NACH deinem Zeichen aufrufen. Ich erinnere mich dunkel daran, dass das bei mir mal geholfen hat.

Aber Michael... hat da schon recht, du musst was an der Reihenfolge tun.

Der "Zeiger" heißt übrigens Cursor oder Caret (in Swing) *klugscheiss*


----------



## André Uhres (25. Sep 2009)

KrokoDiehl hat gesagt.:


> Hm.. hat _JTextField _ein
> 
> 
> 
> ...


Es ist sinnlos, paintChildren innerhalb von paintComponent aufzurufen. Vom System wird immer paint() aufgerufen, wenn es Zeit ist darzustellen. Swing ruft dann innerhalb von paint() folgende Methoden auf, und zwar in der angegebenen Reihenfolge: 

```
protected void paintComponent(Graphics g)
    protected void paintBorder(Graphics g)
    protected void paintChildren(Graphics g)
```
Wie man sieht, wird paintChildren sowieso immer erst nach paintComponent aufgerufen!

Beim JTextField können wir allerdings nicht paintComponent überschreiben, sondern wir müssen mit setUI() einen eigenen Look and Feel Delegate setzen, etwa so:

```
setUI(new BasicTextFieldUI() {
    @Override
    protected void paintBackground(Graphics g) {
        super.paintBackground(g);
        int panelHeight = DialogTextField.super.getHeight();
        int panelWidth = DialogTextField.super.getWidth();
        GradientPaint gradientPaint = new GradientPaint(0, 0, Color.white, 0, panelHeight, Color.LIGHT_GRAY);
        Graphics2D graphics2D = (Graphics2D) g;
        graphics2D.setPaint(gradientPaint);
        graphics2D.fillRect(0, 0, panelWidth, panelHeight);
    }
});
```

Der ursprüngliche Fehler hängt übrigens mit [c]g.dispose();[/c] zusammen. Wenn wir nur casten [c]Graphics2D g2d = (Graphics2D) g;[/c], dann müssen wir [c]g.dispose();[/c] einfach weglassen. Falls wir mit [c]Graphics2D g2d = (Graphics2D) g.create();[/c] ein neues Graphics Objekt anlegen, dann müssen dieses neue Objekt mit [c]g2d.dispose();[/c] freigeben, wenn wir es nicht mehr brauchen (um Speicherplatz zu sparen).



Michael... hat gesagt.:


> Wundert mich, dass es beim Menü funktioniert. Hätte schon erwartet, dass, wenn man etwas mit einem bunten Rechteck übermalt, man eben genau dieses Rechteck sieht und nicht das was dahinter steckt.


"das was dahinter steckt" sind in diesem Fall die Kindkomponenten. Sie werden in paintChildren() gemalt. Wegen der oben gezeigten Reihenfolge der drei paintXXX() Methoden, stehen die Kindkomponenten immer im Vordergrund! Beim JTextField können wir es aber nicht auf die gleiche Art machen, weil keine Kindkomponenten da sind.
Siehe auch Malen in AWT und Swing - Byte-Welt Wiki


----------



## KrokoDiehl (25. Sep 2009)

Ah, gute Erklärung. Danke auch von mir


----------



## AEQUITAS (25. Sep 2009)

Wow, vielen Dank für die sehr gute Erklärung!!!

Eine Frage habe ich nun aber noch. Ist es sinnvoller beim Gestalten einer GUI eher die UI (also das Look&Feel) der einzelnen Komponenten zu ändern oder die einzelnen Komponenten zu Erben und eigene Klassen zu machen, wie ich das jetzt bei der JMenuBar gemacht habe?

Wenn ich sowas nun mache, dann möchte ich das natürlich möglichst "richtig" machen, wenn es in dem Fall überhaupt ein richtig oder falsch gibt.


----------



## AEQUITAS (25. Sep 2009)

Soweit klappt alles mit der eigenen UI für die JMenuBar. Nun habe ich auch entsprechend eine für JMenu gemacht, jedoch ist nun kein Mousehighlighter mehr vorhanden. Sprich wenn ich mit der Maus über "Datei" (mein JMenu) fahre, dann ändert sich nicht der Hintergrund (bzw. die Hintergrundfarbe).

Scheint so, als hätte ich den mit paintBackground überschrieben. Wie kann ich nun dieses "Highlight", "Select" oder wie man es auch immer nennt überschreiben?

Gibt es vielleicht ein gutes Tutorial (Deutsch/Englisch egal), was sich mit der Erstellung von UIs beschäftigt? Habe bisher nur ein sehr gutes über TabbedPanes gefunden:
blog.elevenworks.com  Blog Archive  Creating a custom UI delegate for JTabbedPane

Hier mal der Code:

```
package gui;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JMenuItem;
import javax.swing.plaf.basic.BasicMenuUI;

public class UITopMenu extends BasicMenuUI {
	
	@Override
    protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor) {
		
		super.paintBackground(g, menuItem, bgColor);
        int panelHeight = 25;
        int panelWidth = menuItem.getWidth();
        GradientPaint gradientPaint = new GradientPaint(0, 0, Color.white, 0, panelHeight, new Color(220,220,220));

		Graphics2D g2d = (Graphics2D) g.create();
		g2d.setPaint(gradientPaint);
		g2d.fillRect(0, 0, panelWidth, panelHeight);
		g2d.dispose();
    }
	
}
```


----------



## André Uhres (26. Sep 2009)

In diesem Fall ist das ja ziemlich einfach: wir fragen das Model, ob selektiert wurde. Wenn ja, dann malen wir mit einer anderen Farbe:

```
if (menuItem.getModel().isSelected()) {
    g.setColor(bgColor);
...
```
Ich persönlich halte aber nicht viel von solchen spielerischen Eingriffen ins Aussehen von Standardkomponenten, ohne triftigen funktionellen Grund. Darum kann ich diesbezüglich auch nichts generell empfehlen.


----------



## AEQUITAS (26. Sep 2009)

Ok, gut zu wissen. Vielen Dank!


----------

