# JTable - TableCellRenderer - BufferedImage



## redbomber (26. Mrz 2009)

Hi zusammen,
nachdem ich nun das Geschwindigkeitsproblem mit meiner Tabelle dank euch lösen konnte nun noch eine Frage:

Meine Tabelle (in einer JScrollPane) besitzt Zeilen, in denen die Zellen einfach eine Hintergrundfarbe gesetzt bekommen.
-> Passiert in der Methode des CellRenderers (extends JPanel implements TableCellRenderer): 

```
public Component getTableCellRendererComponent(JTable table, Object value,
			boolean isSelected, boolean hasFocus, int row, int col) {
}
```

Und es gibt Zeilen in denen sich Scala befindet (ausschnitte aus einem zuvor gezeichnetem Bufferedimage)
--> Passiert in der Methode des CellRenderers:

```
public void paintComponent(Graphics g) {
}
```

--------------------------------------------------------------------------------------------

Wenn ich über die Tabelle scrolle, dann werden die Zellen, welche die Hintergrundfarben gesetzt bekommen immer korrekt dargestellt.
Die Scala ist auch "meistens" da.

Wenn ich aber den Scrollbalken an eine andere Stelle ziehe, dann fehlen alle Scala Zeilen (also die sind einfach schwarz)

--------------------------------------------------------------------------------------------


Wenn ich das jetzt richtig verstehe:
Die Tabelle wird zu beginn einmal initialisiert und dann werden alle Zellen entsprechend befüllt. Also bei mir werden die Hintergrundfarben gesetzt.

Wann wird jetzt bei dem CellRenderer die PaintMethode aufgerufen? Wieso habe ich dann an machen Stellen zwar die Zellen mit den gesetzten Hintergrundfarben, aber die Scala, die in der paintComponent() Methode gezeichnet wird fehlt?

Ich hatte eine tolle Lösung  :
Beim Scrollen habe ich einfach die Tabelle immer upgedatet, also alle Zellrenderer neu zeichnen lassen.
Siehe da -> die Scala ist immer da, so wie es sein soll, aber die Tabelle ist sehr sehr lahm.


----------



## Ebenius (26. Mrz 2009)

Zeig mal Deinen paintComponent(...)-Code!

Ebenius


----------



## redbomber (30. Mrz 2009)

Also hier ist mein Code. Ich überprüfe immer ob es sich bei der Zeile um diejenige Zeile handelt,
welche die Scala darstellt (placeholderCell). 
Die Scala wird in einem BufferedImage für die komplette Scala-Zeile gezeichnet:


```
public void paintComponent(Graphics g) {
	super.paintComponent(g);

	if (placeholderCell){
// scalaImage = BufferedImage
		if (this.scalaImage != null) {
			int boxSizeX = master.getBoxSizeX();
			int boxSizeY = master.getBoxSizeY();
			Graphics2D g2d = (Graphics2D) g;
			g2d.drawImage(this.scalaImage, 0, 0, boxSizeX, boxSizeY,
						(column- 1) * boxSizeX, 0,
						((column- 1) * boxSizeX + boxSizeX),
						boxSizeY, this);
			} 
	}
}
```

Falls es sich um eine solche Scala-Zeile handelt, dann schneide ich die Bereiche aus dem BufferedImage heraus und befülle die Zellen der Zeile damit.

INFO:
Die BufferedImages für die Scala zeichne ich immer FÜR DAS AKTUELLE getVisibleRect() der JTable.
Die BufferedImages speichere ich mir dann in einer HashMap (key ist die Zeilennummer) damit ich jede Scala für die entsprechenden Zeilen nur einmal zeichnen muss.
Da meine Tabelle sehr viele Zeilen hat, wollte ich nicht gleich alle Scala Zeilen zu beginn zeichnen.

-------------------------------------------------------------------------------------------------------------

Wenn ich auf die Pfeiltasten der ScrollBar klicke, dann wird die Scala korrekt dargestellt.
Wenn ich die Scrollbar an dem Button verschiebe, dann wird die Scala nicht angezeigt, erst wenn ich wieder hoch oder runter scrolle.


Hier noch mein Controller:

```
/**
* Event occurs if vertical scrollbar is moved.
* 
*/
public void adjustmentValueChanged(AdjustmentEvent e) {

     switch (e.getAdjustmentType()) {
	case AdjustmentEvent.UNIT_INCREMENT:
		System.out.println("UNIT_INCREMENT");
		break;
	case AdjustmentEvent.UNIT_DECREMENT:
		System.out.println("UNIT_DECREMENT");
		break;
	case AdjustmentEvent.BLOCK_DECREMENT:
		System.out.println("BLOCK_DECREMENT");
		break;
	case AdjustmentEvent.BLOCK_INCREMENT:
		System.out.println("BLOCK_INCREMENT");
		break;
	case AdjustmentEvent.TRACK:
		break;
	}

	//model.getTable().repaint();   <-- hiermit klappts
       // model.fireChanged();            <-- und hiermit auch
}
```

Wenn ich model.getTable().repaint() oder model.fireChanged() (-> ruft update() der JTable auf) aufrufe
dann klappt es wunderbar, also dann wird die Scala immer angezeigt, aber dann ist die Tabelle auch wahnsinnig langsam.


----------



## Ebenius (30. Mrz 2009)

