如何在Android ListView中最佳地从URL加载图像

3

我一直在寻找在Android中加载ListView图片的最佳方法。

这些图片来自服务器,所以加载可能需要一些时间。据我了解,只有两种实现方式:

1-在主线程上加载图片-这样可以在视图显示和滚动时立即显示正确的图片,但会导致滚动性能差,并可能导致应用程序挂起和崩溃。

2-在AsyncTask中加载图片。这意味着当列表显示或滚动时,图片将为空,但最终会显示出来。由于列表进行的缓存,您还可以为项目获取错误的图片。但这具有良好的性能和滚动效果,并且不会挂起/崩溃。

似乎这两种解决方案都不正确。难道没有解决方法?我已看到其他类似的帖子,但答案似乎总是1或2,但这都不是一个好的解决方案...

我的列表适配器代码如下。 HttpGetImageAction.fetchImage() 方法根据我设置的标志执行异步任务或在主线程上获取图像。一旦加载完毕,我还在本地和磁盘上缓存这些图像,因此问题主要发生在第一次(但缓存仅适用于100张图片,而列表中有数千张)

public class ImageListAdapter extends ArrayAdapter<WebMediumConfig> {

Activity activity;

public ImageListAdapter(Activity activity, int resourceId, List<WebMediumConfig> items) {
    super(activity, resourceId, items);
    this.activity = activity;
}

class ImageListViewHolder {
    ImageView imageView;
    TextView nameView;
    TextView descriptionView;
    TextView statView;
}

public View getView(int position, View convertView, ViewGroup parent) {
    ImageListViewHolder holder = null;
    WebMediumConfig config = getItem(position);

    LayoutInflater mInflater = (LayoutInflater)this.activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
    if (convertView == null) {
        convertView = mInflater.inflate(R.layout.image_list, null);
        holder = new ImageListViewHolder();
        holder.imageView = (ImageView)convertView.findViewById(R.id.imageView);
        holder.nameView = (TextView) convertView.findViewById(R.id.nameView);
        holder.descriptionView = (TextView)convertView.findViewById(R.id.descriptionView);
        holder.statView = (TextView)convertView.findViewById(R.id.statView);
        convertView.setTag(holder);
    } else {
        holder = (ImageListViewHolder) convertView.getTag();
    }

    holder.nameView.setText(Utils.stripTags(config.name));
    holder.descriptionView.setText(Utils.stripTags(config.description));
    holder.statView.setText(config.stats());
    if (MainActivity.showImages) {
        HttpGetImageAction.fetchImage(this.activity, config.avatar, holder.imageView);
    } else {
        holder.imageView.setVisibility(View.GONE);
    }

    return convertView;
  }
}

Picasso很好。 - Igmer Rodriguez
不要寻找框架,代码不应该复杂。据我了解,框架正在进行异步加载,因此列表将显示为空并缓慢填充,并且对于错误的图像放置没有帮助。除非有一些神奇的事情发生,如果有的话,那么这种神奇是什么? - James
也许提供一个如何取消图像加载以避免错误图像的链接,或者是相关的代码? - James
你尝试过在Android JetPack上结合使用"Paging"库和"Retrofit"库吗? - Zwal Pyae Kyaw
@James,为什么你讨厌库?我能知道原因吗?不要试图重新发明轮子。 - Ranjithkumar
显示剩余4条评论
9个回答

4
您永远不应该在主线程上加载图像。任何网络调用都应该异步完成,不应该在主线程上完成。您的图像可能会快速加载(在主线程上)因为您可能有良好的互联网连接,有小图像和/或少量图像。但是想象一下,如果使用您的应用程序的某个人拥有较慢的互联网,并且有一天您的列表增长到数百个,会发生什么!
以下方法是加载scrollview内容的常用做法:
a)在加载scrollview时加载文本内容。再次加载应该异步执行,但可以显示加载视图,直到下载完成
b)在每个单元格上加载图像占位符,同时加载图像
c)异步加载图像
使用像Picasso这样的库将非常有帮助,因为您需要处理大量的样板代码,例如
取消单元格重用时的图像下载,
为重用的单元格启动新的下载,
缓存,
重试失败的下载
希望这可以帮助您。

0

0

在您的应用级gradle中实现以下存储库:

implementation 'com.squareup.picasso:picasso:2.5.2'

将以下行放置在您想要加载图像的位置

 Picasso.with(getApplicationContext()).load(config.avatar).into(imageview);

0

小心,OP已经在评论中表示他对毕加索不感兴趣。 - Ferdz

0

0

这是我的做法

首先,我总是创建RecyclerView.ViewHolder,将Func<int, Bitmap> image_provider传递给它(或者您可以传递某种类型的lambda?),当调用时,它将检索特定ID的图像,如果无法检索,则显示默认加载图像。

所有内容都存储在一个片段中(我称之为DataFragment),其中retainstate设置为true。

首先加载告诉我必须显示哪些图像的json对象列表,这发生在数据片段内部,因此如果有配置更改,它将继续进行。

我使用类似于SetItems(MyImages)的方法将该信息发送到适配器。

然后,我启动一个新线程来加载图像(同时允许用户工作)。

我使用TPL库来完成这项工作,Android的最接近近似是Anko Async,但您也可以使用AsyncTask来完成它,问题是我向主线程发送了很多消息,因此要使用AsyncTask,您必须为其提供一个处理程序以向主线程发送消息。

在线程中,我循环遍历我需要下载的所有图像,并下载它们并向DataFragment发送消息,然后再将其发送到当前连接的Activity中,该Activity触发Adapter中的NotifyItemChanged方法。
适配器在创建时接收一个Func<int, Bitmap> image_provider参数,调用它并从内存中检索图像。
我知道听起来有点混乱,所以如果您想要代码示例,我可以提供它们,但它们是使用C#编写的。

0

只需使用Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView); 或者 Glide.with(getApplicationContext()).load("image_url").asBitmap().centerCrop().into(new BitmapImageViewTarget(imageView)

在适配器中像任何视图一样完成,无需额外的努力。


0

简单解决方案:Glide。

Glide是一个快速高效的开源媒体管理和图像加载框架,为Android提供了一个简单易用的接口,将媒体解码、内存和磁盘缓存以及资源池包装在一起。Glide支持获取、解码和显示视频静帧、图像和动画GIF。

您可以这样加载图片:

GlideApp.with(context)
    .load("http://via.placeholder.com/300.png")
    .into(imageView);

更多信息请参考:

https://guides.codepath.com/android/Displaying-Images-with-the-Glide-Library https://github.com/bumptech/glide


0

使用 Glide 库.. 这是 Google 推荐的。

步骤 1:

使用最新的 Glide 版本 4 依赖库 (https://github.com/bumptech/glide)

implementation 'com.github.bumptech.glide:glide:4.8.0'

annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'

步骤 2:

 Glide.with(MainActivity.this)
        .load(url)
        .apply(new RequestOptions().placeholder(R.drawable.placeholder).error(R.drawable.error_image))//this line optional - you can skip this line
        .into(imageview);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接