android中ListView异步加载图片时的图片错位问题解决方案

  1.   

[java]  view plain copy

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”

  3. android:layout_width=“fill_parent”

  4. android:layout_height=“wrap_content”

  5. android:background=“@android:color/white”

  6. >

  7. <ImageView

  8. android:layout_width=“150dp”

  9. android:layout_height=“150dp”

  10. android:scaleType=“fitXY”

  11. android:id=“@+id/image_view”

  12. android:background=“@drawable/ic_launcher”

  13. />

  14. <TextView

  15. android:layout_width=“wrap_content”

  16. android:layout_height=“wrap_content”

  17. android:layout_alignTop=“@id/image_view”

  18. android:layout_alignBottom=“@id/image_view”

  19. android:layout_marginLeft=“20dp”

  20. android:layout_alignParentRight=“true”

  21. android:gravity=“center_vertical”

  22. android:layout_toRightOf=“@id/image_view”

  23. android:singleLine=“true”

  24. android:ellipsize=“end”

  25. android:text=“@string/hello”

  26. android:id=“@+id/text_view”

  27. />

  28.   

加入访问网络和读取,写入sdcard的权限。

[java]  view plain copy

  1.   
  2.   
  3.   

接下来,我们来看看MainActivity.java。性能考虑,我们使用convertView和ViewHolder来重用控件。这里涉及到比较关键的一步,我们会在getView的时候给ViewHolder中的ImageView设置tag,其值为要放置在该ImageView上的图片的url地址。这个tag很重要,在异步下载图片完成回调的方法中,我们使用findViewWithTag(String url)来找到ListView中对应的ImagView,然后给该ImageView设置图片即可。其他的就是设置adapter的一般操作了。

[java]  view plain copy

  1. public class MainActivity extends Activity {

  2. ListView mListView;

  3. ImageDownloader mDownloader;

  4. MyListAdapter myListAdapter;

  5. private static final String TAG = “MainActivity”;

  6. int m_flag = 0;

  7. private static final String[] URLS = {

  8. //图片地址就不贴了,自己去这篇帖子中找吧:http://www.cnblogs.com/liongname/articles/2345087.html

  9. //其中有几张图片访问不了。

  10. };

  11. @Override

  12. public void onCreate(Bundle savedInstanceState) {

  13. super.onCreate(savedInstanceState);

  14. setContentView(R.layout.main);

  15. Util.flag = 0;

  16. mListView = (ListView) findViewById(R.id.listview);

  17. myListAdapter = new MyListAdapter();

  18. mListView.setAdapter(myListAdapter);

  19. }

  20. private class MyListAdapter extends BaseAdapter {

  21. private ViewHolder mHolder;

  22. @Override

  23. public int getCount() {

  24. return URLS.length;

  25. }

  26. @Override

  27. public Object getItem(int position) {

  28. return URLS[position];

  29. }

  30. @Override

  31. public long getItemId(int position) {

  32. return position;

  33. }

  34. @Override

  35. public View getView(int position, View convertView, ViewGroup parent) {

  36. //只有当convertView不存在的时候才去inflate子元素

  37. if (convertView == null) {

  38. convertView = getLayoutInflater().inflate(R.layout.single_data,

  39. null);

  40. mHolder = new ViewHolder();

  41. mHolder.mImageView = (ImageView) convertView.findViewById(R.id.image_view);

  42. mHolder.mTextView = (TextView) convertView.findViewById(R.id.text_view);

  43. convertView.setTag(mHolder);

  44. }else {

  45. mHolder = (ViewHolder) convertView.getTag();

  46. }

  47. final String url = URLS[position];

  48. mHolder.mTextView.setText(url != null ? url.substring(url.lastIndexOf(“/”) + 1) : “”);

  49. mHolder.mImageView.setTag(URLS[position]);

  50. if (mDownloader == null) {

  51. mDownloader = new ImageDownloader();

  52. }

  53. //这句代码的作用是为了解决convertView被重用的时候,图片预设的问题

  54. mHolder.mImageView.setImageResource(R.drawable.ic_launcher);

  55. if (mDownloader != null) {

  56. //异步下载图片

  57. mDownloader.imageDownload(url, mHolder.mImageView, “/yanbin”,MainActivity.this, new OnImageDownload() {

  58. @Override

  59. public void onDownloadSucc(Bitmap bitmap,

  60. String c_url,ImageView mimageView) {

  61. ImageView imageView = (ImageView) mListView.findViewWithTag(c_url);

  62. if (imageView != null) {

  63. imageView.setImageBitmap(bitmap);

  64. imageView.setTag(“”);

  65. }

  66. }

  67. });

  68. }

  69. return convertView;

  70. }

  71. /**

  72. * 使用ViewHolder来优化listview

  73. * @author yanbin

  74. *

  75. */

  76. private class ViewHolder {

  77. ImageView mImageView;

  78. TextView mTextView;

  79. }

  80. }

  81. }

