android listview异步加载图片
来源:互联网 发布:qq网络硬盘 编辑:程序博客网 时间:2024/04/30 00:37
Android中的ListView是一个非常常用的控件,但是它却并不像想象中的那么简单。特别是当你需要在ListView中展示大量网络图片的时候,处理不好轻则用户体验不佳,重则OOM,异步线程丢失或者图片错位。
关于其中的OOM和异步线程丢失的问题,是一个很庞大的话题,本人能力有限,无法说清,只有遇到的时候临时找原因,想办法解决了。但是对于图片错位,却是可以避免的,今天我们就来说一说ListView异步加载图片中的图片错位问题。
为什么会出现图片错位的问题呢?一般是重用了convertView导致的。如果你重用了convertView,此时convertView中的ImageView的id值是相等的,而我们在设置ImageView的图片时,是根据id来设置的,此时就出现了图片错位问题。这里童鞋们可以自己去测试一下,不重用convertView,也就是每次getView的时候,都使用findViewById(R.id.xx)去得到每一个Item的ImageView,异步下载图片的方法也只是简单的开一个AsyncTask执行下载。在这种情况下,图片一般是不会产生错位的。原因很简单,认真读一读前面的内容就明白了。但是你如果真的在使用这种方法来使用getView的话,并且图片量比较大的时候,你程序的性能肯定不会好到哪里去了。因此,重用convertView还是很有必要的。
这里需要注意,convertView是否为null会根据ListView的中布局标签值的不同有区别,具体的内容请参见这两篇文章:
android listview 连续调用 getview问题分析及解决
[Android] ListView中getView的原理+如何在ListView中放置多个item
这也就是说,某种情况下你界面中的第一张和第二张图片之间就有可能产生错位,因为有可能第二个可见的ImageView就来自共用的convertView。
处理像这种图片的异步加载的问题,我们的一般思路是:下载的图片根据图片名称存入到SDCard中,最新加载的图片存入到软引用中。我们在getView中给ImageView设置图片的时候,首先根据url,从软引用中读取图片数据;如果软引用中没用,则根据url(对应图片名)从SDCard中读取图片数据;如果SDCard中也没有,则从网络上下载图片,在图片下载完成后,回调主线中的方法更新ImageView。下面我们就照着上面的思路,先把程序整出来再说吧。先看下效果图:
布局文件有两个,很简单,一个表示ListView(main.xml),一个表示ListView中的元素(single_data.xml),如下:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- android:background="@android:color/darker_gray"
- tools:context=".MainActivity" >
- <ListView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:cacheColorHint="@null"
- android:id="@+id/listview"
- />
- </LinearLayout>
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:background="@android:color/darker_gray" tools:context=".MainActivity" > <ListView android:layout_width="fill_parent" android:layout_height="wrap_content" android:cacheColorHint="@null" android:id="@+id/listview" /></LinearLayout>
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:background="@android:color/white"
- >
- <ImageView
- android:layout_width="150dp"
- android:layout_height="150dp"
- android:scaleType="fitXY"
- android:id="@+id/image_view"
- android:background="@drawable/ic_launcher"
- />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignTop="@id/image_view"
- android:layout_alignBottom="@id/image_view"
- android:layout_marginLeft="20dp"
- android:layout_alignParentRight="true"
- android:gravity="center_vertical"
- android:layout_toRightOf="@id/image_view"
- android:singleLine="true"
- android:ellipsize="end"
- android:text="@string/hello"
- android:id="@+id/text_view"
- />
- </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@android:color/white" > <ImageView android:layout_width="150dp" android:layout_height="150dp" android:scaleType="fitXY" android:id="@+id/image_view" android:background="@drawable/ic_launcher" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@id/image_view" android:layout_alignBottom="@id/image_view" android:layout_marginLeft="20dp" android:layout_alignParentRight="true" android:gravity="center_vertical" android:layout_toRightOf="@id/image_view" android:singleLine="true" android:ellipsize="end" android:text="@string/hello" android:id="@+id/text_view" /></RelativeLayout>
加入访问网络和读取,写入sdcard的权限。
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
接下来,我们来看看MainActivity.java。性能考虑,我们使用convertView和ViewHolder来重用控件。这里涉及到比较关键的一步,我们会在getView的时候给ViewHolder中的ImageView设置tag,其值为要放置在该ImageView上的图片的url地址。这个tag很重要,在异步下载图片完成回调的方法中,我们使用findViewWithTag(String url)来找到ListView中对应的ImagView,然后给该ImageView设置图片即可。其他的就是设置adapter的一般操作了。
- public class MainActivity extends Activity {
- ListView mListView;
- ImageDownloader mDownloader;
- MyListAdapter myListAdapter;
- private static final String TAG = "MainActivity";
- int m_flag = 0;
- private static final String[] URLS = {
- //图片地址就不贴了,自己去这篇帖子中找吧:http://www.cnblogs.com/liongname/articles/2345087.html
- //其中有几张图片访问不了。
- };
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- Util.flag = 0;
- mListView = (ListView) findViewById(R.id.listview);
- myListAdapter = new MyListAdapter();
- mListView.setAdapter(myListAdapter);
- }
- private class MyListAdapter extends BaseAdapter {
- private ViewHolder mHolder;
- @Override
- public int getCount() {
- return URLS.length;
- }
- @Override
- public Object getItem(int position) {
- return URLS[position];
- }
- @Override
- public long getItemId(int position) {
- return position;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- //只有当convertView不存在的时候才去inflate子元素
- if (convertView == null) {
- convertView = getLayoutInflater().inflate(R.layout.single_data,
- null);
- mHolder = new ViewHolder();
- mHolder.mImageView = (ImageView) convertView.findViewById(R.id.image_view);
- mHolder.mTextView = (TextView) convertView.findViewById(R.id.text_view);
- convertView.setTag(mHolder);
- }else {
- mHolder = (ViewHolder) convertView.getTag();
- }
- final String url = URLS[position];
- mHolder.mTextView.setText(url != null ? url.substring(url.lastIndexOf("/") + 1) : "");
- mHolder.mImageView.setTag(URLS[position]);
- if (mDownloader == null) {
- mDownloader = new ImageDownloader();
- }
- //这句代码的作用是为了解决convertView被重用的时候,图片预设的问题
- mHolder.mImageView.setImageResource(R.drawable.ic_launcher);
- if (mDownloader != null) {
- //异步下载图片
- mDownloader.imageDownload(url, mHolder.mImageView, "/yanbin",MainActivity.this, new OnImageDownload() {
- @Override
- public void onDownloadSucc(Bitmap bitmap,
- String c_url,ImageView mimageView) {
- ImageView imageView = (ImageView) mListView.findViewWithTag(c_url);
- if (imageView != null) {
- imageView.setImageBitmap(bitmap);
- imageView.setTag("");
- }
- }
- });
- }
- return convertView;
- }
- /**
- * 使用ViewHolder来优化listview
- * @author yanbin
- *
- */
- private class ViewHolder {
- ImageView mImageView;
- TextView mTextView;
- }
- }
- }
public class MainActivity extends Activity {ListView mListView;ImageDownloader mDownloader;MyListAdapter myListAdapter;private static final String TAG = "MainActivity";int m_flag = 0;private static final String[] URLS = {//图片地址就不贴了,自己去这篇帖子中找吧:http://www.cnblogs.com/liongname/articles/2345087.html//其中有几张图片访问不了。 };@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);Util.flag = 0;mListView = (ListView) findViewById(R.id.listview);myListAdapter = new MyListAdapter();mListView.setAdapter(myListAdapter);}private class MyListAdapter extends BaseAdapter {private ViewHolder mHolder;@Overridepublic int getCount() {return URLS.length;}@Overridepublic Object getItem(int position) {return URLS[position];}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {//只有当convertView不存在的时候才去inflate子元素if (convertView == null) {convertView = getLayoutInflater().inflate(R.layout.single_data,null); mHolder = new ViewHolder(); mHolder.mImageView = (ImageView) convertView.findViewById(R.id.image_view); mHolder.mTextView = (TextView) convertView.findViewById(R.id.text_view); convertView.setTag(mHolder);}else { mHolder = (ViewHolder) convertView.getTag(); }final String url = URLS[position]; mHolder.mTextView.setText(url != null ? url.substring(url.lastIndexOf("/") + 1) : ""); mHolder.mImageView.setTag(URLS[position]);if (mDownloader == null) {mDownloader = new ImageDownloader();}//这句代码的作用是为了解决convertView被重用的时候,图片预设的问题mHolder.mImageView.setImageResource(R.drawable.ic_launcher);if (mDownloader != null) {//异步下载图片mDownloader.imageDownload(url, mHolder.mImageView, "/yanbin",MainActivity.this, new OnImageDownload() {@Overridepublic void onDownloadSucc(Bitmap bitmap,String c_url,ImageView mimageView) {ImageView imageView = (ImageView) mListView.findViewWithTag(c_url);if (imageView != null) {imageView.setImageBitmap(bitmap);imageView.setTag("");} }});}return convertView;}/** * 使用ViewHolder来优化listview * @author yanbin * */private class ViewHolder {ImageView mImageView;TextView mTextView;}}}
上面的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接口的代码:
- public interface OnImageDownload {
- void onDownloadSucc(Bitmap bitmap,String c_url,ImageView imageView);
- }
public interface OnImageDownload {void onDownloadSucc(Bitmap bitmap,String c_url,ImageView imageView);}
ImageDownloader.java的代码(有两百多行,拷贝到eclipse中看会舒服一点):
- public class ImageDownloader {
- private static final String TAG = "ImageDownloader";
- private HashMap<String, MyAsyncTask> map = new HashMap<String, MyAsyncTask>();
- private Map<String, SoftReference<Bitmap>> imageCaches = new HashMap<String, SoftReference<Bitmap>>();
- /**
- *
- * @param url 该mImageView对应的url
- * @param mImageView
- * @param path 文件存储路径
- * @param mActivity
- * @param download OnImageDownload回调接口,在onPostExecute()中被调用
- */
- public void imageDownload(String url,ImageView mImageView,String path,Activity mActivity,OnImageDownload download){
- SoftReference<Bitmap> currBitmap = imageCaches.get(url);
- Bitmap softRefBitmap = null;
- if(currBitmap != null){
- softRefBitmap = currBitmap.get();
- }
- String imageName = "";
- if(url != null){
- imageName = Util.getInstance().getImageName(url);
- }
- Bitmap bitmap = getBitmapFromFile(mActivity,imageName,path);
- //先从软引用中拿数据
- if(currBitmap != null && mImageView != null && softRefBitmap != null && url.equals(mImageView.getTag())){
- mImageView.setImageBitmap(softRefBitmap);
- }
- //软引用中没有,从文件中拿数据
- else if(bitmap != null && mImageView != null && url.equals(mImageView.getTag())){
- mImageView.setImageBitmap(bitmap);
- }
- //文件中也没有,此时根据mImageView的tag,即url去判断该url对应的task是否已经在执行,如果在执行,本次操作不创建新的线程,否则创建新的线程。
- else if(url != null && needCreateNewTask(mImageView)){
- MyAsyncTask task = new MyAsyncTask(url, mImageView, path,mActivity,download);
- if(mImageView != null){
- Log.i(TAG, "执行MyAsyncTask --> " + Util.flag);
- Util.flag ++;
- task.execute();
- //将对应的url对应的任务存起来
- map.put(url, task);
- }
- }
- }
- /**
- * 判断是否需要重新创建线程下载图片,如果需要,返回值为true。
- * @param url
- * @param mImageView
- * @return
- */
- private boolean needCreateNewTask(ImageView mImageView){
- boolean b = true;
- if(mImageView != null){
- String curr_task_url = (String)mImageView.getTag();
- if(isTasksContains(curr_task_url)){
- b = false;
- }
- }
- return b;
- }
- /**
- * 检查该url(最终反映的是当前的ImageView的tag,tag会根据position的不同而不同)对应的task是否存在
- * @param url
- * @return
- */
- private boolean isTasksContains(String url){
- boolean b = false;
- if(map != null && map.get(url) != null){
- b = true;
- }
- return b;
- }
- /**
- * 删除map中该url的信息,这一步很重要,不然MyAsyncTask的引用会“一直”存在于map中
- * @param url
- */
- private void removeTaskFormMap(String url){
- if(url != null && map != null && map.get(url) != null){
- map.remove(url);
- System.out.println("当前map的大小=="+map.size());
- }
- }
- /**
- * 从文件中拿图片
- * @param mActivity
- * @param imageName 图片名字
- * @param path 图片路径
- * @return
- */
- private Bitmap getBitmapFromFile(Activity mActivity,String imageName,String path){
- Bitmap bitmap = null;
- if(imageName != null){
- File file = null;
- String real_path = "";
- try {
- if(Util.getInstance().hasSDCard()){
- real_path = Util.getInstance().getExtPath() + (path != null && path.startsWith("/") ? path : "/" + path);
- }else{
- real_path = Util.getInstance().getPackagePath(mActivity) + (path != null && path.startsWith("/") ? path : "/" + path);
- }
- file = new File(real_path, imageName);
- if(file.exists())
- bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
- } catch (Exception e) {
- e.printStackTrace();
- bitmap = null;
- }
- }
- return bitmap;
- }
- /**
- * 将下载好的图片存放到文件中
- * @param path 图片路径
- * @param mActivity
- * @param imageName 图片名字
- * @param bitmap 图片
- * @return
- */
- private boolean setBitmapToFile(String path,Activity mActivity,String imageName,Bitmap bitmap){
- File file = null;
- String real_path = "";
- try {
- if(Util.getInstance().hasSDCard()){
- real_path = Util.getInstance().getExtPath() + (path != null && path.startsWith("/") ? path : "/" + path);
- }else{
- real_path = Util.getInstance().getPackagePath(mActivity) + (path != null && path.startsWith("/") ? path : "/" + path);
- }
- file = new File(real_path, imageName);
- if(!file.exists()){
- File file2 = new File(real_path + "/");
- file2.mkdirs();
- }
- file.createNewFile();
- FileOutputStream fos = null;
- if(Util.getInstance().hasSDCard()){
- fos = new FileOutputStream(file);
- }else{
- fos = mActivity.openFileOutput(imageName, Context.MODE_PRIVATE);
- }
- if (imageName != null && (imageName.contains(".png") || imageName.contains(".PNG"))){
- bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
- }
- else{
- bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
- }
- fos.flush();
- if(fos != null){
- fos.close();
- }
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * 辅助方法,一般不调用
- * @param path
- * @param mActivity
- * @param imageName
- */
- private void removeBitmapFromFile(String path,Activity mActivity,String imageName){
- File file = null;
- String real_path = "";
- try {
- if(Util.getInstance().hasSDCard()){
- real_path = Util.getInstance().getExtPath() + (path != null && path.startsWith("/") ? path : "/" + path);
- }else{
- real_path = Util.getInstance().getPackagePath(mActivity) + (path != null && path.startsWith("/") ? path : "/" + path);
- }
- file = new File(real_path, imageName);
- if(file != null)
- file.delete();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 异步下载图片的方法
- * @author yanbin
- *
- */
- private class MyAsyncTask extends AsyncTask<String, Void, Bitmap>{
- private ImageView mImageView;
- private String url;
- private OnImageDownload download;
- private String path;
- private Activity mActivity;
- public MyAsyncTask(String url,ImageView mImageView,String path,Activity mActivity,OnImageDownload download){
- this.mImageView = mImageView;
- this.url = url;
- this.path = path;
- this.mActivity = mActivity;
- this.download = download;
- }
- @Override
- protected Bitmap doInBackground(String... params) {
- Bitmap data = null;
- if(url != null){
- try {
- URL c_url = new URL(url);
- InputStream bitmap_data = c_url.openStream();
- data = BitmapFactory.decodeStream(bitmap_data);
- String imageName = Util.getInstance().getImageName(url);
- if(!setBitmapToFile(path,mActivity,imageName, data)){
- removeBitmapFromFile(path,mActivity,imageName);
- }
- imageCaches.put(url, new SoftReference<Bitmap>(data.createScaledBitmap(data, 100, 100, true)));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return data;
- }
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- }
- @Override
- protected void onPostExecute(Bitmap result) {
- //回调设置图片
- if(download != null){
- download.onDownloadSucc(result,url,mImageView);
- //该url对应的task已经下载完成,从map中将其删除
- removeTaskFormMap(url);
- }
- super.onPostExecute(result);
- }
- }
- }
public class ImageDownloader {private static final String TAG = "ImageDownloader";private HashMap<String, MyAsyncTask> map = new HashMap<String, MyAsyncTask>();private Map<String, SoftReference<Bitmap>> imageCaches = new HashMap<String, SoftReference<Bitmap>>();/** * * @param url 该mImageView对应的url * @param mImageView * @param path 文件存储路径 * @param mActivity * @param download OnImageDownload回调接口,在onPostExecute()中被调用 */public void imageDownload(String url,ImageView mImageView,String path,Activity mActivity,OnImageDownload download){SoftReference<Bitmap> currBitmap = imageCaches.get(url);Bitmap softRefBitmap = null;if(currBitmap != null){softRefBitmap = currBitmap.get();}String imageName = "";if(url != null){imageName = Util.getInstance().getImageName(url);}Bitmap bitmap = getBitmapFromFile(mActivity,imageName,path);//先从软引用中拿数据if(currBitmap != null && mImageView != null && softRefBitmap != null && url.equals(mImageView.getTag())){mImageView.setImageBitmap(softRefBitmap);}//软引用中没有,从文件中拿数据else if(bitmap != null && mImageView != null && url.equals(mImageView.getTag())){mImageView.setImageBitmap(bitmap);}//文件中也没有,此时根据mImageView的tag,即url去判断该url对应的task是否已经在执行,如果在执行,本次操作不创建新的线程,否则创建新的线程。else if(url != null && needCreateNewTask(mImageView)){MyAsyncTask task = new MyAsyncTask(url, mImageView, path,mActivity,download);if(mImageView != null){Log.i(TAG, "执行MyAsyncTask --> " + Util.flag);Util.flag ++;task.execute();//将对应的url对应的任务存起来map.put(url, task);}}}/** * 判断是否需要重新创建线程下载图片,如果需要,返回值为true。 * @param url * @param mImageView * @return */private boolean needCreateNewTask(ImageView mImageView){boolean b = true;if(mImageView != null){String curr_task_url = (String)mImageView.getTag();if(isTasksContains(curr_task_url)){b = false;}}return b;}/** * 检查该url(最终反映的是当前的ImageView的tag,tag会根据position的不同而不同)对应的task是否存在 * @param url * @return */private boolean isTasksContains(String url){boolean b = false;if(map != null && map.get(url) != null){b = true;}return b;}/** * 删除map中该url的信息,这一步很重要,不然MyAsyncTask的引用会“一直”存在于map中 * @param url */private void removeTaskFormMap(String url){if(url != null && map != null && map.get(url) != null){map.remove(url);System.out.println("当前map的大小=="+map.size());}}/** * 从文件中拿图片 * @param mActivity * @param imageName 图片名字 * @param path 图片路径 * @return */private Bitmap getBitmapFromFile(Activity mActivity,String imageName,String path){Bitmap bitmap = null;if(imageName != null){File file = null;String real_path = "";try {if(Util.getInstance().hasSDCard()){real_path = Util.getInstance().getExtPath() + (path != null && path.startsWith("/") ? path : "/" + path);}else{real_path = Util.getInstance().getPackagePath(mActivity) + (path != null && path.startsWith("/") ? path : "/" + path);}file = new File(real_path, imageName);if(file.exists())bitmap = BitmapFactory.decodeStream(new FileInputStream(file));} catch (Exception e) {e.printStackTrace();bitmap = null;}}return bitmap;}/** * 将下载好的图片存放到文件中 * @param path 图片路径 * @param mActivity * @param imageName 图片名字 * @param bitmap 图片 * @return */private boolean setBitmapToFile(String path,Activity mActivity,String imageName,Bitmap bitmap){File file = null;String real_path = "";try {if(Util.getInstance().hasSDCard()){real_path = Util.getInstance().getExtPath() + (path != null && path.startsWith("/") ? path : "/" + path);}else{real_path = Util.getInstance().getPackagePath(mActivity) + (path != null && path.startsWith("/") ? path : "/" + path);}file = new File(real_path, imageName);if(!file.exists()){File file2 = new File(real_path + "/");file2.mkdirs();}file.createNewFile();FileOutputStream fos = null;if(Util.getInstance().hasSDCard()){fos = new FileOutputStream(file);}else{fos = mActivity.openFileOutput(imageName, Context.MODE_PRIVATE);}if (imageName != null && (imageName.contains(".png") || imageName.contains(".PNG"))){bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);}else{bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);}fos.flush();if(fos != null){fos.close();}return true;} catch (Exception e) {e.printStackTrace();return false;}}/** * 辅助方法,一般不调用 * @param path * @param mActivity * @param imageName */private void removeBitmapFromFile(String path,Activity mActivity,String imageName){File file = null;String real_path = "";try {if(Util.getInstance().hasSDCard()){real_path = Util.getInstance().getExtPath() + (path != null && path.startsWith("/") ? path : "/" + path);}else{real_path = Util.getInstance().getPackagePath(mActivity) + (path != null && path.startsWith("/") ? path : "/" + path);}file = new File(real_path, imageName);if(file != null)file.delete();} catch (Exception e) {e.printStackTrace();}}/** * 异步下载图片的方法 * @author yanbin * */private class MyAsyncTask extends AsyncTask<String, Void, Bitmap>{private ImageView mImageView;private String url;private OnImageDownload download;private String path;private Activity mActivity;public MyAsyncTask(String url,ImageView mImageView,String path,Activity mActivity,OnImageDownload download){this.mImageView = mImageView;this.url = url;this.path = path;this.mActivity = mActivity;this.download = download;}@Overrideprotected Bitmap doInBackground(String... params) {Bitmap data = null;if(url != null){try {URL c_url = new URL(url);InputStream bitmap_data = c_url.openStream();data = BitmapFactory.decodeStream(bitmap_data);String imageName = Util.getInstance().getImageName(url);if(!setBitmapToFile(path,mActivity,imageName, data)){removeBitmapFromFile(path,mActivity,imageName);}imageCaches.put(url, new SoftReference<Bitmap>(data.createScaledBitmap(data, 100, 100, true)));} catch (Exception e) {e.printStackTrace();}}return data;}@Overrideprotected void onPreExecute() {super.onPreExecute();}@Overrideprotected void onPostExecute(Bitmap result) {//回调设置图片if(download != null){download.onDownloadSucc(result,url,mImageView);//该url对应的task已经下载完成,从map中将其删除removeTaskFormMap(url);}super.onPostExecute(result);}}}
Util.java类涉及到判断sdcard,获取图片存放路径以及从url中得到图片名称的操作,很简单,如下:
- public class Util {
- private static Util util;
- public static int flag = 0;
- private Util(){
- }
- public static Util getInstance(){
- if(util == null){
- util = new Util();
- }
- return util;
- }
- /**
- * 判断是否有sdcard
- * @return
- */
- public boolean hasSDCard(){
- boolean b = false;
- if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
- b = true;
- }
- return b;
- }
- /**
- * 得到sdcard路径
- * @return
- */
- public String getExtPath(){
- String path = "";
- if(hasSDCard()){
- path = Environment.getExternalStorageDirectory().getPath();
- }
- return path;
- }
- /**
- * 得到/data/data/yanbin.imagedownload目录
- * @param mActivity
- * @return
- */
- public String getPackagePath(Activity mActivity){
- return mActivity.getFilesDir().toString();
- }
- /**
- * 根据url得到图片名
- * @param url
- * @return
- */
- public String getImageName(String url) {
- String imageName = "";
- if(url != null){
- imageName = url.substring(url.lastIndexOf("/") + 1);
- }
- return imageName;
- }
- }
public class Util {private static Util util;public static int flag = 0;private Util(){}public static Util getInstance(){if(util == null){util = new Util();}return util;}/** * 判断是否有sdcard * @return */public boolean hasSDCard(){boolean b = false;if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){b = true;}return b;}/** * 得到sdcard路径 * @return */public String getExtPath(){String path = "";if(hasSDCard()){path = Environment.getExternalStorageDirectory().getPath();}return path;}/** * 得到/data/data/yanbin.imagedownload目录 * @param mActivity * @return */public String getPackagePath(Activity mActivity){return mActivity.getFilesDir().toString();}/** * 根据url得到图片名 * @param url * @return */public String getImageName(String url) {String imageName = "";if(url != null){imageName = url.substring(url.lastIndexOf("/") + 1);}return imageName;}}
至此,代码就全部贴完了。代码中我用了47张图片做测试,MyAsyncTask.java执行了47次,当最后listView中的最后一张图片展示出来的的时候,map的size为0。上面的一个程序主要解决了图片错位和AsyncTask重复创建的问题。但是还是有不少需要完善的地方,比如同步,比如图片的定期清理(这个可以通过拿每张图片的最后更新时间,根据与当前时间的间隔将缓存图片删除即可)。今天就到这里了,有更好的方法请推荐,有不懂的地方可以回复交流。
DEMO:点这里下载
- Android ListView异步加载图片
- Android ListView异步加载图片
- android ListView 异步加载图片
- Android ListView异步加载图片
- Android ListView异步加载图片
- Android ListView 异步加载图片
- Android ListView异步加载图片
- Android listview 图片异步加载
- Android ListView 异步加载图片
- Android--ListView异步加载图片
- android listview异步加载图片
- Android ListView 异步加载图片
- android listview异步加载图片
- Android ListView异步加载图片
- Android ListView异步加载图片
- Android listview异步加载图片
- Android ListView 异步加载图片
- android listview异步加载图片
- hibernate的学习(BBS论坛)
- JavaMail学习笔记(七)帐号激活与忘记密码 实例
- android中有关ConvertView 的相关介绍
- AOP的简单实现
- VS2010 C++学习(2):BMP图像文件的处理
- android listview异步加载图片
- UVa 572 - Oil Deposits
- Hibernate各种状态与操作
- jCarousel
- JavaEE---Servlet会话跟踪之SessionContext
- Oracle 11g EM 重新配置
- Qt中的串口编程之一
- test
- ps 检索进程号