Eine Klasse soll im Hintergrund laufen... (GUI/Swing/THREADS)

Hallo zusammen!

Ich habe ein paar Fragen zu einer von mir konstruierten Klasse.
Der Gedanke ist folgender: Ich möchte diese Klasse in mehreren Swing Applikationen nutzen, eine davon ist bereits betriebsbereit. Und zwar möchte ich aus meinen Programmen diese Klasse aufrufen um für einen vom Nutzer angegeben Pfad (egal wie der jetzt eingelesen wurde) zwei wesentliche Listen für mich erstellen - Erfassung der OriginalBilder, sowie erstellte Thumbnails davon - und dann die Thumbnails auf die vorhandene Swing Applikation in ein einfaches JScrollPane zeichnen.

Ich poste hier mal meinen Lösungsansatz an dem ich jetzt festhänge, aus dem der Ablauf hoffentlich klar wird:

import java.io.File;
import java.util.LinkedList;
import javax.swing.ImageIcon;
/**
 * @author  Andy Rudolph
 * @date    18.08.2010
 * @project Medienverwaltung 
 * @package projekt
 * @file    CreateThumbnails.java
 */
public class CreateThumbnails {
 
    public LinkedList findings = new LinkedList();
    public LinkedList thumbList = new LinkedList();
    private ImageIcon thumbnail;
    