上面的mDownloader.imageDownload()就是异步下载图片比较核心的方法,该方法在ImageDownloader.java类下。其中的五个参数分别为:要设置在当前ImageView 上的图片的url地址,当前ImageView,文件缓存地址,当前的activity以及图片回调接口。

在ImageDownloader类中,我们首先根据url从软引用中获取图片,如果不存在,从sdcard中读取图片,如果还不存在,则启动一个AsyncTask异步下载图片。注意注意:这里我们做了一个这样的操作:用一个map将当前的url及其对应的MyAsyncTask存放起来了。由于getView会执行至少一次,这一步的操作是为了相同的url创建相同的AsyncTask。在onPostExecute()方法中,将该url对应的信息从map中删除,一定要记得执行这一步。看到很多的异步图片下载的例子中,重复创建AsyncTask都是普遍存在的,这里我们使用上面的思路解决掉了这一问题。更详细的代码自己看ImageDownloader.java类吧,首先给出OnImageDownload.java接口的代码:

[java]  view plain copy

  1. public interface OnImageDownload {

  2. void onDownloadSucc(Bitmap bitmap,String c_url,ImageView imageView);

  3. }

ImageDownloader.java的代码(有两百多行,拷贝到eclipse中看会舒服一点):

[java]  view plain copy

  1. public class ImageDownloader {

  2. private static final String TAG = “ImageDownloader”;

  3. private HashMap<String, MyAsyncTask> map = new HashMap<String, MyAsyncTask>();

  4. private Map<String, SoftReference> imageCaches = new HashMap<String, SoftReference>();

  5. /**

  6. *

  7. * @param url 该mImageView对应的url

  8. * @param mImageView

  9. * @param path 文件存储路径

  10. * @param mActivity

  11. * @param download OnImageDownload回调接口,在onPostExecute()中被调用

  12. */

  13. public void imageDownload(String url,ImageView mImageView,String path,Activity mActivity,OnImageDownload download){

  14. SoftReference currBitmap = imageCaches.get(url);

  15. Bitmap softRefBitmap = null;

  16. if(currBitmap != null){

  17. softRefBitmap = currBitmap.get();

  18. }

  19. String imageName = “”;

  20. if(url != null){

  21. imageName = Util.getInstance().getImageName(url);

  22. }

  23. Bitmap bitmap = getBitmapFromFile(mActivity,imageName,path);

  24. //先从软引用中拿数据

  25. if(currBitmap != null && mImageView != null && softRefBitmap != null && url.equals(mImageView.getTag())){

  26. mImageView.setImageBitmap(softRefBitmap);

  27. }

  28. //软引用中没有,从文件中拿数据

  29. else if(bitmap != null && mImageView != null && url.equals(mImageView.getTag())){

  30. mImageView.setImageBitmap(bitmap);

  31. }

  32. //文件中也没有,此时根据mImageView的tag,即url去判断该url对应的task是否已经在执行,如果在执行,本次操作不创建新的线程,否则创建新的线程。

  33. else if(url != null && needCreateNewTask(mImageView)){

  34. MyAsyncTask task = new MyAsyncTask(url, mImageView, path,mActivity,download);

  35. if(mImageView != null){

  36. Log.i(TAG, "执行MyAsyncTask --> " + Util.flag);

  37. Util.flag ++;

  38. task.execute();

  39. //将对应的url对应的任务存起来

  40. map.put(url, task);

  41. }

  42. }

  43. }

  44. /**

  45. * 判断是否需要重新创建线程下载图片,如果需要,返回值为true。

  46. * @param url

  47. * @param mImageView

  48. * @return

  49. */

  50. private boolean needCreateNewTask(ImageView mImageView){

  51. boolean b = true;

  52. if(mImageView != null){

  53. String curr_task_url = (String)mImageView.getTag();

  54. if(isTasksContains(curr_task_url)){

  55. b = false;

  56. }

  57. }

  58. return b;

  59. }

  60. /**

  61. * 检查该url(最终反映的是当前的ImageView的tag,tag会根据position的不同而不同)对应的task是否存在

  62. * @param url

  63. * @return

  64. */

  65. private boolean isTasksContains(String url){

  66. boolean b = false;

  67. if(map != null && map.get(url) != null){

  68. b = true;

  69. }

  70. return b;

  71. }

  72. /**

  73. * 删除map中该url的信息,这一步很重要,不然MyAsyncTask的引用会“一直”存在于map中

  74. * @param url

  75. */

  76. private void removeTaskFormMap(String url){

  77. if(url != null && map != null && map.get(url) != null){

  78. map.remove(url);

  79. System.out.println(“当前map的大小==”+map.size());

  80. }

  81. }

  82. /**

  83. * 从文件中拿图片

  84. * @param mActivity

  85. * @param imageName 图片名字

  86. * @param path 图片路径

  87. * @return

  88. */

  89. private Bitmap getBitmapFromFile(Activity mActivity,String imageName,String path){

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

最后针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

需要资料的朋友可以点击我的GitHub免费领取

img-zZscr5wk-1711140039527)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-JCuBeoG4-1711140039527)]

