如何在Android中的ListView中延迟加载图像


1929

我正在使用ListView来显示一些图像和与这些图像相关的标题。我正在从互联网上获取图像。有没有一种方法可以延迟加载图像,以便在显示文本时不阻止UI并在下载图像时显示它们?

图像总数不固定。


10
您可以使用GreenDroid的AsyncImageView。只需致电setUrl
Pascal Dimassimo 2011年

7
我用过了 这是一个很棒的实现。坏消息是AsyncImageView是大型GreenDroid项目的一部分,即使您仅需要AsyncImageView,这也会使您的应用程序变大。此外,似乎GreenDroid项目自2011
。– borisstr

5
您甚至可以尝试使用以下库:Android-http-image-manager,我认为这是异步加载图像的最佳选择。
Ritesh Kumar Dubey 2012年

34
只需使用毕加索,它就会自己做。'Picasso.with(yourContext).load(img src / path / drawable here).into(imageView即您的目标);' 而已!
2014年

6
尝试使用:github.com/nostra13/Android-Universal-Image-Loader,该库对于延迟加载和图像缓存非常快速且高效
krunal patel 2014年

Answers:


1086

这是我创建的用于保存应用程序当前正在显示的图像的内容。请注意,这里使用的“ Log”对象是我在Android内部最终Log类周围的自定义包装。

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();
    }
}

104
我认为您应该使用SoftReferences,以便您的程序永远不会导致OutOfMemoryException。由于GC可以在堆大小增加时清除软引用...您可以管理自己的世代,例如几秒钟后可以将图像放入该列表,并且在加载之前应检查图像是否存在,然后不要再次下载它,而是收集它从该列表中重新放回您的softref列表中,过一段时间后您就可以清除您的硬列表了:)
AZ_

36
Google Shelves项目是一个很好的例子,展示了他们如何做到code.google.com/p/shelves
AZ_ 2011年

12
当drawableMap包含图像...而不启动获取线程时,您是否会错过回报?
卡苏尔

5
这段代码有几个问题。首先,您应该缓存Drawable,这将导致内存泄漏:stackoverflow.com/questions/7648740/…。其次,缓存本身永远不会清除,因此它将永远增长,这是另一个内存泄漏。
satur9nine


1024

我对带有图像的惰性列表(位于GitHub上)进行了简单演示

基本用法

ImageLoader imageLoader=new ImageLoader(context); ...
imageLoader.DisplayImage(url, imageView); 

不要忘记向您的AndroidManifest.xml添加以下权限:

 <uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> Please

仅创建一个ImageLoader实例,并在您的应用程序中重复使用它。这样,图像缓存将更加高效。

这可能对某人有帮助。它将在后台线程中下载图像。图像被缓存在SD卡和内存中。缓存的实现非常简单,足以用于演示。我使用inSampleSize解码图像以减少内存消耗。我也尝试正确处理回收的视图。

替代文字


7
谢谢你,我在我的项目中使用的代码稍加修改的版本,几乎无处不在: code.google.com/p/vimeoid/source/browse/apk/src/com/fedorvlasov/...
shaman.sir

3
有没有人看过Gilles Debunne的代码作为替代方案。我看到的两个主要区别是1)内存缓存与SD卡之间的差异; 2)使用AsyncTasks而不是线程池。android-developers.blogspot.com/2010/07/…–
理查德(Richard)

4
有错误,有时会被触发:10-13 09:58:46.738:ERROR / AndroidRuntime(24250):未捕获的处理程序:由于未捕获的异常而导致线程主退出10-13 09:58:46.768:ERROR / AndroidRuntime(24250) :java.lang.ArrayIndexOutOfBoundsException:数组索引超出范围:0 10-13 09:58:46.768:ERROR / AndroidRuntime(24250):at java.util.Vector.elementAt(Vector.java:331)10-13 09: 58:46.768:ERROR / AndroidRuntime(24250):at java.util.Vector.get(Vector.java:445)10-13 09:58:46.768:ERROR / AndroidRuntime(24250):at com.my.app.image .ImageLoader $ PhotosQueue.Clean(ImageLoader.java:91)
Mikooos 2011年