    public CreateThumbnails() {
        super();
        /*Wie, wo - muss ich das hier aufrufen ??
        searchImage(null, null);
        createImageThumb();
        pasteThumbs();*/
    }
    /**
     * Schritt 1: 
     * Durchsucht das beim Start angegebene Verzeichnis 
     * auf vorhandene und unterstützte Bildtypen und 
     * speichert diese in einer File-List
     */
    public void searchImage(File directory, FoundListener fnd)
    {
        File[] files = directory.listFiles();
        for(int i=0; i< files.length; i++)
        {
            if(files** != null && files**.exists())
            {
                    if(files**.getName().contains("jpg")
                            ||files**.getName().contains("png")
                            ||files**.getName().contains("gif"))
                    {
                        findings.add(files**);
                        if(fnd != null) fnd.fileFound(files**);
                    }
                    if(files**.isDirectory())
                        searchImage(files**, fnd);
            }
        }
    }
    /**
     * Schritt 2:
     * Liest die erstellte File-List aus und erstellt 
     * zu jedem Eintrag ein passendes Thumbnail.
     */
    public void createImageThumb() {        
        
        File[] thumbs = thumbList.listFiles();
        for (int i = 0; i < thumbs.length; i++) {
            
            ImageIcon tmpIcon = new ImageIcon((String) findings.get(i));
            if (tmpIcon != null) {
                if (tmpIcon.getIconWidth() >= tmpIcon.getIconHeight()) {
                    //landscape
                    if (tmpIcon.getIconWidth() > Konstanten.PIDIM.width) {
                        thumbnail = new ImageIcon(tmpIcon.getImage()
                                .getScaledInstance(Konstanten.PIDIM.width, -1,
                                        Image.SCALE_DEFAULT));
                    } else { 
                        thumbnail = tmpIcon;
                    }
                } else {
                    //portrait
                    if (tmpIcon.getIconHeight() > Konstanten.PIDIM.height) {
                        thumbnail = new ImageIcon(tmpIcon.getImage()
                                .getScaledInstance(-1, Konstanten.PIDIM.height,
                                        Image.SCALE_DEFAULT));
                    } else { 
                        thumbnail = tmpIcon;
                    }
                }
                thumbList.add(thumbs**);//Zunächst temporär
                //TODO speichern der Thumbnails in einem hierzu
                //angelegten Verzeichnis
            }
        }
    }
    /**
     * Schritt 3:
     * List die vorhandene Thumbs-Liste aus und zeichnet 
     * die Thumbs in der von der Main-Klasse gestartete 
     * Swing-Anwendung in das JScrollPane.
     */
    public void pasteThumbs() {
        //TODO Thumbnails auf vorhandene Swinganwendung
        //in JScrollPane zeichnen (Klasse: Main.java)
    }
}```

Das rekursive Einlesen eines Verzeichnisses hab ich glaube ich sowit verstanden, mit Hilfe der API hab ich das in der ersten Methode (hoffentlich) richtig umgesetzt. Danach möchte ich die erstellte File-Liste nutzen um daraus Thumbnails zu kreieren. Auch das müsste soweit ungefähr stimmen, wobei ich mit dem Casting am Anfang der 2. Methode noch Probleme habe.
Dann möchte ich die erstellten Thumbnails in meine Swing-Applikation zeichnen, die extra dafür ein JScrollPane zur Verfügung stellt.

Fragen:

1. Ist es in Java mit Hilfe von Runnable/synchronized möglich, die Hauptaplikation zu starten, zu nutzen, DANN/WÄHRENDESSEN im Hintergrund diese vorgefertigte Klasse aufzurufen und das ScrollPane live zu füllen?
2. Wie caste ich in der 2.Methode richtig auf die verd... File-List? Da klemmts grad...
3. Wie erreiche ich das JScrollPane - nennen wir es einfach mal "thumbsPane" aus dem Hauptprogramm?

Vielen Dank schon mal für die Mühe sich die ganze Geschichte mal anzuschauen, ich freue mich auf Unterstützung!

Liebe Grüße
Gecko

Das sind ja gleich drei Fragen auf einmal. Das geht nun wirklich nicht :o)

Statt einer konkreten Listenklasse sollte man lieber das Interface “List” verwenden. Und dem kann man noch Generics verpassen, so dass das Problem mit dem Casten vielleicht wegfällt (das habe ich aber nicht ganz verstanden…). Abgesehen davon sollten IMMER ALLE Fields private sein (bestenfalls protected oder default, aber selten)

private List<File> findings = new LinkedList<File>();
private List<ImageIcon> thumbList = new LinkedList<ImageIcon>();

(Das könnten auch ArrayLists sein…)

Du solltest klar beschreiben, was die Klasse (bzw. die public-Methoden) machen sollen. Welche Methoden soll man wie von außen aufrufen? Konkret: Gibt es einen Anwendungsfall, wo man NUR “searchImage” aufruft, aber NICHT “createThumb”? Wohl eher nicht…

In jedem Fall stellt sich die Frage, wie sich das Programm verhalten soll, während es die Thumbnails generiert. Vermutlich sollte das in einem eigenen Thread laufen. Macht die Klasse das automatisch, oder muss sich da derjenige drum kümmern, der die Klasse verwendet? Soll die Klasse einen “Progress Dialog” anzeigen, während sie arbeitet? Wenn ja, wie und wo wird der erstellt, und wer kümmert sich darum?

Du solltest dir mal die Klassen http://download-llnw.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html und http://download-llnw.oracle.com/javase/6/docs/api/javax/swing/ProgressMonitor.html ansehen - die könnten hilfreich sein.

Zum eigentlichen Runterskalieren kann man getScaledInstance verwenden, aber dazu sind hier http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html ein paar bessere Alternativen beschrieben.

Das mit den “Konstanten” sieht etwas unschön aus. So eine Klasse tendiert dazu, irgendwann einen Haufen Mist zu enthalten, und der Widerverwendbarkeit der Klassen, die auf diese Konstanten zugreifen, im Weg zu stehen.

Die Ergebnisse würden wohl nicht zu einer JScrollPane hinzugefügt, sondern eher zu einem JPanel (das in einer JScrollPane liegt). Auch hier stellt sich die Frage: Soll das die Klasse machen? Das widerspricht ein bißchen dem Hollywood-Prinzip: “Don’t call us, we call you”. Also, die Frage WO genau die ImageIcons dann WIE hingelegt werden sollen, ist doch ggf. von Anwender zu Anwender unterschiedlich? Vielleicht (!) wäre es günstiger, wenn eine der Methoden einfach eine List zurückgibt, mit denen der Aufrufer dann machen kann, was er will…

Oki, das war mal ne super Ausführliche Antwort, vielen Dank dafür! Ich hab das ganze jetzt mal etwas entschlackt:

	//Liste der Original-Bilder
	private List<File> findings = new LinkedList<File>();
	//Liste der Thumbnails
	private List<ImageIcon> thumbList = new LinkedList<ImageIcon>();
	private ImageIcon thumbnail;
	
	/**
	 * Schritt 1: 
	 * Durchsucht das beim Start angegebene Verzeichnis 
	 * auf vorhandene und unterstützte Bildtypen und 
	 * speichert diese in einer File-List
	 * Schritt 2:
	 * Liest die erstellte File-List aus und erstellt 
	 * zu jedem Eintrag ein passendes Thumbnail
	 * @return List<ImageIcon>: erstellte Thumbnails
	 */
			
	public List<ImageIcon> createLists(File filePath) {	
		
		File[] files = filePath.listFiles();
		if (files != null) {
			for (int i = 0; i < files.length; i++) {
				if (files**.isDirectory()) {
					createLists(files**);
				}
				if(files**.getName().contains("jpg")
					||files**.getName().contains("png")
					||files**.getName().contains("gif")){
					findings.add(files**);
				}
			}
		}
		for (int i = 0; i < findings.size(); i++) {
			ImageIcon tmpIcon = new ImageIcon(findings.get(i).getName());
			if (tmpIcon.getIconWidth() >= tmpIcon.getIconHeight()) {
				//landscape
				if (tmpIcon.getIconWidth() > 100) {
					thumbnail = new ImageIcon(tmpIcon.getImage()
						.getScaledInstance(100, -1,	Image.SCALE_DEFAULT));
				} else { 
					thumbnail = tmpIcon;
				}
			} else {
				//portrait
				if (tmpIcon.getIconHeight() > 100) {
					thumbnail = new ImageIcon(tmpIcon.getImage()
						.getScaledInstance(-1, 100,	Image.SCALE_DEFAULT));
				} else { 
					thumbnail = tmpIcon;
				}
			}
			thumbList.add(thumbnail);//Zunächst temporär
	//	TODO speichern der Thumbnails in einem hierzu
	//	angelegten Verzeichnis
		}
		return thumbList;
	}
}```