最后

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

[外链图片转存中…(img-mClQtrX9-1711140039528)]

最后针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

需要资料的朋友可以点击我的GitHub免费领取
  • 23
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用 ListView 异步加载图片,可以通过以下步骤实现: 1. 创建一个自定义的适配器(Adapter)类,继承自 BaseAdapter。这个适配器将负责管理数据和视图的绑定。 2. 在适配器,创建一个内部类 ViewHolder,用于保存列表项的视图引用。这个类将包含一个 ImageView 用于显示图片。 3. 在适配器的 getView 方法,获取当前列表项的数据,并更新 ViewHolder 的 ImageView。 4. 在更新 ImageView ,可以使用异步加载图片的第三方库,如 Glide 或 Picasso。这些库提供了简单的接口来加载网络图片,并且处理了图片的缓存和压缩等问题。 下面是一个简单的示例代码: ```java public class CustomAdapter extends BaseAdapter { private List<String> imageUrlList; private Context context; public CustomAdapter(Context context, List<String> imageUrlList) { this.context = context; this.imageUrlList = imageUrlList; } @Override public int getCount() { return imageUrlList.size(); } @Override public Object getItem(int position) { return imageUrlList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { convertView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false); viewHolder = new ViewHolder(); viewHolder.imageView = convertView.findViewById(R.id.image_view); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } String imageUrl = imageUrlList.get(position); // 使用 Glide 异步加载图片 Glide.with(context) .load(imageUrl) .placeholder(R.drawable.placeholder_image) // 加载显示的占位图 .error(R.drawable.error_image) // 加载失败显示的错误图 .into(viewHolder.imageView); return convertView; } static class ViewHolder { ImageView imageView; } } ``` 在上述示例代码,CustomAdapter 是自定义的适配器类,其的 getView 方法使用了 Glide 来异步加载图片。你可以将 imageUrlList 替换为你自己的图片地址列表,并根据需要修改加载加载失败显示的图片资源。同,记得在布局文件定义好 ImageView 的 id。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值