ChatGPT解决这个技术问题 Extra ChatGPT

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

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

图像的总数不固定。

您可以使用 GreenDroid's AsyncImageView。只需调用 setUrl
我已经用过了。这是一个很棒的实现。坏消息是 AsyncImageView 是大型 GreenDroid 项目的一部分,即使在您只需要 AsyncImageView 的情况下,它也会使您的应用程序变得更大。此外,GreenDroid 项目似乎自 2011 年以来没有更新。
你甚至可以试试这个库:Android-http-image-manager,我认为它最适合异步加载图像。
只需使用毕加索,它就会自己完成。 'Picasso.with(yourContext).load(img src/path/drawable here).into(imageView ie your target);'而已!
尝试使用 :github.com/nostra13/Android-Universal-Image-Loader ,这个库对于延迟加载和图像缓存非常快速有效

g
gregrobinz

这是我为保存我的应用当前显示的图像而创建的。请注意,这里使用的“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(Looper.getMainLooper()) {
            @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();
    }
}

我认为您应该使用 SoftReferences,以便您的程序永远不会导致 OutOfMemoryException。由于 GC 可以在堆大小增加时清除软引用...您可以管理自己的生成,例如几秒钟后您可以将图像放入该列表,并且在加载之前您应该检查图像是否存在然后不要再次下载它而不是收集它从该列表中,并将其放回您的软引用列表,一段时间后您可以清除您的硬列表:)
Google Shelves 项目就是一个很好的例子,看看他们是如何做到的code.google.com/p/shelves
当 drawableMap 包含图像时,您不会错过返回...而不启动获取线程吗?
这段代码有几个问题。首先你应该缓存Drawables,这会导致内存泄漏:stackoverflow.com/questions/7648740/…。其次,缓存本身永远不会被清除,所以它会永远增长,这是另一个内存泄漏。
B
Bhargav Rao

我用图像制作了 a simple demo of a lazy list(位于 GitHub)。

基本用法 ImageLoader imageLoader=new ImageLoader(context); ... imageLoader.DisplayImage(url, imageView);不要忘记在您的 AndroidManifest.xml 中添加以下权限: 请只创建一个 ImageLoader 实例并在您的应用程序中重用它。这种方式图像缓存将更加有效。

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

https://i.stack.imgur.com/1NvdB.png


R
Rahul

我推荐开源工具 Universal Image Loader。它最初基于 Fedor Vlasov 的项目 LazyList 并从那时起得到了极大的改进。

多线程图像加载

可以广泛调整 ImageLoader 的配置(线程执行器、下载器、解码器、内存和磁盘缓存、显示图像选项等)

在内存和/或设备的文件系统(或 SD 卡)中缓存图像的可能性

“听”加载过程的可能性

可以使用单独的选项自定义每个显示图像调用

小部件支持

安卓 2.0+ 支持

https://i.stack.imgur.com/QzOIL.png


H
Has AlTaiar

Multithreading For Performance,Gilles Debunne 的教程。

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

异步任务。

一个硬的、有限大小的 FIFO 缓存。

一个软的、易于垃圾收集的缓存。

下载时的占位符 Drawable。

https://i.stack.imgur.com/xdEVJ.png


它在 2.1 中也可以正常工作。只是不要使用 AndroidHttpClient。
@thomas-ahle 谢谢,我看到 AndroidHttpClient 在 2.1 中出现错误,因为它是从 2.2 实现的,但并没有真正尝试找到其他东西来替换它。
@Adina你是对的,我忘了。然而,配方中没有什么不能与普通的 HttpClient 一样好。
我在几个地方听说过,Google 不推荐软引用,因为与早期版本的系统相比,android 内核非常渴望收集这些引用。
R
Rahul

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

感谢 James 提供代码,感谢 Bao-Long 提供使用 SoftReference 的建议。我在 James 的代码上实现了 SoftReference 更改。不幸的是,SoftReferences 导致我的图像被垃圾收集得太快了。在我的情况下,没有 SoftReference 的东西很好,因为我的列表大小是有限的而且我的图像很小。

一年前有一个关于 google 组上的 SoftReferences 的讨论:link to 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 秒内访问的图像。你可以看看谷歌货架项目
developer.android.com/reference/java/lang/ref/… SoftReference 文档有关于缓存的说明,请参阅“避免缓存的软引用”部分。大多数应用程序应该使用 android.util.LruCache 而不是软引用。
我很佩服你的代码,但现在在新的 Android O/S 中有“激进的”垃圾收集。持有弱引用对我来说没有任何意义。
@j2emanue 你是对的,正如我试图在我的答案顶部指出的那样,SoftReferences 垃圾收集得太快了。我将尝试编辑此答案以使其更加清晰。
R
Rahul

毕加索

使用杰克沃顿的毕加索图书馆。 (ActionBarSherlock 开发者提供的完美 ImageLoading 库)

一个强大的安卓图像下载和缓存库。

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

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

