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;  
            }
        });

寫一篇博客感覺好不容易,因爲很多時候你實現了效果,但是很多原理卻沒有很瞭解,因此要查詢更多的資料去了解,文章僅當作記錄,有錯誤或者改進的地方還望多多指教

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