setBackgroundDrawable macht ListView langsam

  • Hi,


    ich suche nun schon eine gefühlte Ewigkeit, kann aber keine Antwort finden.


    Ich setze mit setBackgroundDrawable(Drawable) ein Bild als Hintergrund meiner ListView. Im Prinzip klappt das ganze auch, aber die ListView wird dadurch irgendwie extrem lahm. Soll heißen: Das Scrollen läuft einfach nicht mehr flüssig.


    Habe das ganze mal in einem einfachen Beispiel zusammengefasst, wo das Problem auftritt:




    Das Verhalten ist eindeutig. Bei geladenem Hintergrund-Bild scrollt die ListView überhaupt nicht flüssig. Entferne ich den Hintergrund (im Beispiel-Code einfach durch Clicken auf die ListView), funktioniert scrollen wieder einwandfrei und total flüssig.


    Aber woran liegt das? Wird das Drawable evtl. wiederholt gezeichnet, obwohl es sich eigentlich nicht verändert? Und wird es dabei vielleicht immer erst skaliert?
    Habe auch mal ein ScaleDrawable Objekt genommen, half aber auch nicht.



    Hoffe jemand kann helfen.


    Danke schonmal!

  • In der aktuellen c't (6/2012) ist ein Artikel zur "gefühlten Geschwindigkeit" von Smartphones/Tables im Vergleich zu PCs - die Geschwindigkeit beruht vor allem auf Tricks in der Vorausberechnung der Anzeigelemente. Wenn jedoch jedesmal die Drawables neu berechnet werden müssen, ist ein ARM-Prozessor schnell an seinen Grenzen.


    Es stellt sich also die Frage, wie aufwändig deine Drawables sind und ob du dem System durch irgendwelche Anpassungen am Layout unter die Arme greifen kannst.


    Erste Vorschlag wäre, den Hintergrund auf die List-Elemente zu verteilen...

  • Hi,


    erstmal danke für deine Antwort!


    Erste Vorschlag wäre, den Hintergrund auf die List-Elemente zu verteilen...


    Hmm, ich wüsste nicht, wie das gehen soll!? Ich müsste das Bild ja dann in entsprechende Teile aufteilen und jedem List-Element einen dieser Teile zuordnen. Beim scrollen ergäbe sich dann jedoch das Problem, dass die Anordnung komplett geändert werden müsste, sobald ein Item den sichtbaren Bereich verlässt und ein neues diesen erreicht. Das klingt irgendwie nach erheblichem Aufwand. Oder habe ich dich falsch verstanden?


    Es stellt sich also die Frage, wie aufwändig deine Drawables sind und ob du dem System durch irgendwelche Anpassungen am Layout unter die Arme greifen kannst.


    Die Drawables selber werden in meiner Anwendung vom Benutzer ausgewählt, ich habe also keinen Einfluss auf diese. Eine programmiertechnische Anpassung der Drawables im Vorraus, also bevor sie als Hintergrund gesetzt werden, wäre allerdings eine Option. Nur wie mache ich das? Also wie erstelle ich aus einem Drawable (sagen wir 1 MB png file mit Auflösung 1000x1000) ein anderes Drawable mit besseren Eigenschaften (z.B. 100KB mit Auflösung 200x200). Werde das mal versuchen herauszufinden.


    Welche Anpassungen am Layout meinst du? Habe mal eine eigene Klasse, die Drawable implementiert, gebastelt und die Aufrufe von draw(Canvas) gezählt. Es scheint in der Tat so zu sein, dass genau hier der Flaschenhals liegt. Allerdings kam ich mit ein wenig Rumspielerei schnell zu dem Schluss, dass es keinen Sinn macht die Anzahl der Aufrufe von draw(Canvas) in irgendeiner Weise zu beeinflussen. Schon als ich einfach jeden zweiten Aufruf wegließ, war die optische Ausgabe quasi ruiniert (flackern). Und die Performance war zwar etwas besser, aber immernoch total unzufriedenstellend.

  • Ok, update:


    Mittels Bitmap.createScaledBitmap(..) habe ich mir zuerst ein skaliertes Drawable erstellt und das dann als Hintergrund verwendet. Das Ergebnis war sehr enttäuschend, denn selbst mit Breite und Länge auf 10 Pixel gesetzt (das Bild war schon garnicht mehr zu erkennen), war das Scrollen immer noch sehr träge.


    Allerdings habe ich nun eine Lösung gefunden. Ich verzichte komplett auf setBackgroundDrawable. Stattdessen habe ich dem Layout meiner ListView (im obigen Beispiel nicht vorhanden) als rootelement einen FrameLayout hinzugefügt und als childs eben die ListView sowie eine ImageView, die beide mittels match_parent den gesamten Raum einnehmen.
    Dann noch die ListView mittels setCacheColorHint(0) transparent gestaltet und mittels imageView.setDrawingCacheEnabled(true) das Cachen des Images eingeschaltet.


    Nun klappt es wunderbar, wobei man sich natürlich fragt, wieso setBackground kein solches Caching ermöglicht!?

  • Ich weiß nicht, ob ich dir den Zusammenhang jetzt korrekt darstelle, aber nach meinem Verständnis von Android macht dein Ergebnis schon Sinn.


    Ein ListView baut seine ListItems bei Bedarf öfter neu auf - und muss dann (bei eigenem Background) nach seinem entsprechenden Background-Abschnitt suchen, damit er den Vordergrund korrekt drauf plazieren kann.


    Wenn der ListView aber durchsichtig ist (dein FrameLayout-Background ist ja unterhalb), dann kann er seinen View komplett aufbauen und in einem Stück auf den Background rendern.


    Wie gesagt - ich weiß nicht, ob das dem exakten Ablauf in Android entspricht, aber es würde das Verhalten genau erklären.


    Vielen Dank für die Frage - ich denke mal, andere werden davon bestimmt auch profitieren!!!


    Wir merken uns also: B-)
    ListView bekommt bitte keinen hübschen Background - den verpassen wir einem FrameLayout (o.ä), das wir da drum packen!
    /edit/ Oder wir setzen den Background für die Activity, wie im nachfolgenden Beitrag beschrieben. Gruß an killphil!!!

  • Ich hatte dieses seltsame Verhalten auch und hab beim stöbern im Netz auch Hinweise darauf gefunden, das der Listview massive Probleme mit Transparenzen und Hintergrundbildern haben soll.


    Ich habe das ganze bei mir ohne zusätzliche Container gelöst, sondern den Weg über ein Style (für die jeweilige Activity wo das Hintergrundbild benötigt wird) genutzt.


    Code
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    	<style name="KStyle" parent="android:Theme.Light.NoTitleBar">
     
        	<item name="android:windowBackground">@drawable/backg_rbfans</item>
            	
    	</style>
    </resources>


    Dann im Layout des jeweiligen Listviews per


    Code
    android:cacheColorHint="#00000000"



    den Background auf Transparent geschaltet und schon klappt es mit dem Hintergrund.

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!