Es war tatsächlich etwas zu weit gedacht, alles zu trennen, ich musste nur so Anfangen um mit meinem Konzept im Kopf klar zu kommen ;)
Der Aufruf der ganzen Geschichte soll automatisch nach dem Start des Programmes erfolgen, für mich ist einfach nur der Punkt wichtig, das sich die Swing-Applikation erst in Ruhe komplett aufbauen kann und nutzbar ist. Die Swing-Oberfläche mit Toolbar und Menüs sollte einsatzbereit sein und nur im JScrollPane  - was nur eines von einigen anderen ist - soll der Hinweis stehen, dass die Thumbnails noch in Arbeit sind und gleich erscheinen. Ein Ladebalken oder solche Spielchen sind garnicht notwendig.
Der Hollywood-Spruch gefällt mir, wenn ich jetzt noch rausfinde wie ich nach der initialisierung des Hauptfensters diese Klasse anrufe ohne das der Rest blockiert, bin ich glücklich... ;)

Und wieder einmal: An zwei Stellen darüber zu diskutieren ist Blödsinn und ungerecht denen gegenüber, die sich die Mühe machen, dir zu helfen.

Sry, ich wusste nicht das das Probleme gibt. Das war einfach gesunder Menschenverstand: 10 Köpfe haben deutlich mehr potenzial als nur 2. Eine Anzeige gibt man ja auch nicht nur in einer Zeitung auf…
Ich habe aus beiden Foren zu diesem Thema unterschiedliche und wertvolle Hinweise bekommen, so macht das Arbeiten Spaß und ist zudem für mich als Anfänger dreimal so lehrreich. Aber ich beuge mich den Regeln und entschuldige mich hiermit.

Eins solltest du wissen (nicht böse gemeint):

Die Mitglieder, die dir hier antworten sind quasi 1:1 die, die dir auch im Java-Forum antworten.
Wir sind hier ein eigenes Forum, weil vielen die Politik im Java-Forum nicht gefällt. Desweiteren sind wir hier für mehr, als nur für Java. Deshalb gibt es dieses Forum.

Stellst du in beiden Foren die gleiche Frage, ist das quasi ein Doppelpost.
Das nächste Mal daran denken, bitte.

[QUOTE=Gecko]Ein Ladebalken oder solche Spielchen sind garnicht notwendig.
Der Hollywood-Spruch gefällt mir, wenn ich jetzt noch rausfinde wie ich nach der initialisierung des Hauptfensters diese Klasse anrufe ohne das der Rest blockiert, bin ich glücklich… ;)[/QUOTE]

Ganz allgemein muss dazu das Laden und Umwandeln in einem eigenen Thread laufen. Wenn die Thumbnails dann aber “on the fly” erscheinen sollen, muss DAS doch wieder vom GUI-Thread gemacht werden. Der SwingWorker bietet dazu einige Möglichkeiten, die du dir mal ansehen könntest.

Wegen Crosspost: Natürlich würde niemand auf die gleiche Frage hin zweimal die gleiche Antwort in unterschiedlichen Foren schreiben. Und die Absicht, möglichst mehrere Quellen zu befragen, ist an sich legitim. Nur kann es eben passieren, dass im anderen Forum jemand ausführlichst was schreibt, während die Frage hier schon beantwortet wurde. (Hängt auch von der Art der Frage ab…)

