Eine moderne Android-Anwendung ist ohne Image Loader kaum vorstellbar. Diese übernehmen die harte Arbeit des asynchronen Ladens, der Fehlerverarbeitung, der Platzhalteranzeige, des Cachings und der Bildtransformation. Die Prozessautomatisierung befreit den Entwickler davon, das Rad neu zu erfinden, und gibt ihm die Möglichkeit, sich auf die Geschäftslogik statt auf Boilerplate-Code zu konzentrieren. Im Jahr 2021 sind die Bibliotheken Picasso, Glide und Fresco die am häufigsten in Android-Projekten verwendeten.
Jeder Android-Entwickler hat normalerweise eine bevorzugte Wahl. Dies ist unser Beitrag, um Ihnen bei der Auswahl der besten Option zu helfen.
von Square ist mit dem Ziel entwickelt worden, maximale Einfachheit und Klarheit in die Arbeit zu bringen. Diese Ziele spiegeln sich in der Fähigkeit wider, die APK-Datei klein zu halten – Picasso fügt dem Projekt nur 121 Kb und 849 Methoden hinzu. Jede Medaille hat jedoch zwei Seiten, und dieser begrenzte Raum beeinträchtigt die Funktionalität, die nur grundlegende Operationen mit Bildern abdeckt. Alle Bibliotheksdokumentationen passen problemlos auf eine Webseite.
von Bump Tech basiert auf anderen Prinzipien: leistungsstarke Funktionalität und ein reichhaltiges Arsenal zur Bildverwaltung. Daher vergrößert Glide die Anwendungsgröße um mindestens 440 Kb und 2678 Methoden. Im Gegenzug erhält der Entwickler jedoch eine flexible und benutzerfreundliche Bibliothek zum asynchronen Laden von Bildern.
von Facebook legt den Schwerpunkt auf Produktivität und sorgfältige Nutzung der Betriebssystemressourcen. Es bietet beispielsweise die eher ungewöhnliche Möglichkeit für Entwickler, eine spezielle SimpleDraweeView zur Anzeige von Bildern zu verwenden.
Nun ist es an der Zeit, verschiedene Möglichkeiten zur Hinzufügung von Interaktivität zur Bildanzeige mit Picasso, Glide und Fresco zu unterscheiden. Dieser Artikel behandelt drei Bereiche: den Fade-Effekt, die Anzeige einer Fortschrittsleiste während des Ladens und die Bildrotation nach dem Laden.
Fade-Effekt
Bei Picasso ist die Unterstützung für Animationen nicht die robusteste Funktion, und in den meisten Fällen müssen Benutzer den Animationseffekt selbst hinzufügen. Der native Fade-Effekt ist jedoch bereits implementiert. Alle Bilder, die nicht aus dem RAM geladen werden, erscheinen mit dieser Animation. Es ist nicht möglich, deren Dauer oder andere Eigenschaften zu beeinflussen. Die einzige Option ist das Ein- oder Ausschalten des Effekts.
1 2 3 4 5 6 7 |
Picasso .get() .load(imageUrl) .noFade() .into(test_imageview) |
Glide 4 ermöglicht es, Fade-Effekte mithilfe der transition() Funktion zu definieren. Diese kann als Argument eine Instanz der DrawableTransitionOptions-Klasse übernehmen, die die Parameter für den Übergang zu einem neuen Bild definiert. Insbesondere ermöglicht Glide das Einstellen der Übergangszeit. Im untenstehenden Beispiel haben wir die Fade-Effekt-Zeit auf 5 Sekunden gesetzt.
1 2 3 4 5 6 |
Glide.with(this) .load(imageUrl) .transition(DrawableTransitionOptions.withCrossFade(5)) .into(test_imageview) |
Es sollte auch beachtet werden, dass der Fade-Effekt in Glide zwei Modi hat: Cross-Fades deaktiviert oder aktiviert. Im ersten Modus erscheint ein neues Bild allmählich über dem alten. Dies spart erheblich Ressourcen, kann jedoch problematisch sein, wenn das neue Bild transparente Pixel hat oder kleiner als das alte ist. Im letzteren Fall werden zwei Animationen koordiniert verarbeitet: zuerst verschwindet das alte Bild allmählich, gefolgt von der allmählichen Anzeige des neuen. Dies ergibt einen schönen Effekt, erfordert jedoch viele Ressourcen des Betriebssystems. In großen Listen kann Cross-Fade ein echtes Problem darstellen. Sie können ihn in den Übergangseinstellungen ein- oder ausschalten.
1 2 3 4 5 6 7 8 9 10 11 |
val factory = DrawableCrossFadeFactory .Builder() .setCrossFadeEnabled(true) .build() Glide.with(this) .load(imageUrl) .transition(withCrossFade(factory)) .into(test_imageview) |
Falls gewünscht, kann Glide andere Animationen beim Wechsel zu einem neuen Bild definieren. Alternativ kann ein neues Bild nach links verschoben oder von klein auf Originalgröße vergrößert werden. Um diesen Ansatz umzusetzen, müssen Sie die Animation in einer XML-Datei beschreiben, eine Instanz der GenericTransitionOptions-Klasse mit ihr als Argument für die Konstruktionsfunktion erstellen und diese zur transition() Funktion hinzufügen.
Diese Animation beschreibt den Übergang nach links.
1 2 3 |
<!--?xml version="1.0" encoding="utf-8"?--> |
Das Verwalten des Fade-Effekts mit Fresco ist im Vergleich zu anderen Bibliotheken recht mühelos. Dies kann sowohl im XML-Layout …
… als auch im Activity-/Fragment-Code erfolgen.
1 2 3 4 5 6 7 |
val builder = GenericDraweeHierarchyBuilder(getResources()) val hierarchy = builder .setFadeDuration(3000) .build() fresco_imageview.setHierarchy(hierarchy) |
Gleichzeitig unterstützt Fresco nur eine „immer-aktivierte“ Crossfade-Option, die zu einem höheren Ressourcenverbrauch der Anwendung führen kann.
Ladefortschrittsbalken
Beim Herunterladen großer Bilder oder bei instabiler Internetverbindung kann ein Fortschrittsbalken nützlich sein: entweder unendlich oder den Downloadstatus anzeigend. Bei der Verwendung von Picasso können Sie nur die erste Option implementieren – eine weniger anspruchsvolle Option. Dies kann entweder durch die Verwendung der placeholder() Funktion oder durch das Überschreiben der Target-Klasse erfolgen. Im ersten Fall erstellen wir eine Animation in xml.
1 2 3 |
<!--?xml version="1.0" encoding="utf-8"?--> |
Dann fügen Sie sie einfach als Platzhalter hinzu.
1 2 3 4 5 6 7 |
Picasso .get() .load(imageUrl) .placeholder( R.drawable.infinitive_progressbar) .into(test_imageview) |
Im zweiten Fall fügen wir eine Fortschrittsleiste im Layout über dem ImageView hinzu und erstellen dann eine eigene Klasse, die das Laden des Bildes steuert.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
class ProgressTarget(imageView: ImageView, progressBar: ProgressBar) : com.squareup.picasso.Target { private val ivRef: WeakReference private val progressRef: WeakReference init { ivRef = WeakReference(imageView) progressRef = WeakReference(progressBar) } override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) { val imageView = ivRef.get() imageView?.setImageBitmap(bitmap) val progressBar = progressRef.get() progressBar?.setVisibility(View.GONE) } override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) { val imageView = ivRef.get() imageView?.setImageDrawable(errorDrawable) val progressBar = progressRef.get() progressBar?.setVisibility(View.GONE) } override fun onPrepareLoad(placeHolderDrawable: Drawable) { val imageView = ivRef.get() imageView?.setImageDrawable(placeHolderDrawable) val progressBar = progressRef.get() progressBar?.setVisibility(View.VISIBLE) } } |
Die Verwendung ist äußerst einfach: Erstellen Sie eine Instanz von ProgressTarget mit den erforderlichen Links zu ImageView und ProgressBar und verwenden Sie sie als Argument für die into() Funktion. Der letzte Schritt besteht darin, die ProgressTarget-Instanz als Tag zum ImageView hinzuzufügen, um sie vor dem Garbage Collector zu schützen.
1 2 3 4 5 6 7 8 |
val target = ProgressTarget(test_imageview, test_progressbar) Picasso .get() .load(imageUrl) .into(target) test_imageview.setTag(target) |
Glide bietet einen umfassenderen Ansatz zur Erstellung von Fortschrittsbalken. Der Ansatz ist derselbe wie bei Picasso:
- Animation zur placeholder Funktion hinzufügen (als Option – Verwendung eines GIF-Bildes anstelle einer XML-Animation);
- Die RequestListener-Klasse erweitern.
Außerdem ermöglicht die Flexibilität der Einstellungen dieser Bibliothek, den Ladefortschritt des Bildes abzurufen und nicht nur einen animierten Platzhalter, sondern auch informativere Inhalte anzuzeigen. Dazu müssen Sie die Grundeinstellungen im GlideModule überschreiben. Die hochwertigste Version einer solchen Lösung wurde von Róbert Papp in seinem OkHttpProgressGlideModule vorgeschlagen.
Zweifellos bietet Fresco den einfachsten Weg, das Problem des Fortschrittsbalkens zu lösen. Um einen endlosen Fortschrittsbalken zu erstellen, muss eine Zeile zum XML-Layout hinzugefügt werden …
1 2 3 |
fresco:progressBarImage="@drawable/spinner" |
… oder es im Activity / Fragment Code tun.
1 2 3 |
fresco_imageview.hierarchy.setProgressBarImage(AutoRotateDrawable(icon, 1000)) |
Das Hinzufügen eines Download-Indikators erfordert ebenfalls nicht viel Aufwand. Einfach ProgressDrawable erstellen und bei Bedarf anpassen.
1 2 3 4 5 6 7 |
val progressBarDrawable = ProgressBarDrawable() progressBarDrawable.setColor(Color.YELLOW) progressBarDrawable.setBackgroundColor(Color.BLUE) progressBarDrawable.setRadius(2) fresco_imageview.getHierarchy().setProgressBarImage(progressBarDrawable) |
Bildrotation
Das Hinzufügen von Animationen nach dem Laden des Bildes ist eine seltene, aber durchaus mögliche Option. Lassen Sie uns ohne Umschweife in die Implementierungsdetails für jede der drei Bibliotheken eintauchen.
Picasso hat eine Callback-Klasse, die es ermöglicht, die notwendige Logik nach dem Laden des Bildes auszuführen. Um eine Animation anzuwenden, müssen wir:
- die benötigte Animation im XML-Format bestimmen,
- sie in Activity / Fragment laden,
- ein Callback-Objekt erstellen,
- den Animationswiedergabecode darin einfügen,
- dieses Objekt als Argument zur Funktion into() hinzufügen.
1 2 3 |
<!--?xml version="1.0" encoding="utf-8"?--> |
1 2 3 |
val rotateAnimation = AnimationUtils.loadAnimation(this, R.anim.rotate) |
1 2 3 4 5 6 7 8 9 10 11 |
val callback = object : Callback { override fun onSuccess() { test_imageview.startAnimation(rotateAnimation) } override fun onError(e: java.lang.Exception?) { Log.e(TAG, e?.message) } } |
1 2 3 |
Picasso.get().load(imageUrl).into(test_imageview, callback) |
In Glide können Sie die listener() Funktion verwenden, um ein ähnliches Szenario zu reproduzieren, das ein Objekt der RequestListener-Klasse als Argument übernimmt.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
Glide.with(this) .load(imageUrl) .listener(object : RequestListener { override fun onLoadFailed( e: GlideException?, model: Any?, target: com.bumptech.glide.request.target.Target?, isFirstResource: Boolean ): Boolean { Log.e(TAG, e?.message) return false } override fun onResourceReady( resource: Drawable?, model: Any?, target: com.bumptech.glide.request.target.Target?, dataSource: DataSource?, isFirstResource: Boolean ): Boolean { test_imageview.startAnimation(rotateAnimation) return false } }) .into(test_imageview) |
In Fresco sieht der Aufbau der finalen Animation etwas anders aus. Neben dem Erstellen und Laden des Animationscodes müssen Sie auch zwei Objekte erstellen: BaseControllerListener (reagiert auf den Ladezustand des Bildes) und DraweeController (steuert Anfragen und Rückgaben). Das Gesamtergebnis wird identisch mit den vorherigen Beispielen mit Picasso und Glide sein.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
val frescoListener = object: BaseControllerListener() { override fun onFinalImageSet(id: String?, imageInfo: ImageInfo?, animatable: Animatable?) { fresco_imageview.startAnimation(rotateAnimation) } } val controller = Fresco.newDraweeControllerBuilder() .setUri(imageUrl) .setControllerListener(frescoListener) .build() fresco_imageview.setController(controller) |
Zusammenfassung
Um das Obige zusammenzufassen, hier eine Vergleichstabelle der drei Bibliotheken:
KRITERIUM | ![]() |
![]() |
![]() |
---|---|---|---|
Fade-Effekt-Unterstützung | Außerhalb der Box | Außerhalb der Box | Außerhalb der Box |
Fade-Effekt-Steuerung | Ein-/Ausschaltoption | Dauer anpassen, Cross-Fade-Ansatz, verschiedene Animationen für den Übergang zwischen Bildern definieren | Ein-/Ausschalten, Dauer anpassen |
Unterstützung für Fortschrittsbalken | Keine. Eine separate Fortschrittsansicht erforderlich | Keine. Eine separate Fortschrittsansicht erforderlich / GlideModule neu definieren | Verfügbar. Verschiedene Arten von Fortschrittsbalken sowohl im XML-Layout als auch im Activity / Fragment möglich |
Erstellung eines Fortschrittsbalkens mit Prozentangaben | Unmöglich | Unterstützt bei Verwendung der OkHttp-Bibliothek. GlideModule überschreiben erforderlich | Von der Bibliothek unterstützt |
Mechanismus zur Anzeige der finalen Animation | Callback | RequestListener | BaseControllerListener |
![]() |
|
---|---|
Fade-Effekt-Unterstützung | Außerhalb der Box |
Fade-Effekt-Steuerung | Ein-/Ausschaltoption |
Unterstützung für Fortschrittsbalken | Keine. Eine separate Fortschrittsansicht erforderlich |
Erstellung eines Fortschrittsbalkens mit Prozentangaben | Unmöglich |
Mechanismus zur Anzeige der finalen Animation | Callback |
![]() |
|
---|---|
Fade-Effekt-Unterstützung | Außerhalb der Box |
Fade-Effekt-Steuerung | Dauer anpassen, Cross-Fade-Ansatz, verschiedene Animationen für den Übergang zwischen Bildern definieren |
Unterstützung für Fortschrittsbalken | Keine. Eine separate Fortschrittsansicht erforderlich / GlideModule neu definieren |
Erstellung eines Fortschrittsbalkens mit Prozentangaben | Unterstützt bei Verwendung der OkHttp-Bibliothek. GlideModule überschreiben erforderlich |
Mechanismus zur Anzeige der finalen Animation | RequestListener |
![]() |
|
---|---|
Fade-Effekt-Unterstützung | Außerhalb der Box |
Fade-Effekt-Steuerung | Ein-/Ausschalten, Dauer anpassen |
Unterstützung für Fortschrittsbalken | Verfügbar. Verschiedene Arten von Fortschrittsbalken sowohl im XML-Layout als auch im Activity / Fragment möglich |
Erstellung eines Fortschrittsbalkens mit Prozentangaben | Von der Bibliothek unterstützt |
Mechanismus zur Anzeige der finalen Animation | BaseControllerListener |
So können Sie die Option zur Anzeige von Animationen in allen gängigen Bild-Downloadern für Android-Apps hinzufügen. Die oben besprochenen Bildmanipulationsbibliotheken lassen sich mit den folgenden Vorteilen kurz und bündig beschreiben:
- Glide bietet die beste Funktionalität zur Verwaltung von Übergangseffekten.
- Fresco hat eine gute API zum Erstellen und Anpassen von Fortschrittsbalken.
- Picasso ist einfach zu bedienen, auch für Anfänger.
Die Mechanismen zur Anzeige der finalen Animation sind in allen drei Bibliotheken ähnlich und liefern das gleiche Ergebnis.