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

图像总数不是固定的。


当前回答

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

在Listview教程中延迟加载图像

其他回答

我用图像制作了一个懒惰列表(位于GitHub)的简单演示。

基本用途ImageLoader ImageLoader=新的ImageLoader(上下文)。。。imageLoader.DisplayImage(url,imageView);不要忘记添加对AndroidManifest.xml的以下权限:<uses permission android:name=“android.ppermission.INTERNET”/><uses permission android:name=“android.permission.WRITE_EXTERNAL_STORAGE”/>请只创建ImageLoader的一个实例,并在您的应用这样,图像缓存将更加高效。

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

另一种方法是通过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();

当然,您应该始终缓存图像以避免额外的操作,您可以将图像放在HashMap数组中,检查图像是否存在于数组中,如果不存在,则继续执行线程或从HashMap数组加载图像。此外,请始终检查内存是否泄漏,位图和可绘制文件通常占用大量内存。优化代码取决于您。

如果你想像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();

在渐变文件中添加此相关性。

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

这是它的样子。

除了异步加载数据缓存外,您可能需要UI缓存,如setViewCacheSize

除了加载可见项数据外,您可能需要加载近似的可见项数据

AndroidX分页库是另一个选项,例如,您可以从SQLite数据库加载、缓存和显示10000000个项目到RecyclerView。参考PagedList

例子:假设列表视图可见项为[6,7,8,9,10],您可能需要加载[6,7,8,10]并预加载项[1,2,3,4,5]和[11,12,13,14,15],因为用户可能会滚动到前页或后页

更新:请注意,这个答案现在非常无效。垃圾回收器对SoftReference和WeakReference进行攻击,因此此代码不适用于新应用。(相反,请尝试其他答案中建议的通用图像加载器之类的库。)

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

一年前有一场关于谷歌群组上SoftReferences的讨论:链接到线程。作为过早垃圾收集的解决方案,他们建议使用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();
}