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

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

  2. };

  3. @Override

  4. public void onCreate(Bundle savedInstanceState) {

  5. super.onCreate(savedInstanceState);

  6. setContentView(R.layout.main);

  7. Util.flag = 0;

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

  9. myListAdapter = new MyListAdapter();

  10. mListView.setAdapter(myListAdapter);

  11. }

  12. private class MyListAdapter extends BaseAdapter {

  13. private ViewHolder mHolder;

  14. @Override

  15. public int getCount() {

  16. return URLS.length;

  17. }

  18. @Override

  19. public Object getItem(int position) {

  20. return URLS[position];

  21. }

  22. @Override

  23. public long getItemId(int position) {

  24. return position;

  25. }

  26. @Override

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

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

  29. if (convertView == null) {

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

  31. null);

  32. mHolder = new ViewHolder();

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

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

  35. convertView.setTag(mHolder);

  36. }else {

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

  38. }

  39. final String url = URLS[position];

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

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

  42. if (mDownloader == null) {

  43. mDownloader = new ImageDownloader();

  44. }

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

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

  47. if (mDownloader != null) {

  48. //异步下载图片

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

  50. @Override

  51. public void onDownloadSucc(Bitmap bitmap,

  52. String c_url,ImageView mimageView) {

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

  54. if (imageView != null) {

  55. imageView.setImageBitmap(bitmap);

  56. imageView.setTag(“”);

  57. }

  58. }

  59. });

  60. }

  61. return convertView;

  62. }

  63. /**

  64. * 使用ViewHolder来优化listview

  65. * @author yanbin

  66. *

  67. */

  68. private class ViewHolder {

  69. ImageView mImageView;

  70. TextView mTextView;

  71. }

  72. }

  73. }

上面的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){

  90. Bitmap bitmap = null;

  91. if(imageName != null){

  92. File file = null;

  93. String real_path = “”;

  94. try {

  95. if(Util.getInstance().hasSDCard()){

  96. real_path = Util.getInstance().getExtPath() + (path != null && path.startsWith(“/”) ? path : “/” + path);

  97. }else{

  98. real_path = Util.getInstance().getPackagePath(mActivity) + (path != null && path.startsWith(“/”) ? path : “/” + path);

  99. }

  100. file = new File(real_path, imageName);

  101. if(file.exists())

  102. bitmap = BitmapFactory.decodeStream(new FileInputStream(file));

  103. } catch (Exception e) {

  104. e.printStackTrace();

  105. bitmap = null;

  106. }

  107. }

  108. return bitmap;

  109. }

  110. /**

  111. * 将下载好的图片存放到文件中

  112. * @param path 图片路径

  113. * @param mActivity

  114. * @param imageName 图片名字

  115. * @param bitmap 图片

  116. * @return

  117. */

  118. private boolean setBitmapToFile(String path,Activity mActivity,String imageName,Bitmap bitmap){

  119. File file = null;

  120. String real_path = “”;

  121. try {

  122. if(Util.getInstance().hasSDCard()){

  123. real_path = Util.getInstance().getExtPath() + (path != null && path.startsWith(“/”) ? path : “/” + path);

  124. }else{

  125. real_path = Util.getInstance().getPackagePath(mActivity) + (path != null && path.startsWith(“/”) ? path : “/” + path);

  126. }

  127. file = new File(real_path, imageName);

  128. if(!file.exists()){

  129. File file2 = new File(real_path + “/”);

  130. file2.mkdirs();

  131. }

  132. file.createNewFile();

  133. FileOutputStream fos = null;

  134. if(Util.getInstance().hasSDCard()){

  135. fos = new FileOutputStream(file);

  136. }else{

  137. fos = mActivity.openFileOutput(imageName, Context.MODE_PRIVATE);

  138. }

  139. if (imageName != null && (imageName.contains(“.png”) || imageName.contains(“.PNG”))){

  140. bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);

  141. }

  142. else{

  143. bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);

  144. }

  145. fos.flush();

  146. if(fos != null){

  147. fos.close();

  148. }

  149. return true;

  150. } catch (Exception e) {

  151. e.printStackTrace();

  152. return false;

  153. }

  154. }

  155. /**

  156. * 辅助方法,一般不调用

  157. * @param path

  158. * @param mActivity

  159. * @param imageName

  160. */

  161. private void removeBitmapFromFile(String path,Activity mActivity,String imageName){

  162. File file = null;

  163. String real_path = “”;

  164. try {

  165. if(Util.getInstance().hasSDCard()){

  166. real_path = Util.getInstance().getExtPath() + (path != null && path.startsWith(“/”) ? path : “/” + path);

  167. }else{

  168. real_path = Util.getInstance().getPackagePath(mActivity) + (path != null && path.startsWith(“/”) ? path : “/” + path);

  169. }

  170. file = new File(real_path, imageName);

  171. if(file != null)

  172. file.delete();

  173. } catch (Exception e) {

  174. e.printStackTrace();

  175. }

  176. }

  177. /**

  178. * 异步下载图片的方法

  179. * @author yanbin

  180. *

  181. */

  182. private class MyAsyncTask extends AsyncTask<String, Void, Bitmap>{

  183. private ImageView mImageView;

  184. private String url;

  185. private OnImageDownload download;

  186. private String path;

  187. private Activity mActivity;

  188. public MyAsyncTask(String url,ImageView mImageView,String path,Activity mActivity,OnImageDownload download){

  189. this.mImageView = mImageView;

  190. this.url = url;

  191. this.path = path;

  192. this.mActivity = mActivity;

  193. this.download = download;

  194. }

  195. @Override

  196. protected Bitmap doInBackground(String… params) {

  197. Bitmap data = null;

  198. if(url != null){

  199. try {

  200. URL c_url = new URL(url);

  201. InputStream bitmap_data = c_url.openStream();

  202. data = BitmapFactory.decodeStream(bitmap_data);

  203. String imageName = Util.getInstance().getImageName(url);

  204. if(!setBitmapToFile(path,mActivity,imageName, data)){

  205. removeBitmapFromFile(path,mActivity,imageName);

  206. }

  207. imageCaches.put(url, new SoftReference(data.createScaledBitmap(data, 100, 100, true)));

  208. } catch (Exception e) {

  209. e.printStackTrace();

  210. }

  211. }

  212. return data;

  213. }

  214. @Override

  215. protected void onPreExecute() {

  216. super.onPreExecute();

  217. }

  218. @Override

  219. protected void onPostExecute(Bitmap result) {

  220. //回调设置图片

  221. if(download != null){

  222. download.onDownloadSucc(result,url,mImageView);

  223. //该url对应的task已经下载完成,从map中将其删除

  224. removeTaskFormMap(url);

  225. }

  226. super.onPostExecute(result);

  227. }

  228. }

  229. }

