(JScrollpane) Laden nur bestimmter Bilder

Guten Tag liebes Byte-Welt-Forum,

Ich bin dabei ein Bildergalerie Programm zu schreiben, und lasse dabei sozusagen unendlich viele [JAPI]JButton[/JAPI]s
zu meinem [JAPI]JScrollPane[/JAPI] hinzufügen.

Das Problem ist dabei, das die [JAPI]JButton[/JAPI] s alle direkt ein Bild zugewiesen bekommen, bei 3,4 Ordnern mit 20 Bildern wäre es ja auch in Ordnung,
aber bei größeren gibt es natürlich Probleme zum Beispiel das Java Heap space Problem.

Deswegen kam mir die Idee Nur die Elemente die im [JAPI]JScrollpane[/JAPI] für den Nutzer sichtbar sind zu laden, und erst wenn er weiter runter scrollt,
die Bilder für die [JAPI]JButton[/JAPI] s die hinzukommen hinzuzufügen und die anderen zu entfernen. Das Problem ist das die [JAPI]JButton[/JAPI] s mit dem [JAPI]GridLayout[/JAPI] hinzugefügt werden und somit ich nicht
mit den Rückgabewerten von Bounds der [JAPI]JButton[/JAPI] s und der Größe des [JAPI]JViewPort[/JAPI]s [JAPI]rectangle[/JAPI] arbeiten kann.

Were nett wenn ihr mir trotz meines komplizierten Satzbaus helfen könnt.

Mit freundlichem Gruß,
MrSmile07

schau dir Lazy loading gallery with Java Swing – Andi’s Blog an

falls du bei GridLayout wirklich keine Position bekommen solltest, schwer zu glauben,
wäre das vielleicht auch nebenher zu berechnen,
Viewport =45% -> Zeile 20 von 50 usw., ruhig bisschen mehr oben und unten


je nach Designwünschen kommt evt. eine JTable in Frage,
rechnet gut automatisch was aktuell zu zeichen ist,
aber dafür sicherlich genug andere neue Aufgaben


bei Eigenimplementierung evtl. auch ganz auf Layout und teure Komponenten verzichten, nur selber painten,
wie bei Gridlayout gleich großer Platz für alle Bilder ist schnell selber gerechnet,
Mausklicks wie Button könnten mit MouseListener und Positionsberechnung einfach zugeordnet werden


erhöht sparsam wäre ein großer eigen-kontrollierer Scrollbalken,
aber JPanel davon unabhängig nur in normaler Anzeigegröße, nicht abertausende Pixel hoch/ breit designt,

allein aus Berechung aktuelle Bilder xy-yz zur Anzeige ausählen, manche ggfs. oben abgeschnitten, andere voll, andere ggfs. unten abgeschnitten

so, bis auf Link am Anfang eher weitere Ideen als Lösungen…

Danke erstmal für die schnelle Antwort.

Ich hatte es eigentlich als Projekt gesehen ohne fremde Programmschnipzel, da ich bei meinem Program auch noch spezifische Methoden dafür existieren.

Und ich kann keine konstante Variable der Höhe nennen, da ja jeder Ordner unterschiedlich viele Dateien hat…


Eine JTable ist Leider nicht ein Komponent den ich aus bestimmten gründen nicht benutzen möchte.


Ich weiß nicht ganz genau was du in diesem Abschnitt meinst.


Ich habe den Scrollbutton des JScrollpanes angepasst falls du das meinst.

Mit freundlichem Gruß,
MrSmile07

*** Edit ***

Hier einfach das bisherige Program.

QuickView

ACHTUNG
Es erzeugt auch in eurem Nutzerordner einen Ordner Namens “Quickview”.

Disclaimer: Nicht entfernen drücken lösch Methoden sind ein bisschen buggy.
Insgesammt ist es noch früher als eine Alpha, also Vorsicht.