Alles klar. Ich konnte mit all den Hinweisen die Geschichte (fast) erfolgreich umsetzen. Bei der initialisierung in meiner main hab ich
createThumbnails = new CreateThumbnails();
und kann nun auch erfolgreich auf die erstellte Liste zugreifen lassen.
Hier ist meine Methode zur Erstellung des Preview:

		ThumbInfo = new JPanel(new BorderLayout());
			Preview = new JPanel(new BorderLayout());
				//Überschrift
				JLabel thumbsHead = new JLabel(
						"Vorschau unterstützter Dateitypen: ");
				thumbsHead.setOpaque(true); 
				thumbsHead.setBackground(Color.ORANGE);
				thumbsHead.setForeground(Color.BLACK);
			//	Inhalt
				thumbInsertPane = new JPanel();
				thumbScrollPane = new JScrollPane(); //thumbInsertPane);
				List<ImageIcon> newList = CreateThumbnails.createLists(filePath);
				for (int i = 0; i < newList.size(); i++) {
					JLabel tempLabel = new JLabel(newList.get(i));
					thumbScrollPane.add(tempLabel);
				}
				thumbScrollPane.setFocusable(false);			
			Preview.add(BorderLayout.NORTH, thumbsHead);
			Preview.add(BorderLayout.CENTER, thumbScrollPane);
		ThumbInfo.add(BorderLayout.CENTER, Preview);
		ThumbInfo.add(BorderLayout.SOUTH, Beschreibung);
	}```
Soweit erst mal ohne Thread-Geschichten, einfach zum Verständnis für mich und damit es läuft. Allerding hab ich jetzt folgendes Bild wenn ich die Anwendung starte:
(siehe Anhang)
Anscheinend liegt hier noch ein letzter Denkfehler vor, da die Bilder zum thumbScrollPane hinzugefügt werden - das große auf der rechten Seite - aber leider nicht sichtbar sind? Wo hab ich mich vertan, sieht das jemand?

OK, wie das Threading jetzt gelöst ist, erkennt man daran nicht (könnte aber wichtig sein). Ansonsten vielleicht sowas wie

thumbInsertPane = new JPanel(new GridLayout(0,4));
thumbScrollPane = new JScrollPane(thumbInsertPane);
List<ImageIcon> newList = CreateThumbnails.createLists(filePath);
for (int i = 0; i < newList.size(); i++) {
    JLabel tempLabel = new JLabel(newList.get(i));
    thumbInserPane.add(tempLabel);
}
thumbScrollPane.validate();
thumbScrollPane.repaint();

Hm… hab jetzt verschiedene Möglichkeiten ausprobiert:

		ThumbInfo = new JPanel(new BorderLayout());
			Preview = new JPanel(new BorderLayout());
				//Überschrift
				JLabel thumbsHead = new JLabel(
						"Vorschau unterstützter Dateitypen: ");
				thumbsHead.setOpaque(true); 
				thumbsHead.setBackground(Color.ORANGE);
				thumbsHead.setForeground(Color.BLACK);
				//Inhalt => noch nicht Thread-sicher!
				thumbScrollPane = new JScrollPane();
				JLabel previewLabel = new JLabel();
				ImageIcon tempIcon = null;
				List<ImageIcon> newList = CreateThumbnails.createLists(filePath);
				for (int i = 0; i < newList.size(); i++) {					 
					tempIcon = newList.get(i);
					if (tempIcon != null) {
						int w = tempIcon.getIconWidth();
						int h = tempIcon.getIconHeight();
						System.out.println("B, H, Name: " + w + ", " + h + ", "
								+ newList.get(i));
						previewLabel.setPreferredSize(new Dimension(150, 150));
						previewLabel.setIcon(tempIcon);	
						thumbScrollPane.add(previewLabel);
					}
				}				
				thumbScrollPane.validate();
				thumbScrollPane.repaint();
				thumbScrollPane.updateUI();
			Preview.add(BorderLayout.NORTH, thumbsHead);
			Preview.add(BorderLayout.CENTER, thumbScrollPane);
		ThumbInfo.add(BorderLayout.CENTER, Preview);
	}```

Abfrage auf "null" falls in der Schleife kein Inhalt sein sollte, hab die Maße der Thumbnails abgefragt um das JLabel zusätzlich zu skallieren, hab dieses auch wie jetzt mit festen Maßen versucht und habe ein "System.out.println..." was mir folgendes anzeigt:

B, H, Name: 100, 75, javax.swing.ImageIcon@1bb5c09
B, H, Name: 100, 75, javax.swing.ImageIcon@1976011
B, H, Name: 100, 67, javax.swing.ImageIcon@1242b11
B, H, Name: 75, 100, javax.swing.ImageIcon@137d090
B, H, Name: 100, 75, javax.swing.ImageIcon@15db314
B, H, Name: 100, 75, javax.swing.ImageIcon@97eded
B, H, Name: 100, 75, javax.swing.ImageIcon@858bf1
B, H, Name: 100, 75, javax.swing.ImageIcon@1a68ef9

...also scheint a)tatsächlich etwas anzukommen und b) steht das richtige drin, wenn ich die Konsolenausgabe richtig interpretiere.
Aber leider immernoch das gleiche Ergebnis, es werden keine Bildchen angezeigt im ScrollPane...:grr:

Ich nehm alles zurück, es funktioniert! Wen ich die Idee von Marco13 mal fehlerfrei abgetippt hätte, häts auch schon früher geklappt! :frowning:

Also vielen Dank!!