J'utilise un ListView
pour afficher certaines images et légendes associées à ces images. Je reçois les images d'Internet. Existe-t-il un moyen de charger paresseusement les images de sorte que, pendant l'affichage du texte, l'interface utilisateur n'est pas verrouillée et les images sont affichées au fur et à mesure qu'elles sont téléchargées?
Le nombre total d'images n'est pas fixé.
Voici ce que j'ai créé pour contenir les images que mon application affiche actuellement. Veuillez noter que l'objet "Journal" utilisé ici est mon wrapper personnalisé pour la classe de journalisation finale dans Android.
package com.wilson.Android.library;
/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.Apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
import Java.io.IOException;
public class DrawableManager {
private final Map<String, Drawable> drawableMap;
public DrawableManager() {
drawableMap = new HashMap<String, Drawable>();
}
public Drawable fetchDrawable(String urlString) {
if (drawableMap.containsKey(urlString)) {
return drawableMap.get(urlString);
}
Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
try {
InputStream is = fetch(urlString);
Drawable drawable = Drawable.createFromStream(is, "src");
if (drawable != null) {
drawableMap.put(urlString, drawable);
Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
+ drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
+ drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
} else {
Log.w(this.getClass().getSimpleName(), "could not get thumbnail");
}
return drawable;
} catch (MalformedURLException e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
} catch (IOException e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
}
}
public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
if (drawableMap.containsKey(urlString)) {
imageView.setImageDrawable(drawableMap.get(urlString));
}
final Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
imageView.setImageDrawable((Drawable) message.obj);
}
};
Thread thread = new Thread() {
@Override
public void run() {
//TODO : set imageView to a "pending" image
Drawable drawable = fetchDrawable(urlString);
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
};
thread.start();
}
private InputStream fetch(String urlString) throws MalformedURLException, IOException {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet request = new HttpGet(urlString);
HttpResponse response = httpClient.execute(request);
return response.getEntity().getContent();
}
}
J'ai fait ne simple démonstration d'une liste paresseuse (située sur GitHub) avec des images.
Utilisation de base
ImageLoader imageLoader=new ImageLoader(context); ... imageLoader.DisplayImage(url, imageView);
N'oubliez pas d'ajouter les autorisations suivantes à votre fichier AndroidManifest.xml:
<uses-permission Android:name="Android.permission.INTERNET"/> <uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE"/> Please
créez une seule instance d'ImageLoader et réutilisez-la dans votre application. De cette façon, la mise en cache des images sera beaucoup plus efficace.
Cela peut être utile à quelqu'un. Il télécharge des images dans le fil de fond. Les images sont mises en cache sur une carte SD et en mémoire. L'implémentation du cache est très simple et suffit pour la démo. Je décode des images avec inSampleSize pour réduire la consommation de mémoire. J'essaie également de gérer correctement les vues recyclées.
Je recommande un instrument open source niversal Image Loader. Il est à l'origine basé sur le projet de Fedor Vlasov LazyList et a été grandement amélioré depuis.
Multithreading For Performance , tutoriel de Gilles Debunne.
Cela provient du Android Blog des développeurs. Le code suggéré utilise:
AsyncTasks
.FIFO cache
.garbage collect
- facilement.Drawable
pendant le téléchargement.Mise à jour: Notez que cette réponse est plutôt inefficace pour le moment. Le ramasse-miettes agit de manière agressive sur SoftReference et WeakReference, de sorte que ce code n'est PAS adapté aux nouvelles applications. (Au lieu de cela, essayez des bibliothèques comme niversal Image Loader suggérées dans d'autres réponses. )
Merci à James pour le code et à Bao-Long pour la suggestion d'utiliser SoftReference. J'ai implémenté les modifications de SoftReference sur le code de James. Malheureusement, SoftReferences a entraîné le ramassage trop rapide de mes images. Dans mon cas, tout se passait bien sans SoftReference, car la taille de ma liste est limitée et mes images sont petites.
Il y a une discussion d'il y a un an concernant les SoftReferences sur les groupes de Google: lien vers le fil . En guise de solution à la récupération de place trop précoce, ils suggèrent la possibilité de définir manuellement la taille de segment VM à l'aide de dalvik.system.VMRuntime.setMinimumHeapSize (), ce qui ne m'intéresse pas beaucoup.
public DrawableManager() {
drawableMap = new HashMap<String, SoftReference<Drawable>>();
}
public Drawable fetchDrawable(String urlString) {
SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
if (drawableRef != null) {
Drawable drawable = drawableRef.get();
if (drawable != null)
return drawable;
// Reference has expired so remove the key from drawableMap
drawableMap.remove(urlString);
}
if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
try {
InputStream is = fetch(urlString);
Drawable drawable = Drawable.createFromStream(is, "src");
drawableRef = new SoftReference<Drawable>(drawable);
drawableMap.put(urlString, drawableRef);
if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
+ drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
+ drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
return drawableRef.get();
} catch (MalformedURLException e) {
if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
} catch (IOException e) {
if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
}
}
public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
if (drawableRef != null) {
Drawable drawable = drawableRef.get();
if (drawable != null) {
imageView.setImageDrawable(drawableRef.get());
return;
}
// Reference has expired so remove the key from drawableMap
drawableMap.remove(urlString);
}
final Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
imageView.setImageDrawable((Drawable) message.obj);
}
};
Thread thread = new Thread() {
@Override
public void run() {
//TODO : set imageView to a "pending" image
Drawable drawable = fetchDrawable(urlString);
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
};
thread.start();
}
Picasso
Utilisez la bibliothèque Picasso de Jake Wharton. (Une bibliothèque Perfect ImageLoading constitue le développeur d’ActionBarSherlock)
Une puissante bibliothèque de téléchargement et de mise en cache d'images pour Android.
Les images ajoutent un contexte et une touche visuelle indispensables aux applications Android. Picasso permet de charger des images sans problème dans votre application, souvent en une seule ligne de code!
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
De nombreux pièges courants liés au chargement d'images sur Android sont gérés automatiquement par Picasso:
Gestion du recyclage d'ImageView et de l'annulation du téléchargement dans un adaptateur. Transformations d'images complexes avec une utilisation minimale de la mémoire. Mémoire automatique et mise en cache du disque.
Bibliothèque de Picasso Jake Wharton
Glide
Glide est un cadre de gestion des supports open source rapide et efficace pour Android qui englobe le décodage des supports, la mise en cache de la mémoire et du disque et le regroupement des ressources dans une interface simple et facile à utiliser.
Glide prend en charge la récupération, le décodage et l'affichage d'images fixes, d'images et de GIF animés. Glide comprend une API flexible qui permet aux développeurs de se connecter à presque n'importe quelle pile réseau. Par défaut, Glide utilise une pile personnalisée basée sur HttpUrlConnection, mais inclut également des bibliothèques d’utilitaires connectées au projet Volley de Google ou à la bibliothèque OkHttp de Square.
Glide.with(this).load("http://goo.gl/h8qOq7").into(imageView);
L'objectif principal de Glide est de rendre le défilement de toutes les listes d'images aussi fluide et rapide que possible, mais Glide est également efficace dans presque tous les cas où vous devez extraire, redimensionner et afficher une image distante.
bibliothèque de chargement d'images Glide
Fresque par Facebook
Fresco est un système puissant permettant d'afficher des images dans les applications Android.
Fresco s'occupe du chargement et de l'affichage des images, pour que vous n'ayez pas à le faire. Il charge les images du réseau, du stockage local ou des ressources locales et affiche un espace réservé jusqu'à l'arrivée de l'image. Il a deux niveaux de cache; un en mémoire et un autre en mémoire interne.
Dans Android 4.x et versions antérieures, Fresco place les images dans une région particulière de la mémoire Android. Cela permet à votre application de s'exécuter plus rapidement et de subir beaucoup moins souvent l'effroyable erreur OutOfMemoryError.
Chargeur haute performance - après avoir examiné les méthodes suggérées ici, j'ai utilisé la solution de Ben avec quelques modifications -
J'ai réalisé que travailler avec drawables est plus rapide qu'avec les bitmaps, donc j'utilise drawables à la place.
Utiliser SoftReference est une bonne chose, mais l'image en mémoire cache est supprimée trop souvent. J'ai donc ajouté une liste liée contenant les références des images, empêchant ainsi la suppression de l'image jusqu'à ce qu'elle atteigne une taille prédéfinie.
Pour ouvrir InputStream, j'ai utilisé Java.net.URLConnection, ce qui me permet d'utiliser le cache Web (vous devez d'abord définir un cache de réponses, mais c'est une autre histoire).
Mon code:
import Java.util.Map;
import Java.util.HashMap;
import Java.util.LinkedList;
import Java.util.Collections;
import Java.util.WeakHashMap;
import Java.lang.ref.SoftReference;
import Java.util.concurrent.Executors;
import Java.util.concurrent.ExecutorService;
import Android.graphics.drawable.Drawable;
import Android.widget.ImageView;
import Android.os.Handler;
import Android.os.Message;
import Java.io.InputStream;
import Java.net.MalformedURLException;
import Java.io.IOException;
import Java.net.URL;
import Java.net.URLConnection;
public class DrawableBackgroundDownloader {
private final Map<String, SoftReference<Drawable>> mCache = new HashMap<String, SoftReference<Drawable>>();
private final LinkedList <Drawable> mChacheController = new LinkedList <Drawable> ();
private ExecutorService mThreadPool;
private final Map<ImageView, String> mImageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
public static int MAX_CACHE_SIZE = 80;
public int THREAD_POOL_SIZE = 3;
/**
* Constructor
*/
public DrawableBackgroundDownloader() {
mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
}
/**
* Clears all instance data and stops running threads
*/
public void Reset() {
ExecutorService oldThreadPool = mThreadPool;
mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
oldThreadPool.shutdownNow();
mChacheController.clear();
mCache.clear();
mImageViews.clear();
}
public void loadDrawable(final String url, final ImageView imageView,Drawable placeholder) {
mImageViews.put(imageView, url);
Drawable drawable = getDrawableFromCache(url);
// check in UI thread, so no concurrency issues
if (drawable != null) {
//Log.d(null, "Item loaded from mCache: " + url);
imageView.setImageDrawable(drawable);
} else {
imageView.setImageDrawable(placeholder);
queueJob(url, imageView, placeholder);
}
}
private Drawable getDrawableFromCache(String url) {
if (mCache.containsKey(url)) {
return mCache.get(url).get();
}
return null;
}
private synchronized void putDrawableInCache(String url,Drawable drawable) {
int chacheControllerSize = mChacheController.size();
if (chacheControllerSize > MAX_CACHE_SIZE)
mChacheController.subList(0, MAX_CACHE_SIZE/2).clear();
mChacheController.addLast(drawable);
mCache.put(url, new SoftReference<Drawable>(drawable));
}
private void queueJob(final String url, final ImageView imageView,final Drawable placeholder) {
/* Create handler in UI thread. */
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
String tag = mImageViews.get(imageView);
if (tag != null && tag.equals(url)) {
if (imageView.isShown())
if (msg.obj != null) {
imageView.setImageDrawable((Drawable) msg.obj);
} else {
imageView.setImageDrawable(placeholder);
//Log.d(null, "fail " + url);
}
}
}
};
mThreadPool.submit(new Runnable() {
@Override
public void run() {
final Drawable bmp = downloadDrawable(url);
// if the view is not visible anymore, the image will be ready for next time in cache
if (imageView.isShown())
{
Message message = Message.obtain();
message.obj = bmp;
//Log.d(null, "Item downloaded: " + url);
handler.sendMessage(message);
}
}
});
}
private Drawable downloadDrawable(String url) {
try {
InputStream is = getInputStream(url);
Drawable drawable = Drawable.createFromStream(is, url);
putDrawableInCache(url,drawable);
return drawable;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private InputStream getInputStream(String urlString) throws MalformedURLException, IOException {
URL url = new URL(urlString);
URLConnection connection;
connection = url.openConnection();
connection.setUseCaches(true);
connection.connect();
InputStream response = connection.getInputStream();
return response;
}
}
J'ai suivi cette Android formation et je pense que le téléchargement d’images est excellent, sans bloquer l’interface utilisateur principale. Il gère également la mise en cache et le défilement de nombreuses images: Chargement efficace de grandes images bitmap
1. Picasso permet des tracas chargement gratuit des images dans votre application, souvent dans une seule ligne de code!
Utilisez Gradle:
implementation 'com.squareup.picasso:picasso:2.71828'
Juste une ligne de code!
Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);
2. Glide Une image en cours de chargement et bibliothèque de mise en cache pour Android axée sur le défilement régulier
Utilisez Gradle:
repositories {
mavenCentral()
google()
}
dependencies {
implementation 'com.github.bumptech.glide:glide:4.7.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
}
// Pour une vue simple:
Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(imageView);
3. fresque est un système puissant pour afficher des images dans Android applications.Fresco se charge du chargement et de l'affichage des images, pour que vous n'ayez pas à le faire.
J'ai écrit un didacticiel qui explique comment charger par la fenêtre des images dans une liste. Je vais dans quelques détails sur les questions de recyclage et de concurrence. J'utilise également un pool de threads fixe pour éviter de générer beaucoup de threads.
Pour ce faire, je lance un fil permettant de télécharger les images en arrière-plan et de lui remettre un rappel pour chaque élément de la liste. Lorsque le téléchargement d’une image est terminé, elle appelle le rappel qui met à jour la vue de l’élément de la liste.
Cette méthode ne fonctionne cependant pas très bien lorsque vous recyclez des vues.
Je veux juste ajouter un autre bon exemple, adaptateurs XML. Comme il est utilisé par Google, j'utilise également la même logique pour éviter une erreur OutOfMemory.
En gros this ImageDownloader est votre réponse (car elle couvre la plupart de vos besoins). Vous pouvez également en implémenter certains.
J'utilise NetworkImageView à partir du nouveau Android Volley Library com.Android.volley.toolbox.NetworkImageView
et il semble fonctionner assez bien. Apparemment, il s'agit de la même vue que celle utilisée dans Google Play et dans d'autres nouvelles applications Google. Vaut vraiment le détour.
Il s'agit d'un problème courant sur Android qui a été résolu à bien des égards par de nombreuses personnes. À mon avis, la meilleure solution que j'ai vue est la bibliothèque relativement nouvelle appelée Picasso . Voici les faits saillants:
Jake Wharton
de ActionBarSherlock gloire.ListView
Le temps de chargement des images à partir d’Internet offre de nombreuses solutions. Vous pouvez également utiliser la bibliothèque Android-Query . Cela vous donnera toute l'activité requise. Assurez-vous de ce que vous voulez faire et lisez la page wiki de la bibliothèque. Et résolvez la restriction de chargement des images.
Ceci est mon code:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
ImageView imageview = (ImageView) v.findViewById(R.id.icon);
AQuery aq = new AQuery(convertView);
String imageUrl = "http://www.vikispot.com/z/images/vikispot/Android-w.png";
aq.id(imageview).progress(this).image(imageUrl, true, true, 0, 0, new BitmapAjaxCallback() {
@Override
public void callback(String url, ImageView iv, Bitmap bm, AjaxStatus status) {
iv.setImageBitmap(bm);
}
));
return v;
}
Il devrait être résoudre votre problème de chargement paresseux.
Je pense que ce problème est très populaire parmi les développeurs Android, et il existe de nombreuses bibliothèques de ce type qui prétendent résoudre ce problème, mais seules quelques-unes d’entre elles semblent être d'actualité. AQuery est l'une de ces bibliothèques, mais elle est meilleure que la plupart d'entre elles à tous les égards et vaut la peine d'être essayée.
Vous devez essayer ce chargeur universel est le meilleur. J'utilise ceci après avoir fait beaucoup de RnD sur un chargement paresseux.
Caractéristiques
Prise en charge d'Android 2.0+
Jetez un oeil sur Shutterbug , le port léger SDWebImage (une belle bibliothèque sur iOS) d’Applidium sur Android. Il prend en charge la mise en cache asynchrone, stocke les URL ayant échoué, gère bien la concurrence et inclut des sous-classes utiles.
Les demandes d'extraction (et les rapports de bogues) sont également les bienvenues!
DroidParts a ImageFetcher ne nécessite aucune configuration pour pouvoir commencer.
Clone DroidPartsGram pour un exemple:
Un petit conseil pour ceux qui sont indécis quant à la bibliothèque à utiliser pour les images à chargement paresseux:
Il y a quatre moyens de base.
DIY => Pas la meilleure solution, mais pour quelques images et si vous voulez vous passer de la corvée d'utiliser d'autres bibliothèques
Volley's Lazy Loading library => Des gars sur Android. C'est agréable et tout, mais est mal documenté et pose donc un problème d'utilisation.
Picasso: Une solution simple qui fonctionne, vous pouvez même spécifier la taille exacte de l’image que vous souhaitez importer. C’est très simple à utiliser, mais elle risque de ne pas être très "performante" pour les applications qui doivent gérer des quantités énormes d’images.
UIL: Le meilleur moyen de charger des images paresseux. Vous pouvez mettre en cache des images (vous devez bien sûr obtenir une autorisation), initialiser le chargeur une fois, puis terminer votre travail. La bibliothèque de chargement d'images asynchrones la plus mature que j'ai jamais vue jusqu'à présent.
Novoda a également un super bibliothèque de chargement d'images paresseuses et de nombreuses applications comme Songkick, Podio, SecretDJ et ImageSearch utilisent leur bibliothèque.
Leur bibliothèque est hébergée ici sur Github et ils ont aussi un assez actif suivi des problèmes . Leur projet semble être assez actif aussi, avec plus de 300 engagements au moment de la rédaction de cette réponse.
Vérifiez mon fork de LazyList . En gros, j'améliore LazyList en retardant l'appel de ImageView et crée deux méthodes:
J'ai également amélioré ImageLoader en implémentant un singleton dans cet objet.
Tous les codes ci-dessus ont leur propre valeur, mais avec mon expérience personnelle, essayez simplement avec Picasso.
Picasso est une bibliothèque spécialement conçue à cet effet. En fait, il gérera automatiquement le cache et toutes les autres opérations réseau. Vous devrez ajouter une bibliothèque à votre projet. et écrivez simplement une seule ligne de code pour charger l'image à partir d'une URL distante.
S'il vous plaît visitez ici: http://code.tutsplus.com/tutorials/Android-sdk-working-with-picasso--cms-22149
Si vous souhaitez afficher la mise en page Shimmer comme Facebook, il existe une bibliothèque Facebook officielle à cet effet. FaceBook Shimmer Android
Il s’occupe de tout, il vous suffit de mettre le code de votre choix de manière imbriquée dans un cadre chatoyant. Voici un exemple de code.
<com.facebook.shimmer.ShimmerFrameLayout
Android:id=“@+id/shimmer_view_container”
Android:layout_width=“wrap_content”
Android:layout_height="wrap_content"
shimmer:duration="1000">
<here will be your content to display />
</com.facebook.shimmer.ShimmerFrameLayout>
Et voici le code Java pour cela.
ShimmerFrameLayout shimmerContainer = (ShimmerFrameLayout) findViewById(R.id.shimmer_view_container);
shimmerContainer.startShimmerAnimation();
Ajoutez cette dépendance dans votre fichier Gradle.
implementation 'com.facebook.shimmer:shimmer:0.1.0@aar'
Utilisez la bibliothèque de glisse. Cela a fonctionné pour moi et fonctionnera aussi pour votre code. Cela fonctionne aussi bien pour les images que pour les gifs.
ImageView imageView = (ImageView) findViewById(R.id.test_image);
GlideDrawableImageViewTarget imagePreview = new GlideDrawableImageViewTarget(imageView);
Glide
.with(this)
.load(url)
.listener(new RequestListener<String, GlideDrawable>() {
@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
return false;
}
})
.into(imagePreview);
}
Donnez Aquery un essai. Il dispose de méthodes extrêmement simples pour charger et mettre en cache les images de manière asynchrone.
Je peux recommander une méthode différente qui fonctionne comme un charme: Android Query.
Vous pouvez télécharger ce fichier JAR à partir de ici
AQuery androidAQuery = new AQuery(this);
Par exemple:
androidAQuery.id(YOUR IMAGEVIEW).image(YOUR IMAGE TO LOAD, true, true, getDeviceWidth(), ANY DEFAULT IMAGE YOU WANT TO SHOW);
C'est très rapide et précis, et avec cela, vous pouvez trouver beaucoup plus de fonctionnalités comme l'animation lors du chargement, obtenir un bitmap (si nécessaire), etc.
RLImageViewHelper est une bibliothèque étonnante qui vous aide à le faire.
public class ImageDownloader {
Map<String, Bitmap> imageCache;
public ImageDownloader() {
imageCache = new HashMap<String, Bitmap>();
}
// download function
public void download(String url, ImageView imageView) {
if (cancelPotentialDownload(url, imageView)) {
// Caching code right here
String filename = String.valueOf(url.hashCode());
File f = new File(getCacheDirectory(imageView.getContext()),
filename);
// Is the bitmap in our memory cache?
Bitmap bitmap = null;
bitmap = (Bitmap) imageCache.get(f.getPath());
if (bitmap == null) {
bitmap = BitmapFactory.decodeFile(f.getPath());
if (bitmap != null) {
imageCache.put(f.getPath(), bitmap);
}
}
// No? download it
if (bitmap == null) {
try {
BitmapDownloaderTask task = new BitmapDownloaderTask(
imageView);
DownloadedDrawable downloadedDrawable = new DownloadedDrawable(
task);
imageView.setImageDrawable(downloadedDrawable);
task.execute(url);
} catch (Exception e) {
Log.e("Error==>", e.toString());
}
} else {
// Yes? set the image
imageView.setImageBitmap(bitmap);
}
}
}
// cancel a download (internal only)
private static boolean cancelPotentialDownload(String url,
ImageView imageView) {
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
if (bitmapDownloaderTask != null) {
String bitmapUrl = bitmapDownloaderTask.url;
if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
bitmapDownloaderTask.cancel(true);
} else {
// The same URL is already being downloaded.
return false;
}
}
return true;
}
// gets an existing download if one exists for the imageview
private static BitmapDownloaderTask getBitmapDownloaderTask(
ImageView imageView) {
if (imageView != null) {
Drawable drawable = imageView.getDrawable();
if (drawable instanceof DownloadedDrawable) {
DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable;
return downloadedDrawable.getBitmapDownloaderTask();
}
}
return null;
}
// our caching functions
// Find the dir to save cached images
private static File getCacheDirectory(Context context) {
String sdState = Android.os.Environment.getExternalStorageState();
File cacheDir;
if (sdState.equals(Android.os.Environment.MEDIA_MOUNTED)) {
File sdDir = Android.os.Environment.getExternalStorageDirectory();
// TODO : Change your diretcory here
cacheDir = new File(sdDir, "data/ToDo/images");
} else
cacheDir = context.getCacheDir();
if (!cacheDir.exists())
cacheDir.mkdirs();
return cacheDir;
}
private void writeFile(Bitmap bmp, File f) {
FileOutputStream out = null;
try {
out = new FileOutputStream(f);
bmp.compress(Bitmap.CompressFormat.PNG, 80, out);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null)
out.close();
} catch (Exception ex) {
}
}
}
// download asynctask
public class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private String url;
private final WeakReference<ImageView> imageViewReference;
public BitmapDownloaderTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
@Override
// Actual download method, run in the task thread
protected Bitmap doInBackground(String... params) {
// params comes from the execute() call: params[0] is the url.
url = (String) params[0];
return downloadBitmap(params[0]);
}
@Override
// Once the image is downloaded, associates it to the imageView
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
// Change bitmap only if this process is still associated with
// it
if (this == bitmapDownloaderTask) {
imageView.setImageBitmap(bitmap);
// cache the image
String filename = String.valueOf(url.hashCode());
File f = new File(
getCacheDirectory(imageView.getContext()), filename);
imageCache.put(f.getPath(), bitmap);
writeFile(bitmap, f);
}
}
}
}
static class DownloadedDrawable extends ColorDrawable {
private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;
public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
super(Color.WHITE);
bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(
bitmapDownloaderTask);
}
public BitmapDownloaderTask getBitmapDownloaderTask() {
return bitmapDownloaderTaskReference.get();
}
}
// the actual download code
static Bitmap downloadBitmap(String url) {
HttpParams params = new BasicHttpParams();
params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
HttpVersion.HTTP_1_1);
HttpClient client = new DefaultHttpClient(params);
final HttpGet getRequest = new HttpGet(url);
try {
HttpResponse response = client.execute(getRequest);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.w("ImageDownloader", "Error " + statusCode
+ " while retrieving bitmap from " + url);
return null;
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
inputStream = entity.getContent();
final Bitmap bitmap = BitmapFactory
.decodeStream(inputStream);
return bitmap;
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
// Could provide a more explicit error message for IOException or
// IllegalStateException
getRequest.abort();
Log.w("ImageDownloader", "Error while retrieving bitmap from "
+ url + e.toString());
} finally {
if (client != null) {
// client.close();
}
}
return null;
}
}
Une autre façon de le faire consiste à utiliser votre adaptateur dans un thread de votre méthode getView ():
Thread pics_thread = new Thread(new Runnable() {
@Override
public void run() {
Bitmap bitmap = getPicture(url);
if(bitmap != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
holder.imageview.setImageBitmap(bitmap);
adapter.notifyDataSetChanged();
}
});
}
}
});
pics_thread.start();
bien sûr, vous devriez toujours mettre en cache vos images pour éviter des opérations supplémentaires, vous pouvez les placer dans un tableau HashMap, vérifier si l'image existe dans le tableau, sinon, continuer avec le fil ou bien charger l'image depuis votre tableau HashMap. Assurez-vous également toujours que vous ne perdez pas de mémoire, les bitmaps et les éléments dessinables sont souvent très chargés en mémoire. C'est à vous d'optimiser votre code.
J'ai eu ce problème et implémenté lruCache. Je crois que vous avez besoin de l’API 12 ou supérieure ou que vous utilisez la bibliothèque de compatibilité v4. lurCache est une mémoire rapide, mais elle a aussi un budget, donc si cela vous inquiète, vous pouvez utiliser un cache de disque ... Tout est décrit dans Caching Bitmaps.
Je vais maintenant fournir mon implémentation qui est un singleton J'appelle de n'importe où comme ceci:
//Where the first is a string and the other is a imageview to load.
DownloadImageTask.getInstance().loadBitmap(avatarURL, iv_avatar);
Voici le code idéal pour mettre en cache, puis appeler ce qui précède dans getView d'un adaptateur lors de la récupération de l'image Web:
public class DownloadImageTask {
private LruCache<String, Bitmap> mMemoryCache;
/* Create a singleton class to call this from multiple classes */
private static DownloadImageTask instance = null;
public static DownloadImageTask getInstance() {
if (instance == null) {
instance = new DownloadImageTask();
}
return instance;
}
//Lock the constructor from public instances
private DownloadImageTask() {
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
};
}
public void loadBitmap(String avatarURL, ImageView imageView) {
final String imageKey = String.valueOf(avatarURL);
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageResource(R.drawable.ic_launcher);
new DownloadImageTaskViaWeb(imageView).execute(avatarURL);
}
}
private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
private Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
/* A background process that opens a http stream and decodes a web image. */
class DownloadImageTaskViaWeb extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTaskViaWeb(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon = null;
try {
InputStream in = new Java.net.URL(urldisplay).openStream();
mIcon = BitmapFactory.decodeStream(in);
}
catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
addBitmapToMemoryCache(String.valueOf(urldisplay), mIcon);
return mIcon;
}
/* After decoding we update the view on the main UI. */
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
}
Vous pouvez essayer la bibliothèque Aquery Android pour le chargement paresseux d’images et de listview ... Le code ci-dessous peut vous aider ..... bibliothèque de téléchargement d'ici .
AQuery aq = new AQuery(mContext);
aq.id(R.id.image1).image("http://data.whicdn.com/images/63995806/original.jpg");
utilisez la classe ci-dessous pour télécharger et charger des images dans listview.Il met en cache chaque image une fois téléchargée. Charge également les images et le chargement paresseux.
package com.fudiyoxpress.images;
import Java.io.File;
import Java.io.FileInputStream;
import Java.io.FileNotFoundException;
import Java.io.FileOutputStream;
import Java.io.IOException;
import Java.io.InputStream;
import Java.io.OutputStream;
import Java.net.HttpURLConnection;
import Java.net.URL;
import Java.util.Collections;
import Java.util.Map;
import Java.util.WeakHashMap;
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;
import Android.content.Context;
import Android.graphics.Bitmap;
import Android.graphics.BitmapFactory;
import Android.os.Handler;
import Android.widget.ImageView;
import com.fudiyoxpress.R;
import com.fudiyoxpress.config.Config;
import com.fudiyoxpress.Twitter.ScaleBitmap;
public class ImageLoader {
// Initialize MemoryCache
MemoryCache memoryCache = new MemoryCache();
FileCache fileCache;
Context C;
// Create Map (collection) to store image and image url in key value pair
private Map<ImageView, String> imageViews = Collections
.synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;
// handler to display images in UI thread
Handler handler = new Handler();
public ImageLoader(Context context) {
C = context;
fileCache = new FileCache(context);
// Creates a thread pool that reuses a fixed number of
// threads operating off a shared unbounded queue.
executorService = Executors.newFixedThreadPool(5);
}
// default image show in list (Before online image download)
final int stub_id = R.drawable.restlogoplaceholder;
public void DisplayImage(String url, ImageView imageView, Context context,
boolean header_flag) {
Bitmap largeIcon = BitmapFactory.decodeResource(context.getResources(),
R.drawable.restlogoplaceholder);
header_flag = false;
// Store image and url in Map
imageViews.put(imageView, url);
// Check image is stored in MemoryCache Map or not (see
// MemoryCache.Java)
Bitmap bitmap = memoryCache.get(url);
if (bitmap != null) {
// if image is stored in MemoryCache Map then
// Show image in listview row
Bitmap b = ScaleBitmap
.getScaledBitmap(context, bitmap, header_flag);
imageView.setImageBitmap(b);
} else {
// queue Photo to download from url
queuePhoto(url, imageView, header_flag);
// Before downloading image show default image
imageView.setImageBitmap(ScaleBitmap.getScaledBitmap(context,
largeIcon, header_flag));
}
}
private void queuePhoto(String url, ImageView imageView, boolean header_flag) {
// Store image and url in PhotoToLoad object
PhotoToLoad p = new PhotoToLoad(url, imageView, header_flag);
// pass PhotoToLoad object to PhotosLoader runnable class
// and submit PhotosLoader runnable to executers to run runnable
// Submits a PhotosLoader runnable task for execution
executorService.submit(new PhotosLoader(p));
}
// Task for the queue
private class PhotoToLoad {
public String url;
public ImageView imageView;
public boolean b;
public PhotoToLoad(String u, ImageView i, boolean header_flag) {
url = u;
imageView = i;
b = header_flag;
}
}
class PhotosLoader implements Runnable {
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad) {
this.photoToLoad = photoToLoad;
}
@Override
public void run() {
try {
// Check if image already downloaded
if (imageViewReused(photoToLoad))
return;
// download image from web url
Bitmap bmp = getBitmap(photoToLoad.url);
// set image data in Memory Cache
memoryCache.put(photoToLoad.url, bmp);
if (imageViewReused(photoToLoad))
return;
// Get bitmap to display
BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);
// Causes the Runnable bd (BitmapDisplayer) to be added to the
// message queue.
// The runnable will be run on the thread to which this handler
// is attached.
// BitmapDisplayer run method will call
handler.post(bd);
} catch (Throwable th) {
// th.printStackTrace();
}
}
}
private Bitmap getBitmap(String url) {
File f = fileCache.getFile(url);
// from SD cache
// CHECK : if trying to decode file which not exist in cache return null
Bitmap b = decodeFile(f);
if (b != null)
return b;
// Download image file from web
try {
// // download the image
Bitmap bitmap = null;
URL imageURL = null;
try {
imageURL = new URL(Config.WEB_URL + "/ServeBlob?id=" + url);
HttpURLConnection connection = (HttpURLConnection) imageURL
.openConnection();
connection.setDoInput(true);
connection.connect();
// if(!(new File(imageURL.toString())).exists())
// {
// imageURL=new URL("");
// }
InputStream inputStream = connection.getInputStream();
// Constructs a new FileOutputStream that writes to
// file
// if file not exist then it will create file
OutputStream os = new FileOutputStream(f);
// See Utils class CopyStream method
// It will each pixel from input stream and
// write pixels to output stream (file)
Utils.CopyStream(inputStream, os);
os.close();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
bitmap = BitmapFactory.decodeStream(inputStream, null, options);
} catch (IOException e) {
// e.printStackTrace();
}
// Now file created and going to resize file with defined height
// Decodes image and scales it to reduce memory consumption
bitmap = decodeFile(f);
return bitmap;
} catch (Throwable ex) {
ex.printStackTrace();
if (ex instanceof OutOfMemoryError)
memoryCache.clear();
return null;
}
}
// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream stream1 = new FileInputStream(f);
BitmapFactory.decodeStream(stream1, null, o);
stream1.close();
// Find the correct scale value. It should be the power of 2.
// Set width/height of recreated image
final int REQUIRED_SIZE = 85;
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while (true) {
if (width_tmp / 2 < REQUIRED_SIZE
|| height_tmp / 2 < REQUIRED_SIZE)
break;
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
// decode with current scale values
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
FileInputStream stream2 = new FileInputStream(f);
Bitmap bitmap = BitmapFactory.decodeStream(stream2, null, o2);
stream2.close();
return bitmap;
} catch (FileNotFoundException e) {
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
boolean imageViewReused(PhotoToLoad photoToLoad) {
String tag = imageViews.get(photoToLoad.imageView);
// Check url is already exist in imageViews MAP
if (tag == null || !tag.equals(photoToLoad.url))
return true;
return false;
}
// Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable {
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p) {
bitmap = b;
photoToLoad = p;
}
public void run() {
if (imageViewReused(photoToLoad))
return;
// Show bitmap on UI
if (bitmap != null) {
photoToLoad.imageView.setImageBitmap(ScaleBitmap
.getScaledBitmap(C, bitmap, photoToLoad.b));
} else {
}
// photoToLoad.imageView.setImageResource(stub_id);
}
}
public void clearCache() {
// Clear cache directory downloaded images and stored data in maps
memoryCache.clear();
fileCache.clear();
}
}
package com.fudiyoxpress.images;
import Java.util.Collections;
import Java.util.Iterator;
import Java.util.LinkedHashMap;
import Java.util.Map;
import Java.util.Map.Entry;
import Android.graphics.Bitmap;
import Android.util.Log;
public class MemoryCache {
private static final String TAG = "MemoryCache";
//Last argument true for LRU ordering
private Map<String, Bitmap> cache = Collections.synchronizedMap(
new LinkedHashMap<String, Bitmap>(10,1.5f,true));
//current allocated size
private long size=0;
//max memory cache folder used to download images in bytes
private long limit = 1000000;
public MemoryCache(){
//use 25% of available heap size
setLimit(Runtime.getRuntime().maxMemory()/4);
}
public void setLimit(long new_limit){
limit=new_limit;
Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
}
public Bitmap get(String id){
try{
if(!cache.containsKey(id))
return null;
//NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78
return cache.get(id);
}catch(NullPointerException ex){
ex.printStackTrace();
return null;
}
}
public void put(String id, Bitmap bitmap){
try{
if(cache.containsKey(id))
size-=getSizeInBytes(cache.get(id));
cache.put(id, bitmap);
size+=getSizeInBytes(bitmap);
checkSize();
}catch(Throwable th){
th.printStackTrace();
}
}
private void checkSize() {
Log.i(TAG, "cache size="+size+" length="+cache.size());
if(size>limit){
Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated
while(iter.hasNext()){
Entry<String, Bitmap> entry=iter.next();
size-=getSizeInBytes(entry.getValue());
iter.remove();
if(size<=limit)
break;
}
Log.i(TAG, "Clean cache. New size "+cache.size());
}
}
public void clear() {
try{
//NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78
cache.clear();
size=0;
}catch(NullPointerException ex){
ex.printStackTrace();
}
}
long getSizeInBytes(Bitmap bitmap) {
if(bitmap==null)
return 0;
return bitmap.getRowBytes() * bitmap.getHeight();
}
}
package com.fudiyoxpress.images;
import Java.io.InputStream;
import Java.io.OutputStream;
public class Utils {
public static void CopyStream(InputStream is, OutputStream os)
{
final int buffer_size=1024;
try
{
byte[] bytes=new byte[buffer_size];
for(;;)
{
//Read byte from input stream
int count=is.read(bytes, 0, buffer_size);
if(count==-1)
break;
//Write byte from output stream
os.write(bytes, 0, count);
}
}
catch(Exception ex){}
}
}
J'utilise droidQuery . Il existe deux mécanismes pour charger une image à partir d'une URL. Le premier (raccourci) est simplement:
$.with(myView).image(url);
Ceci peut être ajouté très facilement dans la méthode getView(...)
de ArrayAdapter
.
La méthode longhand donnera beaucoup plus de contrôle et comporte des options qui ne sont même pas abordées ici (comme la mise en cache et les rappels), mais une implémentation de base spécifiant la taille de sortie de 200px x 200px peut être trouvée ici:
$.ajax(new AjaxOptions().url(url)
.type("GET")
.dataType("image")
.imageWidth(200).imageHeight(200)
.success(new Function() {
@Override
public void invoke($ droidQuery, Object... params) {
myImageView.setImageBitmap((Bitmap) params[0]);
}
})
.error(new Function() {
@Override
public void invoke($ droidQuery, Object... params) {
AjaxError e = (AjaxError) params[0];
Log.e("$", "Error " + e.status + ": " + e.error);
}
})
);
Certaines réponses ont déjà mentionné l'utilisation de diverses bibliothèques d'images telles que Universal Image Loader et androidimageloader, etc. Ceci est une vieille question, mais pour ceux qui recherchent encore quelque chose comme cela, il existe plusieurs bibliothèques de ce type pour le chargement/la mise en cache des images.
Vous pouvez utiliser une bibliothèque tierce telle que Piccaso
ou Volley
pour un chargement paresseux efficace. Vous pouvez également créer le vôtre en implémentant ce qui suit.
Implémenter le code pour télécharger l'image à partir de l'URL
Implémentation d'un mécanisme de mise en cache pour stocker et récupérer des images (utilisez LruCache
sur Android pour la mise en cache)
J'ai trouvé le Glide comme meilleure option que Picasso
. J'utilisais picasso pour charger environ 32
images de taille environ 200-500KB
chacune et j'obtenais toujours OOM
. Mais le Glide
a résolu tous mes problèmes OOM
.
Vous pouvez utiliser AsyncImageView de GreenDroid. Il suffit d’appeler setUrl et j’imagine que cela pourrait vous aider à faire ce que vous ne voudriez pas faire et vous référer au lien ci-dessous:
Sauf charger le cache de données de manière asynchrone, vous pouvez avoir besoin du cache d'interface utilisateur
Excepté le chargement des données d’éléments visibles, vous devrez peut-être charger les données d’approximable-visible
Exemple: supposons que l'élément visible dans la liste soit [6,7,8,9,10], vous devrez peut-être charger [6,7,8,9,10] ET pré-charger l'élément [1, 2, 3, 4 , 5] & [11, 12, 13, 14, 15], car l'utilisateur fait probablement défiler l'écran vers la page précédente ou la page suivante