前言:本以为异步加载挺简单,因为网上代码多,但真想要做好,还真不那么简单,从看代码到弄懂再到自己写,实在是有太多的东西需要学了,用了两天的时间,终于弄出来了,因为用到回调函数,所以理解起来可能难度有点大,讲起来也不太好讲,我尽力讲的明白些,其实还是要多看代码,自己摸索摸索,动手写写就什么都理解了。这篇我们只讲怎样实现异步加载,对于滑动时停止加载的事下篇再讲。
实现效果:
1、异步加载图片,在加载图片时,先加载一个默认的图片,然后在后台加载图片,加载完成后显示出来;
2、当用户在滑动时,停止加载图片的线程,当停止滑动时,看哪几个ITEM在显示屏内,只加载这几个,其它线程保持阻止;(下篇再讲)
效果图:
刚开始加载时 向下划动(新划出来的是空白块) 停划,加载完成
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ListView android:id="@+id/list" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </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="70.0dip" android:background="@drawable/item" android:drawingCacheQuality="high" android:minHeight="70.0dip" android:orientation="horizontal" > <ImageView android:id="@+id/sItemIcon" android:layout_width="42.0dip" android:layout_height="54.0dip" android:layout_margin="10.0dip" android:background="@drawable/rc_item_bg" android:padding="2.0dip" android:scaleType="fitXY" /> <TextView android:text="斗破苍穹" android:id="@+id/sItemTitle" android:layout_width="fill_parent" android:layout_height="30.0dip" android:layout_alignTop="@+id/sItemIcon" android:layout_toRightOf="@+id/sItemIcon" android:gravity="center_vertical" android:singleLine="true" android:textColor="#ffffff" android:textSize="18.0sp" /> </RelativeLayout>
package cn.wangmeng.test; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.os.Bundle; import android.widget.ListView; public class AsyncListImage extends Activity { private ListView list; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); list=(ListView)findViewById(R.id.list); List<ImageAndText> dataArray=new ArrayList<ImageAndText>(); for (int i = 0; i < 100; i++) { ImageAndText test=new ImageAndText("http://www.wangmeng.cn/images/logo.gif", "test"); ImageAndText test1=new ImageAndText("http://www.pfwx.com/files/article/image/0/54/54s.jpg", "test1"); ImageAndText test2=new ImageAndText("http://www.pfwx.com/files/article/image/0/4/4s.jpg","test2"); ImageAndText test3=new ImageAndText("http://www.pfwx.com/files/article/image/9/9760/9760s.jpg","test3"); ImageAndText test4=new ImageAndText("http://www.pfwx.com/files/article/image/3/3382/3382s.jpg","test4"); ImageAndText test5=new ImageAndText("http://www.pfwx.com/files/article/image/3/3237/3237s.jpg","test5"); dataArray.add(test); dataArray.add(test1); dataArray.add(test2); dataArray.add(test3); dataArray.add(test4); dataArray.add(test5); } ImageAndTextListAdapter adapter=new ImageAndTextListAdapter(this, dataArray, list); list.setAdapter(adapter); } }
package cn.wangmeng.test; public class ImageAndText { private String imageUrl; private String text; public ImageAndText(String imageUrl, String text) { this.imageUrl = imageUrl; this.text = text; } public String getImageUrl() { return imageUrl; } public String getText() { return text; } }
上面两个代码一块讲 1、ImageAndText类是用来存储要与XML绑定的图片地址和名字地址的。
2、将所有的地址都放在一个List里面(dataArray),然后将其传入ImageAndTextListAdapter()函数中;可见这个ImageAndTextListAdapter()函数是根据传进去的dataArray生成对应的Adapter的
3、然后将ImageAndTextListAdapter()返回的Adapter与listView绑定
这是重写于baseAdapter的类,由于重写于baseAdapter,所以有几个必须重写的函数,getCount()、getItem()、getItemId()、getView(),我们先把总体代码写出来,只讲一个getView()函数,其实函数就不讲了,先着重说下getView()函数在什么时候被系统调用:
getView()函数在什么时候被系统调用:
注意一点是android系统在显示列表时,并不是把所有代表都显示出来,让你随便划,划到哪是哪;而是根据当前的在划到的ITEM,调用当前ITEM的getView()来显示它。
全部代码:
package cn.wangmeng.test; import java.util.ArrayList; import java.util.List; import cn.wangmeng.test.AsyncImageLoader.ImageCallback; import android.app.Activity; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; public class ImageAndTextListAdapter extends BaseAdapter { private LayoutInflater inflater; private ListView listView; private AsyncImageLoader asyncImageLoader; private List<ImageAndText> dataArray=new ArrayList<ImageAndText>(); public ImageAndTextListAdapter(Activity activity, List<ImageAndText> imageAndTexts, ListView listView) { this.listView = listView; asyncImageLoader = new AsyncImageLoader(); inflater = activity.getLayoutInflater(); dataArray=imageAndTexts; } @Override public int getCount() { // TODO Auto-generated method stub return dataArray.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub if(position >= getCount()){ return null; } return dataArray.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } //不需要ViewHolder版,直接将ImageAndText与XML资源关联起来 public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = inflater.inflate(R.layout.book_item_adapter, null); } convertView.setTag(position); ImageAndText imageAndText = (ImageAndText) getItem(position); String imageUrl = imageAndText.getImageUrl(); TextView textView = (TextView) convertView.findViewById(R.id.sItemTitle); // 将XML视图项与用户输入的URL和文本在绑定 textView.setText(imageAndText.getText());//加载TEXT ImageView iv = (ImageView) convertView.findViewById(R.id.sItemIcon); iv.setBackgroundResource(R.drawable.rc_item_bg);//在初始化时,先把背景图片设置成默认背景, //否则在下拉时会随机匹配背景,不美观 asyncImageLoader.loadDrawable(position,imageUrl, new ImageCallback() { @Override public void onImageLoad(Integer pos, Drawable drawable) { View view = listView.findViewWithTag(pos); if(view != null){ ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon); iv.setBackgroundDrawable(drawable); } } //加载不成功的图片处理 @Override public void onError(Integer pos) { View view = listView.findViewWithTag(pos); if(view != null){ ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon); iv.setBackgroundResource(R.drawable.rc_item_bg); } } }); return convertView; } }
我们着重看getView()函数 1、看这段:
if (convertView == null) { convertView = inflater.inflate(R.layout.book_item_adapter, null); } convertView.setTag(position); ImageAndText imageAndText = (ImageAndText) getItem(position); String imageUrl = imageAndText.getImageUrl(); TextView textView = (TextView) convertView.findViewById(R.id.sItemTitle); // 将XML视图项与用户输入的URL和文本在绑定 textView.setText(imageAndText.getText());//加载TEXT ImageView iv = (ImageView) convertView.findViewById(R.id.sItemIcon); iv.setBackgroundResource(R.drawable.rc_item_bg);
这段代码没什么特别的就是将前面dataArray的信息与XML的元素项对应起来,并绑定,最关键的是下面这段,下面这个方法才是实现异步加载图片的关键:
asyncImageLoader.loadDrawable(position,imageUrl, new ImageCallback() { @Override public void onImageLoad(Integer pos, Drawable drawable) { View view = listView.findViewWithTag(pos); if(view != null){ ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon); iv.setBackgroundDrawable(drawable); } } //加载不成功的图片处理 @Override public void onError(Integer pos) { View view = listView.findViewWithTag(pos); if(view != null){ ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon); iv.setBackgroundResource(R.drawable.rc_item_bg); } } });
这段代码的奇特之处在于,利用AsyncImageLoader类的实例(asyncImageLoader),调用方法loadDrawable()方法,就实现了加载后图像的绑定;好神奇,仔细看他是怎么做到的。这里先注意的有两点: (1)、传进去的参数,当前项的位置(position),当前图片的URL(imageUrl),一个名称为ImageCallback()接口函数;
(2)、ImageCallback()接口函数里的两个被重写的函数onImageLoad()和onError()
从上面的讲解也应该能猜到这个类,主要的功能就是加载图片,然后成功后更新UI;
先看全部代码,然后再逐步讲
package cn.wangmeng.test; import java.io.IOException; import java.io.InputStream; import java.lang.ref.SoftReference; import java.net.URL; import java.util.HashMap; import android.graphics.drawable.Drawable; import android.os.Handler; public class AsyncImageLoader { final Handler handler = new Handler(); private HashMap<String, SoftReference<Drawable>> imageCache; public AsyncImageLoader() { imageCache = new HashMap<String, SoftReference<Drawable>>();//图片缓存 } // 回调函数 public interface ImageCallback { public void onImageLoad(Integer t, Drawable drawable); public void onError(Integer t); } public Drawable loadDrawable(final Integer pos, final String imageUrl, final ImageCallback imageCallback) { new Thread() { @Override public void run() { LoadImg(pos, imageUrl, imageCallback); } }.start(); return null; }// loadDrawable---end public void LoadImg(final Integer pos, final String imageUrl, final ImageCallback imageCallback) { // 首先判断是否在缓存中 // 但有个问题是:ImageCache可能会越来越大,以至用户内存用光,所以要用SoftReference(弱引用)来实现 if (imageCache.containsKey(imageUrl)) { SoftReference<Drawable> softReference = imageCache.get(imageUrl); final Drawable drawable = softReference.get(); if (drawable != null) { handler.post(new Runnable() { @Override public void run() { imageCallback.onImageLoad(pos, drawable); } }); return; } } // 尝试从URL中加载 try { final Drawable drawable = loadImageFromUrl(imageUrl); if (drawable != null) { imageCache.put(imageUrl, new SoftReference<Drawable>(drawable)); } handler.post(new Runnable() { @Override public void run() { imageCallback.onImageLoad(pos, drawable); } }); } catch (IOException e) { handler.post(new Runnable() { @Override public void run() { imageCallback.onError(pos); } }); e.printStackTrace(); } } // 根据URL加载图片,如果出现错误throws IOException式的错误,以便在LoadImg中捕获,执行OnError()函数 public static Drawable loadImageFromUrl(String url) throws IOException { URL m; InputStream i = null; m = new URL(url); i = (InputStream) m.getContent(); Drawable d = Drawable.createFromStream(i, "src"); return d; } }
分别讲解 1、先看这段代码
// 回调函数 public interface ImageCallback { public void onImageLoad(Integer t, Drawable drawable); public void onError(Integer t); } public Drawable loadDrawable(final Integer pos, final String imageUrl, final ImageCallback imageCallback) { new Thread() { @Override public void run() { LoadImg(pos, imageUrl, imageCallback); } }.start(); return null; }// loadDrawable---end
(1)首先注意,刚才我们调用的loadDrawable()函数,里面初始化并运行了一个线程,而这个线程的里面只有一个函数LoadImg(),对于这个函数下面我们具体讲,它的主要功能就是加载图片,然后更新UI (2)上面也看出了ImageCallback是一个接口,而里面的两个函数onImageLoad()和onError()在这里是没有具体实现的,那在哪里实现呢,当然是我们上面的ImageAndTextListAdapter.java里面了,等下我们具体会再讲。
再往下看
// 根据URL加载图片,如果出现错误throws IOException式的错误,以便在LoadImg中捕获,执行OnError()函数 public static Drawable loadImageFromUrl(String url) throws IOException { URL m; InputStream i = null; m = new URL(url); i = (InputStream) m.getContent(); Drawable d = Drawable.createFromStream(i, "src"); return d; } public void LoadImg(final Integer pos, final String imageUrl, final ImageCallback imageCallback) { // 首先判断是否在缓存中 // 但有个问题是:ImageCache可能会越来越大,以至用户内存用光,所以要用SoftReference(弱引用)来实现 if (imageCache.containsKey(imageUrl)) { SoftReference<Drawable> softReference = imageCache.get(imageUrl); final Drawable drawable = softReference.get(); if (drawable != null) { handler.post(new Runnable() { @Override public void run() { imageCallback.onImageLoad(pos, drawable); } }); return; } } // 尝试从URL中加载 try { final Drawable drawable = loadImageFromUrl(imageUrl); if (drawable != null) { imageCache.put(imageUrl, new SoftReference<Drawable>(drawable)); } handler.post(new Runnable() { @Override public void run() { imageCallback.onImageLoad(pos, drawable); } }); } catch (IOException e) { handler.post(new Runnable() { @Override public void run() { imageCallback.onError(pos); } }); e.printStackTrace(); } }
(1)、loadImageFromUrl()函数,就是根据URL到网上加载图片,然后返回图片流Drawable类型变量 (2)、对于LoadImg()我们再拆一下,先看如何在缓存中加载
if (imageCache.containsKey(imageUrl)) { SoftReference<Drawable> softReference = imageCache.get(imageUrl); final Drawable drawable = softReference.get(); if (drawable != null) { handler.post(new Runnable() { @Override public void run() { imageCallback.onImageLoad(pos, drawable); } }); return; } }
注意:
1、在这里就已经得到了图像
SoftReference<Drawable> softReference = imageCache.get(imageUrl); final Drawable drawable = softReference.get();
2、得到图像之后就到了这段代码:
if (drawable != null) { handler.post(new Runnable() { @Override public void run() { imageCallback.onImageLoad(pos, drawable); } }); return; }
当我们得到图像之后,调用imageCallback.onImageLoad(pos, drawable);来更新UI,由于我们再回来看看ImageAndTextListAdapter.java中的代码
asyncImageLoader.loadDrawable(position,imageUrl, new ImageCallback() { @Override public void onImageLoad(Integer pos, Drawable drawable) { View view = listView.findViewWithTag(pos); if(view != null){ ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon); iv.setBackgroundDrawable(drawable); } } });
看到了吧,就是把得到的drawable与加载到UI中!!!!这就实现了回调
OK,就到这吧,OnError()的原理一样,只不过是对程序没有加载到图片时应该怎么处理,其实就是当没有加载到图片时就是默认图片代替。
下面给出源码:http://download.csdn.net/detail/harvic880925/6802241(不要分,仅供分享)
请大家尊重作者板权,转载请标明出处:http://blog.csdn.net/harvic880925/article/details/17766027 ,谢谢!