Picasso 会自动处理 Android 上许多常见的图像加载陷阱:

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

Picasso Jake Wharton's Library

滑行

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

Glide 支持获取、解码和显示视频静止图像、图像和动画 GIF。 Glide 包含一个灵活的 API,允许开发人员插入几乎任何网络堆栈。默认情况下,Glide 使用自定义的基于 HttpUrlConnection 的堆栈,但也包含实用程序库插件到 Google 的 Volley 项目或 Square 的 OkHttp 库。

Glide.with(this).load("your-url-here").into(imageView);

Glide 的主要重点是让滚动任何类型的图像列表尽可能流畅和快速,但 Glide 也适用于几乎所有需要获取、调整大小和显示远程图像的情况。

Glide Image Loading Library

Facebook的壁画

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

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

Fresco Github

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

Fresco Documentation


Picasso 是 Square 开发的一个库
7
7 revs, 5 users 93%

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

我意识到使用drawable比使用位图更快,所以我使用drawable而不是使用SoftReference很棒,但是它会使缓存的图像经常被删除,所以我添加了一个包含图像引用的链接列表,防止图像被删除,直到达到预定义的大小要打开 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;
}
}

效果很好!顺便说一句,类名中有一个错字。
如果它可以节省其他人的时间: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;
非常感谢,这是一个很好的实现。我还为加载可绘制对象时放置了一个不同的占位符,以便用户可以获得一些反馈。
此外,我认为最好在 executorService (mThreadPool) 中使用 LIFO 队列而不是默认的 FIFO,以便首先加载最后请求的图像(可能是可见的图像)。请参阅stackoverflow.com/questions/4620061/how-to-create-lifo-executor
@MichaelReed,如果您是 Eclipse 用户,我建议您使用 Ctrl-Shift-O(这是字母 O,而不是数字 0)。它可以自动执行添加导入的过程并为您组织它们。如果您使用的是 Mac,请改用 Command-Shift-O。
j
jnthnjns

我遵循了这个 Android 培训,我认为它在不阻塞主 UI 的情况下下载图像做得很好。它还处理缓存和处理许多图像的滚动:Loading Large Bitmaps Efficiently

https://i.stack.imgur.com/zqoX2.jpg


对不起,我只指出了 Google IO 应用程序的一个类(我来不及编辑)。您应该真正研究一下您可以在 same package as the cache class 中找到的所有图像加载和缓存实用程序类。
有人会建议从 iosched 应用程序的 util 文件夹中获取 DiskLruCache、Image*.java 文件来帮助处理列表视图的图像加载/缓存吗?我的意思是绝对值得关注有关该主题的在线开发人员指南,但这些课程(来自 iosched)在该模式上走得更远。
c
chiragkyada

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

使用摇篮:

implementation 'com.squareup.picasso:picasso:(insert latest version)'

只需一行代码!

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

2. Glide 一个专注于平滑滚动的 Android 图像加载和缓存库

使用摇篮:

repositories {
  mavenCentral() 
  google()
}

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

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

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

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

Getting Started with Fresco


本教程可以帮助您更多地了解 PICASOO :- androidtutorialshub.com/… 和 GLIDE :- androidtutorialshub.com/…
T
Trying Tobemyself

我写了一个教程,解释了如何在列表视图中延迟加载图像。我详细介绍了回收和并发问题。我还使用固定线程池来防止产生大量线程。

Lazy loading of images in Listview Tutorial


j
jasonhudgins

我这样做的方法是启动一个线程以在后台下载图像并将每个列表项的回调交给它。当图像下载完成后,它会调用回调来更新列表项的视图。

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


为每个图像使用一个线程也是我使用的方法。如果您将模型与视图分开,则可以将模型保留在 Activity 之外(例如在“应用程序”类中)以保持它们的缓存。如果您有很多图像,请注意资源不足。
你能详细说明一下吗?我是android开发的新手。感谢您的提示
为每个图像启动一个新线程并不是一个有效的解决方案。您最终可能会在内存中出现大量线程并冻结 UI。
Fedor,同意,我通常使用队列和线程池,这是 imo 的最佳方式。
P
Peter Mortensen

我只想再添加一个很好的例子,XML Adapters。因为它被谷歌使用,我也使用相同的逻辑来避免 OutOfMemory 错误。

基本上 this ImageDownloader 是您的答案(因为它涵盖了您的大部分要求)。有些你也可以在其中实现。


未遵守 ImageDownloader 类:请参阅下面的解决方案 code.google.com/p/parleys-android-nextgen/issues/detail?id=1
h
howettl

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

开源,但由 ActionBarSherlock 的 Jake Wharton 领导。

使用一行代码从网络或应用程序资源中异步加载图像

自动列表视图检测

自动磁盘和内存缓存

可以进行自定义转换

许多可配置的选项

超级简单的API

经常更新


P
Peter Mortensen

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

