ListView中每一项的Item可能包含一个复杂的内容,而用户在使用时既要加载数据又要滑动ListView,有时会造成ListView的卡顿现象,因此需要进行完善。
考虑到的解决这一问题的思想是:使ListView滑动过程中不加载任何异步任务,停止后再加载可见项。
----数据适配器实现整个ListView的滑动事件监听器接口,并实现其中的方法。整体思想:判断当前ListView的滚动状态,如果处于滚动过程中则停止加载可见项,停止滚动后再加载可见项。
----onScroll方法中有两个形参:int firstVisibleItem第一个可见元素,int visibleItemCount可见元素长度。因此在适配器类中声明两个int类型的成员变量mStart,mEnd记录开始和终止的可见元素;声明一个String数组记录url地址,通过mStart和mEnd决定加载String数组中哪一段的数据(onScroll方法的作用就在于不断获取当前的第一个和最后一个可见项);在构造方法中初始化该string类型数组,以for循环将图片的url传入静态数组
public NewsAdapter(Context context,List<NewsBean> data){
mList=data;
mInflater=LayoutInflater.from(context);
mImageLoader=new ImageLoader();
URLS=new String[data.size()];
for (int i=0;i<data.size();i++){
URLS[i]=data.get(i).newsIconURL;//图片的url转入静态数组
}
}
----ImageLoader.java文件中添加一个加载从mStart到mEnd的url数据的方法LoadImages,仿照从AsyncTask中读取url的步骤,加载从start到end的所有图片:
public void loadImages(int start,int end){
for(int i=start;i<end;i++){
String url=NewsAdapter.URLS[i];
Bitmap bitmap=getBitmapFromCache(url);
if(bitmap==null) {
NewsAsyncTask task=new NewsAsyncTask(url);
task.execute(url);
mTask.add(task);
}else{
ImageView imageView= (ImageView) mListView.findViewWithTag(url);
imageView.setImageBitmap(bitmap);
}
}
}
但此时不是再向整个ImageView中读取url,而是向指定的起始和终止位置之间读取数据,因此NewsAsyncTask(imageView, url).execute(url);以及imageView.setImageBitmap(bitmap);语句中向imageView传递url是不合适的,用ListView就能找到ImageView。需要给ImageLoader声明新的成员变量private ListView mListView;和private Set<NewsAsyncTask> mTask;并在构造方法中进行初始化:mListView=listview; mTask=new HashSet<>();;在onPostExecute方法中通过找到的ImageView去设置bitmap;设置完bitmap之后代表这个Task已经结束了,需要在集合中remove掉这个AsyncTask。
class NewsAsyncTask extends AsyncTask<String,Void,Bitmap>{
// private ImageView mImageView;
private String mUrl;
public NewsAsyncTask(String url){
/*mImageView = imageView;*/
mUrl=url;
}
@Override
protected Bitmap doInBackground(String... params) {
Bitmap bitmap=getBitmapFromUrl(params[0]);
if (bitmap!=null){
addBitmapToCache(params[0], bitmap);
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {//将Bitmap传递给ImageView
super.onPostExecute(bitmap);
ImageView imageView= (ImageView) mListView.findViewWithTag(mUrl);
if (imageView!=null&&bitmap!=null){
imageView.setImageBitmap(bitmap);
}
mTask.remove(this);
}
}
----**不要忘记注册接口——注册对应的事件:listView.setOnScrollListener(this);
以上,实现了将显示图片的控制权从getView转换到了滑动监听事件。但是发现初次启动程序时图片是不加载的,想到的原因在于,在第一次启动ListView的时候是默认ListView的状态没有发生改变的,而onScrollStateChanged方法在初次运行时不会自动调用。要进行完善。解决问题的思路:虽然onScrollStateChanged方法在初次运行时不会自动调用,,但是onScroll方法会调用,因此在适配器类中增加一个成员变量Flag来判断是否是第一次启动,并在初始化时赋值为true——private boolean mFirstIn; mFirstIn=true;;在onScroll方法中增加判断语句判断是否是第一次加载且屏幕上的item大于0,若是则将start和end传递给loadImages()方法,人为的显示第一屏的数据;之后将flag设置为false,此后不会再走这一步的逻辑,通过onScrollStateChanged方法来加载图片
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//firstVisibleItem第一个可见元素,visibleItemCount可见元素长度
mStart=firstVisibleItem;
mEnd=firstVisibleItem+visibleItemCount;
if (mFirstIn&&visibleItemCount>0){
mImageLoader.loadImages(mStart,mEnd);
mFirstIn=false;
}
}
以上完成了对ListView加载图片执行效率的全部优化。
总结:此例完成的是一级缓存——将加载内容缓存到内存,其余的还有将内容缓存到硬盘等,可以使用DiskLruCache这个第三方的类来实现“二级缓存”。异步加载不仅是获取网络资源,可以将任何耗时操作都看成异步加载,所有通过耗时操作获取的结果都可以通过缓存来提高效率。但是Cache也存在缺点——不能保证数据使实时的,所以在realtime需求高的应用中不能使用缓存。而对于本地资源,很少用到缓存。