Swing JScrollPane mit "viel Inhalt" scrollt zu langsam (inkl. See-For-Yourself.jar :D)

Hutmacher

Bekanntes Mitglied
Hallo 🙂
In meinem Programm habe ich ein JFrame, auf dem ein JScrollPane liegt. In diesem ist wiederum ein Panel, in dem sehr viel gezeichnet wird. Genauer gesagt muss das JPanel einige (~300) Bilder halten, die dann dort reingemalt werden.

Natürlich sind es verschiedene Bilder, die gemalt werden, aber als Vereinfachung habe ich nun einfach 300x das gleiche Bild genommen (PNG, 477x400, 33,2Kb). Das sieht dann so aus:


Das Problem:
Wenn ich nach unten scrolle, "kommt das JScrollPane nicht mehr hinterher" und alles laggt.

Der Sourcecode (ein kleines SSCCE):
Java:
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.*;

public class DrawingTest extends JFrame
{
	private JPanel panel = new JPanel()
	{
		private BufferedImage image = readImage("X.png");
		
		@Override
		public Dimension getPreferredSize()
		{
			return new Dimension(image.getWidth() * 3, image.getHeight() * 100);
		}
		
		@Override
		public void paintComponent(Graphics graphics)
		{
			super.paintComponent(graphics);
			
			long time = System.currentTimeMillis();
			
			for (int i = 0; i < 100; i++)
			{
				for (int j = 0; j < 3; j++)
				{
					graphics.drawImage(image, image.getWidth() * j, image.getHeight() * i, null);
				}
			}
			
			System.out.println(System.currentTimeMillis() - time + "ms");
		}
	};
	
	public DrawingTest()
	{
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(800, 800);
		JScrollPane scrollPane = new JScrollPane(panel);
		scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		scrollPane.getVerticalScrollBar().setUnitIncrement(100);
		add(scrollPane);
	}

	private BufferedImage readImage(String filename) 
	{
		BufferedImage image = null;
		
		try {
			image = ImageIO.read(new File(filename));
			image.flush();
		} 
		catch (IOException e) {
			throw new RuntimeException("Could not load");
		}
		
		return image;
	}
	
	public static void main(String[] args)
	{
		DrawingTest t = new DrawingTest();
		t.setVisible(true);
	}
}

Wie kann ich das Problem lösen, dass das Scrollen so extrem langsam und laggy ist?

Am besten probiert ihr es selbst mal aus; im Anhang findet ihr eine ZIP-Datei, in der diese Klasse als Runnable JAR mit dem Test-Bild "X.png" und dem Sourcecode liegt. Scrollt einfach mal ein bisschen nach unten.


Bin dankbar für jede Hilfe!

(Anm:
Ich habe zum Test einmal die Variable "BufferedImage image" durch ein "VolatileImage image" ersetzt - das Zeichnen geht zwar viel schneller, allerdings sollen in der "richtigen Version" des Programms ~300 verschiedene Bilder im Speicher gehalten werden, weshalb VolatileImage dort wenig sinnvoll ist.)
 

Anhänge

Kann es sein, dass dein PC nicht der beste ist ?

Ich habe bei 300 Bildern absolut keine laggs.
Erst bei rund 100.000 Bildern fängt es bei mir an zu haken.

Sehe auch da auch keine Verbesserungsmöglichkeiten auf Anhieb. 300 Bilder sollten eigentlich für keinen PC zu Problemen führen.
Gibt bestimmt irgendwelche Vorgehensweisen aus der Spielebranche, aber da kenne ich mich nicht mit aus.
 
Zuletzt bearbeitet:
Eigentlich dürfte mein Laptop lockerst genug Power dafür haben (GeForce GT 640M, Intel i5, 4GB RAM).
Danke jedenfalls fürs Testen 🙂

Aber habe die Lösung gerade gefunden:
Normale BufferedImages, wenn ich sie per ImageIO.read einlesen, scheinen in einem Format zu sein, das für meinen Bildschirm kräftig umgerechnet werden muss, um ausgegeben zu werden. Das scheint bei einem einfachen Zeichenprozess kein Problem zu sein, allerdings wird immer, wenn ich im ScrollPane scrolle, die repaint-Methode erneut aufgerufen - daher kommt es zu Lags. Deshalb habe ich ein mal versucht, das Bild, das ich zeichnen lasse, vorher in ein kompatibles Format zu konvertieren. Dazu benutze ich die unten stehende Methode und erzeuge mein Bild nun einfach per:
Code:
BufferedImage image = toCompatibleImage(readImage("X.png"));

Java:
public static BufferedImage toCompatibleImage(BufferedImage image)
	{
		GraphicsConfiguration graphicsConfig = GraphicsEnvironment
			                                   .getLocalGraphicsEnvironment()
			                                   .getDefaultScreenDevice()
			                                   .getDefaultConfiguration();

		if (image.getColorModel().equals(graphicsConfig.getColorModel()))
			return image;

		BufferedImage compatibleImage = graphicsConfig.createCompatibleImage( image.getWidth(), 
				                        image.getHeight(), image.getTransparency());

		Graphics2D g2d = (Graphics2D) compatibleImage.getGraphics();
		g2d.drawImage(image, 0, 0, null);
		g2d.dispose();

		return compatibleImage; 
	}
 

Neue Themen


Zurück
Oben