ich weiß nun auch nicht ganz, was du damit meinst, aber wer im Forum fragt sollte sich für fremden Code nicht zu schade sein :wink:
musst ja nix übernehmen, nur evtl. Ideen anschauen, wie auf Events reagiert werden kann,
wie aus Position Indexe berechnet und dazu Bilder geladen werden usw.


Und ich kann keine konstante Variable der Höhe nennen, da ja jeder Ordner unterschiedlich viele Dateien hat…

man kann sicher alles variabel machen, ob Scrollbar bei 400 von 2000 oder 400 von 4000 steht ist doch recht egal, von allem kann man weiterrechnen,
oder noch in % umrechnen und von dort weiter


vielleicht nochmal konkretisieren was du möchtest:
allgemein Ideen besprechen
oder Korrekturen in deinem nun geposteten Code, den du vielleicht im Grundaufbau nicht mehr zu stark ändern willst,
oder oder?

[quote=SlaterB]Zitat Zitat von MrSmile07 Beitrag anzeigen
Ich hatte es eigentlich als Projekt gesehen ohne fremde Programmschnipzel, da ich bei meinem Program auch noch spezifische Methoden dafür existieren.
ich weiß nun auch nicht ganz, was du damit meinst, aber wer im Forum fragt sollte sich für fremden Code nicht zu schade sein
musst ja nix übernehmen, nur evtl. Ideen anschauen, wie auf Events reagiert werden kann usw.[/quote]

Ich meinte nach dem Motto copy & paste. :wink: Natürlich brauche ich Code den ich dann auf mein Programm umschreibe.^^

[quote=SlaterB;133058]Und ich kann keine konstante Variable der Höhe nennen, da ja jeder Ordner unterschiedlich viele Dateien hat…
man kann sicher alles variabel machen, ob Scrollbar bei 400 von 2000 oder 400 von 4000 steht ist doch recht egal, von allem kann man weiterrechnen,
oder noch in % umrechnen und von dort weiter[/quote]

Es geht ja darum mit Variablen zu rechnen.^^XDD
Aber ich weiß halt nicht wie ich das machen soll’.^^


Ich habe ja das Programm und nicht den Code geposted (den ich auch nur sehr ungern preisgeben möchte^^)
und ich möchte das Bilder die nicht sichtbar sind nicht geladen werden, sondern erst laden wenn die JButtons sichtbar sind.

Mit freundlichem Gruß,
MrSmile07

Da gibt’s natürlich 1000 Möglichkeiten und Freiheitsgrade.

Erstmal vorneweg: Heap space!? Das klingt verdächtig danach, als würdest du die Bilder in voller Auflösung vorhalten, obwohl auf den Buttons ja wahrscheinlich nur Thumbnails dargestellt werden sollten.

Ansonsten schien die eigentliche Frage ja darauf rauszulaufen, rauszufinden, welche Components (Buttons) gerade im Viewport sichtbar sind. Und gerade bei einem GridLayout sollte das ja recht einfach sein: Alles ist gleich groß. Das kann man eigentlich ja direkt ausrechnen. Nur schnell hingeschrieben:

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;

