相信接触android,与listview打交道的频率应该很高,而很多时候使用listview会出现卡顿、当图片比较大也会遇到内存溢出问题,本章主要针对listview加载网络图片的优化做个简单的记录!
一、Adapter中复用convertView和使用ViewHolder
1、当convertView不为空时,不重复地调用inflate(“布局文件”, null);
减少内存消耗
2、使用ViewHolder类,通过convertView的setTag方法和getTag方法存储和获取,避免重复地调用findViewById减少重复创建视图时内存的消耗。
具体代码:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
viewHolder holder = null;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.img_item, null);
holder = new viewHolder();
holder.imgUrl = (ImageView) convertView.findViewById(R.id.img_url);
holder.tvName = (TextView) convertView.findViewById(R.id.tv_name);
convertView.setTag(holder);
} else {
holder = (viewHolder) convertView.getTag();
}
InfoBean infoBean = list.get(position);
LinearLayout.LayoutParams linearParams = (LinearLayout.LayoutParams) holder.imgUrl
.getLayoutParams();
linearParams.height = height;
linearParams.width = width;
holder.imgUrl.setLayoutParams(linearParams);
if(isInit){
ImageLoader.getInstance().displayImage(infoBean.getImgUrl(),
holder.imgUrl);
}
holder.tvName.setText(infoBean.getName());
holder.imgUrl.setTag(position);
return convertView;
}
class viewHolder {
ImageView imgUrl;
TextView tvName;
}
这里有一个问题就是针对ViewHolder是使用静态和非静态,说静态是保证holder = new ViewHolder();时不会重新分配内存空间,节省内存,然而我大多数的时候使用都是使用非静态的,所以我也不是很懂,下次测试看看结果有啥差距,也希望大家多多指教。
二、图片异步加载
这里用开源的框架Universal-Image-Loader,支持多种uri的图片加载,具体的介绍可以网上查查,
compile ‘com.squareup.picasso:picasso:2.5.2’
compile ‘com.facebook.fresco:fresco:0.9.0+’也是相关图片加载的
(1)xml中的权限配置
权限一定要记得配置,我是吃过这个的亏的,要不然图片可就加载不出来了
//可以读写SDCARD
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
//允许程序打开网络套接字
<uses-permission android:name="android.permission.INTERNET" />
(2)在MyApplication初始化ImageLoader
使用ImageLoader前一定要先初始化哦!不然程序会报错。而为什么要在Application中我觉得是因为ImageLoader是个单例,在程序开始时就初始化会更好,而后任何一个activity中去调用不用再考虑是否初始化过,也因为是单例所以重复初始化也没关系的啦!
建议可以写个工具类用来配置这些数据,在Application直接调用,这样Application看起来比较简洁,而且可以在工具按自己的要求配置相对应的DisplayImageOptions,这里我比较偷懒的在Application中直接写了配置,具体的可以去查看Universal-Image-Loader这个框架的介绍,这里就不多说了。
public class MyApplication extends Application {
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
initImageLoader();
}
private void initImageLoader() {
ImageLoader imageLoader = ImageLoader.getInstance();
if (!imageLoader.isInited()) {
// imageLoader.init(ImageLoaderConfiguration.createDefault(this));
DisplayImageOptions options = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.showStubImage(R.drawable.img_loading)
// 加载开始默认的图片
.showImageForEmptyUri(R.drawable.img_loading)
// url为空显示该图片,自己放在drawable里面的
.showImageOnFail(R.drawable.img_failure)
// 设置下载的图片是否缓存在SD卡中
.cacheOnDisk(true)
.considerExifParams(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.imageScaleType(ImageScaleType.IN_SAMPLE_INT).build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(
getApplicationContext())
.threadPoolSize(3)
// default
.threadPriority(Thread.NORM_PRIORITY - 2)
.denyCacheImageMultipleSizesInMemory()
.diskCacheFileNameGenerator(new Md5FileNameGenerator())
.tasksProcessingOrder(QueueProcessingType.LIFO)
.denyCacheImageMultipleSizesInMemory()
// .memoryCache(new LruMemoryCache((int) (6 * 1024 * 1024)))
.memoryCache(new WeakMemoryCache())
.memoryCacheSize((int) (2 * 1024 * 1024))
.memoryCacheSizePercentage(13)
// default
// .diskCache(new UnlimitedDiscCache(new
// File(FileUtils.SDPATH))
// default
.diskCacheSize(50 * 1024 * 1024)
.diskCacheFileCount(100)
.diskCacheFileNameGenerator(new HashCodeFileNameGenerator())
.defaultDisplayImageOptions(options).writeDebugLogs() // Remove
.build();
// Initialize ImageLoader with configuration.
ImageLoader.getInstance().init(config);
}
}
}
(3)具体使用
初始化好ImageLoader,现在就可以使用啦!项目中我用了其中一种加载方式,具体使用如下:
ImageLoader.getInstance().displayImage("url地址","显示的imageView");
ps: 其他的一些加载方式
1、displayImage(String uri, ImageView imageView, DisplayImageOptions options)
2、public void displayImage(String uri, ImageAware imageAware, ImageLoadingListener listener)
3、displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
ImageLoadingListener listener)
三、滑动停止加载图片
listview在快速滑动中边加载图片数据可能会导致listview卡顿,为了解决这一问题将listview设置当滑动停止后再去加载图片数据,但是这样做后会出现一个问题,就是当等到停止了去加载会给人一种一下子换了一张图片感觉,不知道这样的用户体验效果会不会变差。
//设置滚动监听,gvImg是GridView
gvImg.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
adapter.setInit(false);
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_IDLE:// 滑动停止
for (; start < end; start++) {
ImageView img = (ImageView) gvImg.findViewWithTag(start);
ImageLoader.getInstance().displayImage(list.get(start).getImgUrl(), img);
}
break;
default:
break;
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
// 设置当前屏幕显示的起始start和结束end
start = firstVisibleItem;
end = firstVisibleItem + visibleItemCount;
}
});
写一篇博客感觉好不容易,因为很多时候你实现了效果,但是很多原理却没有很了解,因此要查询更多的资料去了解,文章仅当作记录,有错误或者改进的地方还望多多指教