Android之ListView优化

相信接触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)
2public 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;  
            }
        });

写一篇博客感觉好不容易,因为很多时候你实现了效果,但是很多原理却没有很了解,因此要查询更多的资料去了解,文章仅当作记录,有错误或者改进的地方还望多多指教

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章