public class ScrollpaneVisibilityDetection
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(()->createAndShowGUI());
    }
    
    private static final int BUTTON_SIZE_X = 100;
    private static final int BUTTON_SIZE_Y = 100;

    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        JPanel panel = new ScrollablePanel();
        panel.setLayout(new GridLayout(0,3));
        for (int i=0; i<100; i++)
        {
            JButton button = new JButton(String.valueOf(i));
            button.setPreferredSize(
                new Dimension(BUTTON_SIZE_X,BUTTON_SIZE_Y));
            panel.add(button);
        }
        
        JScrollPane scrollPane = new JScrollPane(panel);
        scrollPane.getVerticalScrollBar().addAdjustmentListener(
            new AdjustmentListener()
        {
            @Override
            public void adjustmentValueChanged(AdjustmentEvent e)
            {
                computeVisibleButtonsOn(panel);
            }
        });
        f.getContentPane().add(scrollPane);
        
        f.setSize(600,600);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static void computeVisibleButtonsOn(JPanel panel)
    {
        Rectangle visibleRect = panel.getVisibleRect();
        int y0 = visibleRect.y / BUTTON_SIZE_Y;
        int x0 = visibleRect.x / BUTTON_SIZE_X;
        double nx = (double)visibleRect.width / BUTTON_SIZE_X;
        double ny = (double)visibleRect.height / BUTTON_SIZE_Y;
        int x1 = x0 + (int)Math.ceil(nx);
        int y1 = y0 + (int)Math.ceil(ny);
        
        System.out.println("Visible: "+x0+", "+y0+" to "+x1+", "+y1);
    }
    
    static class ScrollablePanel extends JPanel implements Scrollable
    {
        @Override
        public Dimension getPreferredScrollableViewportSize()
        {
            return getPreferredSize();
        }

        @Override
        public int getScrollableUnitIncrement(
            Rectangle visibleRect, int orientation, int direction)
        {
            return 1;
        }

        @Override
        public int getScrollableBlockIncrement(
            Rectangle visibleRect, int orientation, int direction)
        {
            return 1;
        }

        @Override
        public boolean getScrollableTracksViewportWidth()
        {
            return false;
        }

        @Override
        public boolean getScrollableTracksViewportHeight()
        {
            return false;
        }
        
    }
    
}

(kann man auch an einen anderen Listener hängen, ist nur zur Demo)