Util.java类涉及到判断sdcard,获取图片存放路径以及从url中得到图片名称的操作,很简单,如下:

[java]  view plain copy

  1. public class Util {

  2. private static Util util;

  3. public static int flag = 0;

  4. private Util(){

  5. }

  6. public static Util getInstance(){

  7. if(util == null){

  8. util = new Util();

  9. }

  10. return util;

  11. }

  12. /**

  13. * 判断是否有sdcard

  14. * @return

  15. */

  16. public boolean hasSDCard(){

  17. boolean b = false;

  18. if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){

  19. b = true;

  20. }

  21. return b;

  22. }

  23. /**

  24. * 得到sdcard路径

  25. * @return

  26. */

  27. public String getExtPath(){

  28. String path = “”;

  29. if(hasSDCard()){

  30. path = Environment.getExternalStorageDirectory().getPath();

  31. }

  32. return path;

  33. }

  34. /**

  35. * 得到/data/data/yanbin.imagedownload目录

  36. * @param mActivity

  37. * @return

  38. */

  39. public String getPackagePath(Activity mActivity){

  40. return mActivity.getFilesDir().toString();

  41. }

  42. /**

  43. * 根据url得到图片名

  44. * @param url

  45. * @return

  46. */

  47. public String getImageName(String url) {

  48. String imageName = “”;

  49. if(url != null){

  50. imageName = url.substring(url.lastIndexOf(“/”) + 1);

  51. }

  52. return imageName;

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

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

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

如果你需要这些资料, ⬅ 专栏获取
51.      * @return

  1. */

  2. public String getImageName(String url) {

  3. String imageName = “”;

  4. if(url != null){

  5. imageName = url.substring(url.lastIndexOf(“/”) + 1);

  6. }

  7. return imageName;

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

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

[外链图片转存中…(img-bdCAjvgQ-1719172450805)]

[外链图片转存中…(img-XT9lJy7L-1719172450805)]

[外链图片转存中…(img-hfUw4akH-1719172450806)]

[外链图片转存中…(img-pR2o7xxM-1719172450807)]

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

如果你需要这些资料, ⬅ 专栏获取

  • 22
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值