64
我想为像我这样的新手添加,如果要将此代码添加到自己的项目中,则必须在清单中添加外部缓存权限。谢谢
亚当

2
@BruceHill最新的源代码中没有photosQueue。您似乎正在使用一些非常旧的版本。
2013年

551

我建议使用开源工具Universal Image Loader。它最初是基于Fedor Vlasov的项目LazyList进行的,此后得到了极大的改进。

  • 多线程图像加载
  • 可以广泛调整ImageLoader的配置(线程执行程序,下载器,解码器,内存和磁盘缓存,显示图像选项等)
  • 内存和/或设备文件系统(或SD卡)中图像缓存的可能性
  • “监听”加载过程的可能性
  • 可以使用单独的选项自定义每个显示图像调用
  • 小部件支持
  • Android 2.0+支持


16
如果您正在寻找伟大的“图像加载”代码,其创建者将其视为并珍藏为他的宝贝(不仅是一次性解决方案),那么您刚刚找到了它;大起大落的Nostra
AndroidGecko 2012年

确实很棒,但是有点滞后于我的列表视图。如果您可以说出最佳性能的“ ImageLoader”构建……或者可能是因为“ DisplayImageOptions”。
dinigo 2012年

我非常喜欢这个gridview,但是我不能在本教程中使用它。我是android的初学者。我制作了有关gridview的应用程序,但我的应用程序是targetSdkVersion 15,因此我需要在后台线程中使用Asynctask进行处理。我曾经使用过这个gridview,但是它不起作用。如何在targetSdkVersion 15中使用它?
kongkea 2012年

您可以使用[universal-image-loader]
nostra13 2012年

3
在阅读了官方的android文档并尝试自己做这些事情之后,我可以确定这个库的最终目的就是图像加载。无法投票足够。
核心

158

Gilles Debunne撰写的教程Multi Performanceing for Performance

这是来自Android开发者博客。建议的代码使用:

  • AsyncTasks
  • 硬而有限的尺寸FIFO cache
  • 一个易于编辑的软garbage collect缓存。
  • 下载时的占位符 Drawable

在此处输入图片说明


10
它在2.1中也能正常工作。只是不要使用AndroidHttpClient。
Thomas Ahle

2
@ thomas-ahle谢谢,我看到AndroidHttpClient在2.1中给出了错误,因为它是从2.2实现的,但并未真正尝试寻找其他替代它的方法。
阿迪尼亚2011年

4
@阿迪娜你是对的,我忘了。但是,食谱中没有什么不能像普通的HttpClient一样好。
托马斯·阿勒

我在很多地方都听说过,Google不建议使用软引用,因为与系统的早期版本相比,Android内核非常渴望收集这些引用。
穆罕默德·艾哈迈德·阿布塔利布

108

更新:请注意,此答案现在无效。垃圾收集器对SoftReference和WeakReference采取积极措施,因此此代码不适用于新应用。 (相反,请尝试使用Universal Image Loader之类的其他答案中建议的。)

感谢James的代码,以及Bao-Long提出的使用SoftReference的建议。我在James的代码上实现了SoftReference更改。不幸的是,SoftReferences导致我的图像被垃圾收集的速度过快。就我而言,没有SoftReference的东西很好,因为我的列表大小有限并且图像很小。

一年前,有一个关于Google组上的SoftReferences的讨论:链接到thread。作为对过早的垃圾回收的解决方案,他们建议使用dalvik.system.VMRuntime.setMinimumHeapSize()手动设置VM堆大小的可能性,这对我来说不是很有吸引力。

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();
}

3
您可以创建硬代和软代等代。你可以定个时间清除缓存将清除所有未在3秒访问的所有图片..你可以看看谷歌的货架项目
AZ_

developer.android.com/reference/java/lang/ref/…SoftReference文档中有关于缓存的注释,请参阅“避免使用缓存的软引用”一节。大多数应用程序应使用android.util.LruCache而不是软引用。
vokilam

我很欣赏您的代码,但是现在在新的Android O / S中,存在“激进的”垃圾收集。保持低调对我没有任何意义。
j2emanue