Ansonsten noch ein Tipp (trotz des Hinweises zu den “fremden Programmschnipseln” - es ist nicht klar, ob da libraries dazugehören ;-)) : In Google Guava gibt es Caches ( https://github.com/google/guava/wiki/CachesExplained ), die könnten praktisch sein, um immer “so viele Bilder wie möglich” geladen zu haben. Mit ein bißchen Tweaken könnte man da IIRC auch sagen, welche Bilder als erstes aus dem Speicher geworfen werden sollen. Aber natürlich kann man sowas auch mit SoftReference selbst bauen, kann mal ganz interessant sein.

Erstmal danke für die schnelle Antwort,

und Ja also das Problem ist ja ich skalierte die Bilder alle runter, aber man hat ja so um die 1000 Bilder auf dem Rechner und jede Menge Ordner weswegen dann der Heapspace sich in den Weg stellt.

Ja… ok, auf die Idee bin ich jetzt nicht gekommen^^, ist aber genau das was ich suchte, herzlichen dank das du dir die Mühe dafür genommen hast.^^
Die Frage ist jetzt eigentlich nur, wie ich das mit dem Bilder laden jetzt löse… .

Also beim Button erzeugen rufe ich ja von meinem Custom [JAPI]JButton[/JAPI] diese Methode auf: (Das Icon wird durch eine andere Methode für die Klasse gesetzt)

private void load(){
		if(!imgloaded){
			if(imageForOne.getIconHeight() >= imageForOne.getIconWidth()){
				if(imageForOne.getIconHeight() != imageForOne.getIconWidth()){
					imageForOne.setImage(imageForOne.getImage().getScaledInstance(this.getWidth(), this.getHeight() - imageForOne.getIconHeight(), Image.SCALE_FAST));
				}else{
					imageForOne.setImage(imageForOne.getImage().getScaledInstance(this.getWidth(), this.getHeight(), Image.SCALE_FAST));
				}
			}else{
				imageForOne.setImage(imageForOne.getImage().getScaledInstance(this.getWidth() + (imageForOne.getIconWidth()/2), this.getHeight(), Image.SCALE_FAST));
			}
		
			img = imageForOne.getImage();
			imgloaded = true;
		}
		repaint();
	}

Ich weiß jetzt nur nicht wie ich das genau machen soll… .


Ja ich meinte auch Libaries die nicht zum Standart gehören Ich möchte es selbst schreiben, weil ich die dann auch auf meine Bedürfnisse verändern kann, ich weiß ich könnte da auch die API nachschalgen, aber da sind dann 3000 Methoden drin, die ich gar nicht brauche und nur das Programm noch verlangsamen…^^

Mit freundlichem Gruß,
MrSmile07

*** Edit ***

Achso der Code setzt übrigens die Image Größe, ich muss die Bilder so komisch skalieren, denn sonst kriege ich sowas wie ein Zuschneid Effekt nicht hin…^^

Nebenbei meine Idee/Inspiration kommt von der Handy App QuickPic, falls es euch am Verständniss fehlt, was ich probiere zu machen, obwohl mein Programm das ich schon geposted habe
das eigentlich erklären sollte.^^

Das mit dem eigenen JButton und der Load-Methode klingt auch etwas suspekt … und der Code sieht noch suspekter aus :wink: (Auch wenn unklar ist, was z.B. „imageForOne“ ist…)

SlaterB hatte ja schon die Option in den Raum gestellt, die Images „händisch“ selbst zu zeichnen. Das könnte ein paar Vorteile bringen (z.B. in dem Sinne, wie du es weiter unten angedeutet hast, „Flexibilität“, aber dazu später). Aber notwendig ist es natürlich nicht, und wenn man einige der Funktionen eines Buttons ausnutzen will, kann der Button OK sein (ich denke hier z.B. an Fokus, Traversal, ActionListeners usw…).

Es stellen sich anhand der bisherigen Beschreibung ein paar Fragen. Z.B. Wer ist dafür verantwortlich, dass das Laden eines Bildes „getriggert“ wird? Wo wird das „Mapping“ zwischen den Buttons und den Bildern gespeichert (also woher weiß man, welches Bild auf welchen Button gehört)? Wo wird der Ladestatus von Bildern verwaltet? (Es gibt da dieses „imgloaded“, aber das alleine kann es ja nicht sein - irgendwann müssen Bilder ja auch wieder „ge-UN-loaded“ werden, wenn der Speicher voll ist…)

Das ist anmaßend, oder Unfug (oder beides :D). Durch mehr Methoden wird das Programm nicht „langsamer“. Die Performance hängt von GANZ anderen Dingen ab (siehe auch unten).

Was damit gemeint ist, ist nicht ganz klar. Ich würde erwarten, dass die Thumbnails das Seitenverhältnis der Bilder erhalten (was also „Zuschneiden“?). Unabhängig davon: Das Skalieren der Bilder sollte man anders machen. „getScaledInstance“ ist in vieler Hinsicht „schlecht“ - insbesondere kann die Performance davon furchtbar sein. Für eine bessere Alternative kannst du mal in diese Antwort schauen: java - How to improve the performance of g.drawImage() method for resizing images - Stack Overflow . Einen einfachen Performancevergleich hatte ich etwas weiter unten, in java - How to improve the performance of g.drawImage() method for resizing images - Stack Overflow gemacht.

(In bezug auf oben, beachte aber, dass das eine der wenigen Ausnahmen ist, wo etwas „selbstgebautes“ tatsächlich deutlich schneller ist, als das, was durch die Standard-Library angeboten wird)

ImageForOne war glaube ich ein Image Objekt. (Sry sitze in der Bahn am Handy)

Was meinst du mit “händisch nachzeichnen”?

Und ja ich brauche die Buttons wegen den Listenern etc.

Für das Laden ist der Konstruktor Verantwortlich der eine Linked Hashset von Typ Directory aus ließt (Eigener Typ Mit Pfad des Bildes des Ordnernamens und noch etwas).

Beim Konstruktor aufruf geht er die LinkedHashset durch und weißt wärend er die durchließt einem neuem Button das Bild und den Namen zu.

Wie man die “unloaded” weiß ich leider nicht, ich würde einfach mein Element das Icon auf einen Unsinigen Pfad setzen wie z.B. “Bla” so das es nichtmehr angezeigt wird.

Ich war auch schon am überlegn das mit g.draw zu verkleinern zudem ich das eh schon gebrauche um das Bild darzustellen mit Deko.(siehe einfach mein Programm im jetztigem Status (download oben))

Ich hoffe ich konnte es jetzt verständlicher nahebringen.

Mit freundlichem Gruß,
MrSmile07

PS: Sry für alle Rechtschreibfehler und ungenaue bezeichnungen, da ich mich in der Bahn befinde und das nur schwer alles kontrollieren kann.

Gesendet von meinem LG-H735 mit Tapatalk

[OT]wieso genau macht das ding nen screenshot bevor es startet? ^^[/OT]

[OT]Ich frage mich zwar warum du das weißt. (Wahrscheinlich durch den Ordner, aber trztdm)
Das ist dafür, das wenn man ein Bild geöffnet hat man “C” für den Covermodus drücken kann. Zeigt dann den Desktop an, das ist für ungebetende Gäste, damit die nicht sehen hust was du z.B. offen hast.
Genauso gibt es z.B. Wenn du “L” bei einem offenem Bild drückst den Lightmodus, der für dunkle Bilder ist, damit man sie erkennen kann.
Oder auch Die null bei einem offenen Bild um auf die Orginalgröße zu gehen. Und so weiter…
[/OT]

Mit freundlichem Gruß,
MrSmile07

Gesendet von meinem LG-H735 mit Tapatalk

Damit meinte ich, dass man auch eine eigene Klasse (z.B. von JPanel abgeleitet) machen könnte, wo man die Thumbnails selbst mit graphics.drawImage(…) reinmalt. Auch das Anklicken könnte man da mit einem eigenen MouseListener regeln. Aber das ist wohl nicht so wichtig.

Wie man die „unloaded“ weiß ich leider nicht, ich würde einfach mein Element das Icon auf einen Unsinigen Pfad setzen wie z.B. „Bla“ so das es nichtmehr angezeigt wird.

Das klingt auch wieder hakelig. Das Icon auf „null“ setzen wäre eher eine Option, aber auch da muss man sich überlegen, wer diesen Status überwacht, wer mitkriegt, welche „null“ sind, und welche noch geladen werden müssen.

PS: Sry für alle Rechtschreibfehler und ungenaue bezeichnungen, da ich mich in der Bahn befinde und das nur schwer alles kontrollieren kann.

Ist es sooo eilig? „Zeit haben“ ist oft nur der Wille, sich Zeit zu nehmen

Achso ich benutze ja einen Button dafür weil es da einfacher ist die gesammten Events abzufangen und sie z.B. auf disabled zu setzen.

Genau da hapert es bei mir ja… Ich muss gucken wer das überwachen soll, weil das Scrollpane an sich… Naja^^

Nee, Zeit habe ich genug, ich möchte aber auch allen so schnell wie möglich antworten, damit sie auch verstehen was ich meine damit ich schneller das Problem lösen kann.^^

Mit freundlichem Gruß,
MrSmile07

[quote=MrSmile07]Deswegen kam mir die Idee Nur die Elemente die im JScrollpane für den Nutzer sichtbar sind zu laden, und erst wenn er weiter runter scrollt,
die Bilder für die JButton s die hinzukommen hinzuzufügen und die anderen zu entfernen. Das Problem ist das die JButton s mit dem GridLayout hinzugefügt werden und somit ich nicht
mit den Rückgabewerten von Bounds der JButton s und der Größe des JViewPorts rectangle arbeiten kann.[/quote]

ich habe jetzt nicht alle antworten gelesen. aber das problem verstehe ich trotzdem nicht ganz.
Was ist deine Frage - wie du an den Wert kommst, wo der user gerade hingescrollt hat, also welche
bilder du jetzt laden sollst, oder?.. könntest du das nochmal irgendwie anders formulieren?

[OT]ja ich hatte tatsächlich das programm mal geöffnet und mir dann den ordner angeschaut. aber wieso
lässt du nicht einfach das fenster verschwinden wenn man c drückt? so ein desktop screenshot sieht nicht gerade
glaubwürdig aus :confused:[/OT]

Mein Problem ist das ich nicht weiß wo ich jetzt den Listener dran packen soll, damit ich nur die Bilder lade die sichtbar sind.

[OT]
Ich benutze dewegen ein Screenshot, weil das auf jedem Betriebssystem unterstützt wird, weil man einfach “C” wieder drücken kann damit man wieder zurück kommt. Und bei dem minimieren man sonsr ja das Icon des Programmes sehen würde.[/OT]

Mit freundlichem Gruß,
MrSmile07

Gesendet von meinem LG-H735 mit Tapatalk

Die Frage ist sehr allgemein (wie auch schon die bisherigen). Deswegen auch nur der allgemeine Tipp: Es gibt einen Unterschied zwischen “Software entwickeln” und “Code hinschreiben, der irgendwas macht”. Wenn du keine (also nichtmal eine spontante) Idee hast, wie du die Bilder und ihren Sichtbarkeits- und Ladestatus verwalten kannst, ist nicht klar, was da rauskommen soll. Angenommen man sieht die ersten 10 von 100 Bildern. Dann sollen die geladen werden. Das sollte ggf. im Hintergrund passieren, d.h. in einem eigenen Thread. SwingWorker würde sich da erstmal anbieten, aber auch da stellt sich noch die Frage: Wenn der User dann “Page Down” drückt, und man nicht mehr die erste 10 Bilder sieht, sondern die Bilder 11-20. Dann sollen ja DIE geladen werden, und nichtmehr die 0-10. Das ganze müßte/könnte/sollte sich recht dynamisch anpassen. Aber solange die Ziele nicht klar (oder nicht konkret) sind, ist es schwer, konkrete Hinweise zu geben.

Genau was du da geschrieben hast ist mein Problem, ich weiß halt nicht, wer über die Komponenten wachen soll.
Außerdem weiß ich nicht wie ich die Bilder entlade, mit null setzen ist es doch nicht getan, oder?

Mit freundlichem Gruß,
MrSmile07

[quote=MrSmile07]Außerdem weiß ich nicht wie ich die Bilder entlade, mit null setzen ist es doch nicht getan, oder?[/quote]Doch, wir sind ja bei Java und nicht bei C++…

Wenn ein Objekt vom ETD (oder main) aus nicht mehr erreichbar ist wird es vom GC abgeräumt.

Darüber hinaus gibt es noch [JAPI]WeakReference[/JAPI], womit man dem GC sagt:

„Es ist nicht so schlimm, wenn ich das referenzierte Objekt neu anlegen muss, ich rechne damit, dass es NULL sein könnte.“

bye
TT

Danke für die Antwort.

Jetzt muss ich nurnoch wissen an was ich den Listener klemmen muss, da am Scrollpane das ziemlich komisch ist, weil man da ja nur abhören kann wenn es sich verändert, und nicht z.B. wie es am Anfang ist…

Aber ich glaube ich gucke mir das mal morgen wenn ich am PC bin genauer an…

Vllt. finde ich ja auch die Lösung beim nochmaligen durchstöbern des Quelltextes von @Marco13 .

Mit freundlichem Gruß,
Aron

PS: Gute Nacht (Wer das auch immer so spät noch liest^^)

Gesendet von meinem LG-H735 mit Tapatalk

Nun, auf das gepostete Beispiel bezogen kannst du “computeVisibleButtonsOn” auch direkt am Anfang aufrufen (und nicht nur bei Änderungen). Aber trotzdem sind natürlich noch viele Fragen offen.