hintergrundbild für jtable als jscrollpane

Flaming_Ace

Mitglied
Hi,
habe folgendes Problem:
ich hab eine jtable die auf einem jscrollpane liegt. jetzt wollte ich ein hintergrundbild einfügen.
Da die Tabellenzeilen das bild nach und nach (je nachdem wieviel zeilen es gibt) verdecken soll, habe
ich das bild durch die methode paint der klasse jscrollpane eingefügt:
Java:
public class TableScrollPaneBackground extends JScrollPane{
	// The Image to store the background image in.
    Image img;
    Frm_Main main;
    public TableScrollPaneBackground(Frm_Main main)
    {
        // Loads the background image and stores in img object.
        img = Toolkit.getDefaultToolkit().createImage(getClass().getResource("/grafiken/Logo.png"));
        this.main = main;
    }

    public void paint(Graphics g)
    {
        // Draws the img to the BackgroundPanel.
    	Dimension d = getSize();
		for( int x = 0; x < d.width; x += img.getWidth(null))
			for( int y = 0; y < d.height; y += img.getHeight(null) )
				g.drawImage( img, x, y, null, null );
//        super.paint(g);//dann geht nichts mehr
//        this.main.validate();//bringt nix
    }
}

die jtable füge ich dann auf das jscrollpane hinzu:
Java:
scrollPane = new TableScrollPaneBackground(this);
scrollPane.setViewportView(getTableMain());//getTableMain() erstellt eine neue Tabelle

Das Bild wird gezeichnet, aber die Tabelle wird zu beginn nicht angezeigt. erst wenn man auf ihr herumklickt.:autsch:
und dabei auch nur die entsprechenden zeilen..
die Scrollbalken werden auch nicht korrekt angezeigt.

hat jemand ne lösung :rtfm: oder eine alternative? danke im vorraus..
 
Mal abgesehen davon, dass man bei Swingkomponenten paintComponent anstelle von paint überschreibt, würde ich mich hüten sowas bei JScrollPane zu machen. JScrollPane ist doch eine relativ komplexe Komponente.
Ich würde ein Überschreiben des Viewports (denn da soll schliesslich das Bild erscheinen) bevorzugen.
Wobei ich mir sicher bin, ob folgendes Bsp nicht doch irgendwie Probleme verursachen kann.
Java:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;

public class ScrollBackgroundDemo extends JFrame {
	private BufferedImage image;
	public ScrollBackgroundDemo() {	
		JTable table = new JTable(new Object[][] {{"Test"}, {"Test"}}, new String[] {"Titel"});
		image = ScrollBackgroundDemo.createImage();
		JScrollPane scroll = new JScrollPane();
		JViewport view = new JViewport() {
			public void paintComponent(Graphics g) {
				g.drawImage(image, 0, 0, this);
			}
		};
		view.add(table);
		scroll.setViewport(view);
		this.getContentPane().add(scroll);
	}
	
	public static BufferedImage createImage() {
		BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB);
		Graphics2D g = image.createGraphics();
		g.setColor(Color.RED);
		g.fillRect(20, 20, 160, 160);
		g.dispose();
		return image;
	}

	public static void main(String[] args) {
		JFrame frame = new ScrollBackgroundDemo();
		frame.setBounds(0, 0, 250, 150);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	}
}
 
viewport oder scrollpane ergeben keine änderung.
jedoch weiß ich jetzt warum das scrollpane das bild nicht angezeigt hat. viewport ist default opaque:
deswegen habe ich folgende änderungen gemacht:
Java:
public class TableScrollPaneBackground extends JScrollPane{
    private Image img;
    private Dimension d;
    
    public TableScrollPaneBackground()
    {
    	img = Toolkit.getDefaultToolkit().createImage(getClass().getResource("/grafiken/HintergrundTabelle.png"));//Bild laden

        getViewport().setOpaque(false);//den Viewport auf durchsichtig stellen

        Dimension dim = new Dimension(img.getHeight(null), img.getWidth(null));//Dimension des bildes holen
        setSize(dim);//panel an bild anpassen
    }