@ j2emanue是的,正如我试图在回答的顶部指出的那样,SoftReferences的垃圾收集速度太快。我将尝试编辑此答案以使其更加清晰。
TalkLittle

96

毕加索

使用杰克·沃顿的毕加索图书馆。(由ActionBarSherlock的开发人员提供的完善的ImageLoading库)

适用于Android的功能强大的图像下载和缓存库。

图像为Android应用程序增加了急需的上下文和视觉效果。Picasso允许在您的应用程序中轻松加载图像-通常只需一行代码!

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

毕加索会自动处理Android上许多常见的图片加载陷阱:

在适配器中处理ImageView回收和下载取消。复杂的图像转换,使用最少的内存。自动内存和磁盘缓存。

毕加索杰克·沃顿的图书馆

滑行

Glide是适用于Android的快速高效的开源媒体管理框架,该框架将媒体解码,内存和磁盘缓存以及资源池包装到一个简单易用的界面中。

Glide支持获取,解码和显示视频静止图像,图像和动画GIF。Glide包含灵活的api,允许开发人员插入几乎所有网络堆栈。默认情况下,Glide使用基于自定义HttpUrlConnection的堆栈,但还包括实用程序库,它们可以插入Google的Volley项目或Square的OkHttp库。

Glide.with(this).load("http://goo.gl/h8qOq7").into(imageView);

Glide的主要重点是使任何种类的图像列表尽可能平滑和快速地滚动,但是Glide在几乎所有需要获取,调整大小和显示远程图像的情况下也有效。

滑行图像加载库

Facebook的壁画

Fresco是一个功能强大的系统,可在Android应用程序中显示图像。

壁画负责图像的加载和显示,因此您不必这样做。它将从网络,本地存储或本地资源加载图像,并显示一个占位符,直到图像到达为止。它具有两个级别的缓存;一个在内存中,另一个在内部存储器中。

壁画Github

在Android 4.x及更低版本中,Fresco将图像放置在Android内存的特殊区域中。这使您的应用程序运行得更快-并减少了遭受可怕的OutOfMemoryError的频率。

壁画文档


毕加索是Square开发的图书馆
LordRaydenMK,

82

高性能加载程序-在检查了此处建议的方法后,我使用了Ben的解决方案并进行了一些更改-

  1. 我意识到使用可绘制对象比使用位图更快,因此我改用可绘制对象

  2. 使用SoftReference很棒,但是它会使缓存的图像被删除得太频繁,所以我添加了一个链接列表,该列表保存了图像引用,防止图像被删除,直到达到预定大小为止

  3. 要打开InputStream,我使用了java.net.URLConnection,它允许我使用Web缓存(您需要首先设置响应缓存,但这是另一回事了)

我的代码:

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;
}
}

很棒!顺便说一句,班级名称中有一个错字。
Mullins

5
如果它节省了其他人的时间: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;
Michael Reed

非常感谢,这是一个不错的实现。我还为加载可绘制对象的时间放置了一个不同的占位符,以便用户获得一些反馈。
胡安·埃尔南德斯

另外,我认为最好在executorService(mThreadPool)中使用LIFO队列,而不是使用默认FIFO,以便首先加载最后请求的图像(可能是可见图像)。参见stackoverflow.com/questions/4620061/how-to-create-lifo-executor
Juan Hernandez 2012年

9
@MichaelReed,如果您是Eclipse用户,建议您使用Ctrl-Shift-O(即字母O,而不是数字0)。它会自动执行添加导入的过程,并为您进行整理。如果您使用的是Mac,请改用Command-Shift-O。
SilithCrowe 2012年

78

我已经接受了本Android培训,并且我认为它在下载图像方面做得很好,而且不会阻塞主界面。它还处理缓存和处理滚动浏览许多图像的步骤:有效地加载大型位图


抱歉,我只针对Google IO应用程序指定了一个类(并且我来不及编辑)。您应该真正研究它们的所有图像加载和缓存实用程序类,这些类与缓存类可以在同一包中找到。
mkuech