Mach mal einen _else_-Zweig an die innere _if_-Anweisung in der paintComponent(Graphics)-Methode. Erstmal nachsehen, ob das Image eventuell hin und wieder _null_ ist.

Wenn das nicht der Fall ist; kannst Du das ganze Programm zusammenpacken, als Attachment an den nächsten Beitrag, so dass man sich den Quelltext mal insgesamt ansehen kann?

Ebenius


----------



## redbomber (30. Mrz 2009)

also ich kann gerne gleich noch den Code anfügen.

Habe eben aber wie du gesagt hast überprüft ob die images vorhanden sind.
Wenn ich also auf die Pfeiltasten klcike, dann sind die Bilder immer vorhanden.

Wenn ich die ScrollBar an dem Balken bewege, dann sind ganz viele Images nicht vorhanden...
Ich suche jetzt gerade in die Richtung weiter, also warum es für die Zeilen noch keine Bilder gibt.


----------



## Ebenius (30. Mrz 2009)

Viel Erfolg!


----------



## redbomber (30. Mrz 2009)

also der else Abfrage habe ich jetzt einfach eine Methode hinzugefügt, die falls kein Buffered Image vorhanden ist,
dieses einfach zeichnet.
Also jetzt wird die Scala auch dann angezeigt, wenn ich den Scrollbalken an dem Button verschiebe.


Jetzt habe ich nur noch eine Frage:

Bisher hatte ich mir immer die erste und letzte sichtabre Zeile der Tabelle berechnet, und für diesen Bereich die BufferedImages gezeichnet. 
Diese werden dann in einer HashMap gespeichert (falls ich wieder dieselbe Zeile anzeige).
-> in der paintComponent() checke ich daher nun immer ob in der HashMap für die Zeile ein BufferedImage vorhanden ist, 
wenn ja: dann nehm ich dieses, 
wenn nein: dann zeichne ich es.

Frage:
Ist es sinnvoll die BufferedImages in der HashMap zu halten? Benötigen diese viel Speicherplatz?
Oder soll ich die BufferedImages einfach lieber immer neu zeichnen?


----------



## Ebenius (30. Mrz 2009)

Wie kompliziert ist denn Deine Skala? Für mich klingt einfach alles nach neu zeichnen, aber das hängt schon von der Zeichengeschwindigkeit ab.

Ebenius


----------



## redbomber (30. Mrz 2009)

also einige Berechnungen aber im grund wird einfach nur ein waagrechter strich gezeichnet,
allo 5 boxen ein kleines Rechteck als Marker und alle 10 Boxen ein großes Rechteck
etwa so:
_____,_____|_____,_____|_____,   (nur auf dem Kopf stehend)

Zusätzlich alle 10 Boxen die aktuelle Zellnummer.


```
// only draw scala if height is big enough
if(master.getBoxSizeY()>5){
	for(int i = this.firstCell; i <= this.lastCell; i++){
	// set "1" for the first cell
	if(i==1){
		int pos = counter * boxSizeX;
		int smallMarkerWidth = (int)Math.floor((double)boxSizeX/10.);
		if(smallMarkerWidth == 0) smallMarkerWidth = 1;
		int bigMarkerWidth = (int)Math.floor((double)boxSizeX/7.);
		if(bigMarkerWidth == 0) bigMarkerWidth = 1;

		int valuePosWidth = pos + bigMarkerWidth + (int)Math.floor((double)bigMarkerWidth/2.);
		int valuePosHeight = height - (int)Math.floor((double)height / 4.) ;
					
		if(master.getBoxSizeY()>10)g2d.drawString(Integer.toString(1),valuePosWidth,valuePosHeight);
	}
				
	// set all 5 boxes small marker and all 10 boxes a big marker
	if((i) % 5 == 0){
	// compute position in scala image
		int pos = counter * boxSizeX;
		int smallMarkerWidth = (int)Math.floor((double)boxSizeX/10.);
		if(smallMarkerWidth == 0) smallMarkerWidth = 1;
		int smallMarkerHeight = (int)Math.floor((double)height / 3.);
		int bigMarkerWidth = (int)Math.floor((double)boxSizeX/7.);
		if(bigMarkerWidth == 0) bigMarkerWidth = 1;
					
		int bigMarkerHeight = (int)Math.floor((double)height / 2.);
		int valuePosWidth = pos + bigMarkerWidth + (int)Math.floor((double)bigMarkerWidth/2.);
		int valuePosHeight = height - (int)Math.floor((double)height / 4.) ;
					
	        if(pos < actualWidth){
			if((i) % 10 == 0){
			     // draw big marker (all 10 Boxes)
			     g2d.fillRect(pos, 0, bigMarkerWidth, bigMarkerHeight);
			     int valueOfPos = computeValueOfPos(i);
							
			     if(master.getBoxSizeY()>10)g2d.drawString(Integer.toString(valueOfPos), 
									valuePosWidth,valuePosHeight);
		        }else {
			     // draw small marker (all 5 Boxes)
			     g2d.fillRect(pos, 0, smallMarkerWidth , smallMarkerHeight);
		        }
	         }
	}
	counter++;
	}
}
```


----------



## Ebenius (30. Mrz 2009)

Probier's mal aus, sieht mir nicht zu kompliziert aus um es on-the-fly zu malen.

Ebenius


----------