    @Override
    public void paintComponent(Graphics g)
    {
    		//Bildzeichnen
        	d = getSize();
    		for( int x = 0; x < d.width; x += img.getWidth(null))
    			for( int y = 0; y < d.height; y += img.getHeight(null) )//Kachel-Modus.. so dass kein leerer raum entsteht
    				g.drawImage( img, x, y, null );

		//Componenten über das Bildzeichnen
		super.paintComponents(g);
    }
}
die einbindung habe ich nicht geändert.
allerdings wird das bild beim ersten anzeigen des fensters nicht gezeigt, erst sobald man die fenstergröße ändert oder ein anderes fenster über das programm gezogen hat.
setze ich den ImageObserver auf "this".. wird das bild beim ersten laden zwar angezeigt, aber nicht korrekt, die komponenten "überlappen"..soll heißen man sieht die oberen komponenten (buttons, menü) nach unten gespiegelt.. ;(
 

Anhänge

  • Aufzeichnen.jpg
    Aufzeichnen.jpg
    48,3 KB · Aufrufe: 52
Zuletzt bearbeitet:
Mein Bsp. funktioniert doch. Warum orientierst Du Dich nicht daran?
Mit einer überschrieben paintComponent in der JScrollPane wirst Du vermutlich nie glücklich werden.
 
ich habs auch mit einem viewport versucht, so wie in deinem beispiel.
dies hat allerdings keine änderungen ergeben, genau die gleichen effekte:
Java:
public class TableViewPort extends JViewport{
    
	private Image img;
    private Dimension d;
    
    public TableViewPort()
    {
    	img = Toolkit.getDefaultToolkit().createImage(getClass().getResource("/grafiken/HintergrundTabelle.png"));//Bild laden
        Dimension dim = new Dimension(img.getHeight(null), img.getWidth(null));//Dimension des bildes holen
        setSize(dim);//viewport an bild anpassen
    }

    @Override
    public void paintComponent(Graphics g)
    {
    		//Bildzeichnen
        	d = getSize();
    		for( int x = 0; x < d.width; x += img.getWidth(this))
    			for( int y = 0; y < d.height; y += img.getHeight(this) )//Kachel-Modus.. so dass kein leerer raum entsteht
    				g.drawImage( img, x, y, this );
		//Componenten über das Bildzeichnen
		super.paintComponents(g);
    }
}
Die einbindung hab ich so gemacht:
Java:
TableViewPort view = new TableViewPort();//
view.add(getTableMain());//liefert die tabelle
scrollPane.setViewportView(view);//
wie gesagt: auch mit dieser variante habe ich die gleichen probleme.
außerdem wird dann der tabellenkopf nichtmehr angezeigt.

*update*
wenn ich das bild im viewport zeichne, entsteht der überlappen effekt "nur" bei ungefähr 50% aller programmstarts???:L:shock:
 
Zuletzt bearbeitet:
wieso rufst Du eigentlich paintComponents(g) auf? Das wird doch eh von der paint() der Komponente aufgerufen.

Hier mal ein Bsp. mit "gekacheltem" Hintergrund, bei mir wird alles - auch der Tabellenkopf - gezeichnet.
Poste vielleicht mal ein KSKB, damit man erkennen kann woran es liegen könnte.
Java:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;

public class ScrollBackgroundDemo extends JFrame {
	private BufferedImage image;
	public ScrollBackgroundDemo() {	
		JTable table = new JTable(new Object[][] {{"Test"}, {"Test"}}, new String[] {"Titel"});
		image = ScrollBackgroundDemo.createImage();
		JScrollPane scroll = new JScrollPane();
		JViewport view = new JViewport() {
			public void paintComponent(Graphics g) {
				super.paintComponent(g);
				int w = image.getWidth(), h = image.getHeight();
				for (int x=0; x<this.getWidth(); x += w)
					for (int y=0; y<this.getHeight(); y += h)
						g.drawImage(image, x, y, this);
			}
		};
		view.add(table);
		scroll.setViewport(view);
		this.getContentPane().add(scroll);
	}
	
	public static BufferedImage createImage() {
		BufferedImage image = new BufferedImage(20, 20, BufferedImage.TYPE_INT_RGB);
		Graphics2D g = image.createGraphics();
		g.setColor(Color.RED);
		g.fillRect(5, 5, 10, 10);
		g.dispose();
		return image;
	}

	public static void main(String[] args) {
		JFrame frame = new ScrollBackgroundDemo();
		frame.setBounds(0, 0, 250, 150);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	}
}
 
In Zeile 19 von ScrollBackgroundDemo rufst du doch auch super.paintComponents() auf..
allerdings am anfang der methode..
wenn ich diesen aufruf rausnehme hat es keine auswirkungen... ;(

ich habe gerade versucht ein JPanel mit einem hintergrundbild zu erstellen.
auf dieses panel habe ich dann das scrollPane und darauf dann durch viewport die tabelle.
das scrollpane und viewport habe ich auf opaque(false) gesetzt.

die anzeige funktioniert auch, jedoch habe ich wieder den überlappungseffekt. der hängt irgendwie mit ImageObserver zusammen.. wenn ich statt "this" "null" verwende habe ich den überlappungseffekt nicht, aber er zeichnete das bild in diesem fall auch ab und zu nicht.
 
Zuletzt bearbeitet:
Ich rufe
Code:
super.paintComponent(g);
auf (ohne s), das ist ein kleiner aber feiner Unterschied ;-)
Läuft den mein Bsp. bei Dir? Ansonsten wie gesagt, wäre ein KSKB nicht schlecht.
 
dein beispiel läuft einwandfrei!

und jetzt hab ich auch den fehler.. Ich habe paintComponent mit paintComponents verwechselt
:lol:

:applaus::applaus::applaus::applaus::applaus::applaus::applaus::applaus:

jetzt funktionieren alle 3 varianten (hintergrundbild im panel/viewport/scrollpane)

für alle, die nicht wissen, wo der unterschied zwischen den zeichenmethoden sind.
Galileo Computing :: Java ist auch eine Insel (8. Auflage) – 17 Grafikprogrammierung

Grundsätzlich ließe sich auch von JFrame eine Unterklasse bilden und paint() überschreiben, doch ist das nicht der übliche Weg.
Stattdessen wählen wir einen anderen Ansatz, der sogar unter AWT eine gute Lösung ist.
Wir bilden eine eigene Komponente, eine Unterklasse von JPanel (unter AWT Panel, was wir aber nicht mehr weiter verfolgen wollen),
und setzen diese auf das Fenster.
Wird das Fenster neu gezeichnet, gibt das Grafiksystem den Zeichenauftrag an die Kinder weiter,
also auf unser spezielles JPanel, und ruft die überschriebene paint()-Methode auf.
Allerdings überschreiben eigene Unterklassen von Swing-Komponenten im Regelfall nicht paint(), sondern paintComponent().
Das liegt daran, dass Swing in paint() zum Beispiel noch Rahmen zeichnet und sich um eine Pufferung des Bildschirminhalts zur Optimierung kümmert.
So ruft paint() die drei Methoden paintComponent(), paintBorder() und paintChildren() auf, und bei einer Neudarstellung kümmert sich ein RepaintManager
um eine zügige Darstellung mit Hilfe der gepufferten Inhalte, was bei normalen Swing-Interaktionskomponenten wie Schaltflächen wichtig ist.

Damit ist die Darstellung von Inhalten in einem JFrame einfach. Wir importieren drei Klassen, JPanel und JFrame aus javax.swing sowie Graphics aus java.awt. Dann bilden wir eine Unterklasse von JPanel und überschreiben paintComponent().

Die Methode paintComponent() besitzt in der Oberklasse die Sichtbarkeit protected, was wir beibehalten sollten;
die Methode wird nicht von uns von anderer Stelle aufgerufen, daher muss eine Unterklasse die Sichtbarkeit nicht zu public erweitern.
Der Aufruf von super.paintComponent() ist immer dann angebracht, wenn die Oberklasse ihre Inhalte zeichnen soll.
Bei vollständig eigenem Inhalt ist das nicht notwendig.


aus javadoc für paintComponents():
Paints each of the components in this container.

hier noch der korrekte code für das hintergrundBildPanel:
Java:
 public class TablePanelHintergrundbild extends JPanel{
    
	private Image img;
    private Dimension d;
    
    public TablePanelHintergrundbild()
    {
    	img = Toolkit.getDefaultToolkit().createImage(getClass().getResource("/grafiken/HintergrundTabelle.png"));//Bild laden
        Dimension dim = new Dimension(img.getHeight(null), img.getWidth(null));//Dimension des bildes holen
        setSize(dim);//panel an bild anpassen
    }

    @Override
    protected void paintComponent(Graphics g)
    {
    	super.paintComponent(g);
    	//Bildzeichnen
        d = getSize();
    	for( int x = 0; x < d.width; x += img.getWidth(this))
    		for( int y = 0; y < d.height; y += img.getHeight(this) )//Kachel-Modus.. so dass kein leerer raum entsteht
    			g.drawImage( img, x, y, this );
    }
}
wie gesagt alle Elemente die darüber liegen auf opaque(false) stellen (das wären bei mir scrollPane und ViewPort)

Vielen vielen Dank an: Michael... !!!
 

Zurück
Oben