Google I/O 2013 凌空图像缓存教程

开发者谷歌活动


我认为这是最好的解决方案 - 其他答案非常古老 - volley 非常快,并且与 jake warthons disklrucache 结合使用它是一个完美的解决方案 - 我尝试了很多其他的解决方案,但没有一个像 volley 一样稳定和快速
Z
Ziem

嗯,图片加载时间从网上有很多解决办法。您也可以使用库 Android-Query。它将为您提供所有必需的活动。确定你想要做什么并阅读图书馆维基页面。并解决图片加载限制。

这是我的代码:

@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
P
Peter Mortensen

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


G
Girish Patel

你必须试试这个 Universal Loader 是最好的。在对延迟加载进行了许多 RnD 之后,我正在使用它。

Universal Image Loader

特征

多线程图像加载(异步或同步)

ImageLoader 配置的广泛定制(线程执行器、下载器、解码器、内存和磁盘缓存、显示图像选项等)

每个显示图像调用的许多自定义选项(存根图像、缓存开关、解码选项、位图处理和显示等)

内存和/或磁盘(设备的文件系统或 SD 卡)中的图像缓存

监听加载过程(包括下载进度)

安卓 2.0+ 支持

https://i.stack.imgur.com/iFKcR.png


P
PatrickNLT

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

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


P
Peter Mortensen

DroidPartsImageFetcher,需要零配置才能开始。

使用磁盘和内存中最近最少使用 (LRU) 缓存。

有效地解码图像。

支持在后台线程中修改位图。

有简单的交叉淡入淡出。

具有图像加载进度回调。

以克隆 DroidPartsGram 为例:

https://i.stack.imgur.com/DegXF.png


嗨,我查看了代码示例,但在使用带有 ArrayAdapter 的 ImageFetcher 时遇到问题,您介意看看我的问题吗? stackoverflow.com/questions/21089147/… 谢谢 =]
S
Soham

Novoda 还有一个很棒的lazy image loading library,许多应用程序(如 Songkick、Podio、SecretDJ 和 ImageSearch)都使用它们的库。

他们的库在 Github 上托管 here,并且他们也有一个非常活跃的 issues tracker。他们的项目似乎也很活跃,在撰写此回复时有超过 300 次提交。


实际上,Novoda 是一个很棒的库,但是……有时您不需要一个庞大的库,而只需要一种简单的解决方案。这就是为什么 Github 中的 LazyList 如此出色的原因,如果您的应用程序仅在 listView 中显示图像并且不是您应用程序的主要功能,只是另一个我更喜欢使用更轻量级的活动。否则,如果您知道必须经常使用并且是核心的一部分,请尝试 Novoda。
B
Bijay Koirala

给那些犹豫不决的人提供一个快速提示,用于延迟加载图像的库:

有四种基本方法。

DIY => 不是最好的解决方案,但对于一些图像,如果你不想使用其他库 Volley 的延迟加载库 => 来自 android 的人。它很好,一切都很好,但文档记录很差,因此使用起来有问题。 Picasso:一个简单有效的解决方案,您甚至可以指定要引入的确切图像大小。它使用起来非常简单,但对于必须处理大量图像的应用程序来说可能不是很“高性能”。 UIL:延迟加载图像的最佳方式。您可以缓存图像(当然需要许可),初始化一次加载器,然后完成您的工作。迄今为止我见过的最成熟的异步图片加载库。


r
recnac

如果你想像 Facebook 一样显示 Shimmer 布局,有一个官方的 facebook 库。 FaceBook Shimmer 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'

https://i.stack.imgur.com/cP4QU.gif


P
Peter Mortensen

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

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

我还通过在该对象中实现 singleton 来改进 ImageLoader。


A
Akber

以上所有代码都有其自身的价值,但根据我的个人经验,请尝试使用毕加索。

Picasso 是一个专门用于此目的的库,事实上它会自动管理缓存和所有其他网络操作。您必须在项目中添加库,只需编写一行代码即可从远程 URL 加载图像。

请访问此处:http://code.tutsplus.com/tutorials/android-sdk-working-with-picasso--cms-22149


S
Saket Kumar

使用滑行库。它对我有用,也适用于您的代码。它也适用于图像和 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);
}

P
Peter Mortensen

我可以推荐一种像魅力一样工作的不同方式:Android Query。

您可以从 here 下载该 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);

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


P
Peter Mortensen

试一试Aquery。它具有非常简单的异步加载和缓存图像的方法。


P
Peter Mortensen

URLImageViewHelper 是一个很棒的库,可以帮助您做到这一点。


R
Rupesh Yadav
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;
 }
}

P
Peter Mortensen

我遇到了这个问题并实现了 lruCache。我相信您需要 API 12 及更高版本或使用兼容性 v4 库。 lurCache 是快速内存,但它也有预算,因此如果您担心可以使用磁盘缓存...Caching Bitmaps 中对此进行了描述。

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

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