有人建议从iosched应​​用程序的util文件夹中获取DiskLruCache,Image * .java文件,以帮助处理列表视图的图像加载/缓存吗?我的意思是,绝对值得您参考有关该主题的在线开发人员指南,但是这些类(来自iosched)在模式上要更进一步。
Gautam

65

1. Picasso允许在您的应用程序中轻松加载图像-通常只需一行代码!

使用Gradle:

implementation 'com.squareup.picasso:picasso:2.71828'

只需一行代码!

Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);

2. 滑行 Android的图像加载和缓存库着重于平滑滚动

使用Gradle:

repositories {
  mavenCentral() 
  google()
}

dependencies {
   implementation 'com.github.bumptech.glide:glide:4.7.1'
   annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
}

//对于一个简单的视图:

  Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(imageView);

3. fresco 是一个功能强大的系统,可在Android应用程序中显示图像。Fresco负责图像的加载和显示,因此您不必这样做。

壁画入门


本教程可以帮助你更多的PICASOO: - androidtutorialshub.com/...和GLIDE: - androidtutorialshub.com/...
拉利特vasan


41

我这样做的方法是启动一个线程在后台下载图像,并为每个列表项提供一个回调。映像下载完成后,它将调用回调,该回调将更新列表项的视图。

但是,在回收视图时,此方法效果不佳。


为每个图像使用线程也是我使用的方法。如果您将模型从视图中分离出来,则可以将模型持久保存在Activity外(例如在“应用程序”类中),以保持对模型的缓存。如果您有很多图像,请当心资源不足。
James A Wilson

能否请您详细说明。我是android开发的新手。不过,感谢您的提示
–lostInTransit

14
为每个图像启动新线程不是有效的解决方案。您最终可能会在内存中冻结许多线程并冻结UI。
费多(Fedor)2010年

Fedor,同意,我通常使用队列和线程池,这是使用imo的最佳方法。
jasonhudgins


29

这是Android上的常见问题,许多人已通过多种方式解决了这个问题。在我看来,我所见过的最好的解决方案是名为Picasso的相对较新的库。以下是重点内容:

  • 开源,但为首Jake WhartonActionBarSherlock成名。
  • 使用一行代码从网络或应用程序资源异步加载图像
  • 自动ListView检测
  • 自动磁盘和内存缓存
  • 可以进行自定义转换
  • 许多可配置的选项
  • 超级简单的API
  • 经常更新

29

我一直在使用新的Android Volley Library中的NetworkImageView com.android.volley.toolbox.NetworkImageView,它似乎运行得很好。显然,这与Google Play和其他新的Google应用程序中使用的视图相同。绝对值得一试。


1
我觉得这是最好的解决方法-其他的答案是很老-凌空抽射是真的快,加上杰克warthons disklrucache它的一个PERFEKT解决方案-我尝试了很多人,但没有一个是稳定和快速的凌空
亚历山大Sidikov Pfeif

26

嗯,从Internet加载图像的时间有很多解决方案。您也可以使用库Android-Query。它将为您提供所有必需的活动。确保您要做什么并阅读图书馆Wiki页面。并解决了图像加载的限制。

这是我的代码:

@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;
}

应该可以解决您的延迟加载问题。


我的工作不错,但需要一个Jar文件包含在您的项目中。您可以从此处下载该JAR文件。AQuery androidAQuery = new AQuery(this); 链接是:code.google.com/archive/p/android-query/downloads
Selim Raza

25

我认为这个问题在Android开发人员中非常流行,并且有许多此类库声称可以解决此问题,但似乎只有少数几个库可以解决。AQuery是这样的一个库,但在各个方面都比大多数库要好,值得尝试。


22

您必须尝试最好的Universal Loader。我在延迟加载上执行了许多RnD之后就使用了它。

通用图像加载器

特征

  • 多线程映像加载(异步或同步)
  • 广泛定制ImageLoader的配置(线程执行器,下载器,解码器,内存和磁盘缓存,显示图像选项等)
  • 每个显示图像调用都有许多自定义选项(存根图像,缓存开关,解码选项,位图处理和显示等)
  • 内存和/或磁盘(设备的文件系统或SD卡)中的图像缓存
  • 监听加载过程(包括下载进度)

Android 2.0+支持

在此处输入图片说明


20

看看Shutterbug,这是Applidium的轻量级SDWebImage(iOS上的一个不错的库)移植到Android。它支持异步缓存,存储失败的URL,很好地处理并发性,并且包括有用的子类。

也欢迎请求请求(和错误报告)!



16

对于那些不确定要使用哪个库延迟加载图像的人来说,这只是一个快速提示:

有四种基本方法。

  1. DIY =>不是最好的解决方案,但是对于一些图像,如果您想不用使用其他库的麻烦

  2. Volley的惰性加载库=>来自android的家伙。很好,一切都很好,但是文档记录很差,因此使用起来很麻烦。

  3. 毕加索(Picasso):这是一个行之有效的简单解决方案,您甚至可以指定要引入的确切图像大小。使用非常简单,但对于必须处理大量图像的应用程序可能并不十分“出色”。

  4. UIL:延迟加载图像的最佳方法。您可以缓存图像(当然需要您的许可),一次初始化加载程序,然后完成您的工作。到目前为止,我所见过的最成熟的异步图像加载库。


15

Novoda还具有出色的惰性图像加载库,并且Songkick,Podio,SecretDJ和ImageSearch等许多应用程序都使用它们的库。

他们的图书馆托管这里 Github上,他们有一个非常活跃的问题跟踪器为好。他们的项目似乎也很活跃,在撰写此回复时有300多个提交。


1
实际上,Novoda是一个很棒的库,但是...有时您不需要庞大的库,而只需一个简单的解决方案即可。这就是为什么Github中的LazyList这么好的原因,如果您的应用程序仅在listView中显示图像,而不是应用程序的主要功能,那只是我想使用更轻巧的另一项活动。否则,如果您知道必须经常使用并且是核心的一部分,请尝试使用Novoda。
Nicolas Jafelle 2013年

13

检查我的LazyList叉子。基本上,我通过延迟对ImageView的调用来改进LazyList并创建两个方法:

  1. 当您需要放置“正在加载图片...”之类的内容时
  2. 需要显示下载的图像时。

通过在此对象中实现单例,我还改进了ImageLoader 。


12

如果要显示Facebook的Shimmer布局,则有一个官方的Facebook库。FaceBook微光Android

它会处理所有事情,您只需要将所需的设计代码以嵌套的方式放在微光框架中即可。这是示例代码。

<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>

这是它的Java代码。

ShimmerFrameLayout shimmerContainer = (ShimmerFrameLayout) findViewById(R.id.shimmer_view_container);
shimmerContainer.startShimmerAnimation();

在您的gradle文件中添加此依赖项。

implementation 'com.facebook.shimmer:shimmer:0.1.0@aar'

这是它的样子。闪光Android屏幕截图



9

使用滑行库。它适用于我,也适用于您的代码,适用于图像和gif。

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);
}

7

我可以推荐一种像魅力一样工作的方法:Android查询。

您可以从此处下载该JAR文件

AQuery androidAQuery = new AQuery(this);

举个例子:

androidAQuery.id(YOUR IMAGEVIEW).image(YOUR IMAGE TO LOAD, true, true, getDeviceWidth(), ANY DEFAULT IMAGE YOU WANT TO SHOW);

它非常快速且准确,使用它您可以找到许多其他功能,例如加载时的动画,获取位图(如果需要)等。




4
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;
 }
}

4

我遇到了这个问题,并实现了lruCache。我相信您需要API 12及更高版本,或者使用兼容性v4库。lurCache是​​快速内存,但是它也有预算,因此,如果您担心可以使用磁盘缓存,请参见缓存位图中的所有内容。

现在,我将提供我的实现,它是我从任何地方调用的单例代码,如下所示:

//Where the first is a string and the other is a imageview to load.

DownloadImageTask.getInstance().loadBitmap(avatarURL, iv_avatar);

这是缓存的理想代码,然后在检索Web图像时在适配器的getView中调用以上代码:

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);
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.