天天看點

Android ListView性能優化,異步加載圖檔ListView性能優化+異步加載圖檔

版權聲明:本文為部落客原創文章,轉載請标明出處。 https://blog.csdn.net/lyhhj/article/details/48184383

ListView性能優化+異步加載圖檔

一、緒論

相信大家在Android開發中肯定會經常用到ListView吧,那麼怎麼優化ListView呢?尤其是每個item裡面中還有圖檔需要加載,資料源比較多,如果處理不好的話,在滑動ListView的過程中肯定會遇到卡頓的現象,做了這麼久的開發,自己多少也摸索到了一些規律,接下來就給大家詳細的介紹一下ListView的性能優化。

二、思路:

1.利用ViewHolder

我們自己定義一個ViewHolder,存放我們item中的元件,減少不必要的findViewById(),把我們的控件引用存在裡面,利用view.setTag()存放在view裡面,下次就可以直接取了。說一下setTag()是幹什麼用的吧,就是給View一個标簽,下次getView()的時候我們可以根據标簽擷取這個View,如果标簽存在了,就不需要再建立了,也就不再需要重新初始化那些元件了。

2.異步加載圖檔

我們在ListView中異步加載圖檔,可以使用一些第三方API來加載圖檔,比較好用的是ImageLoader,Xutils裡面的BitmapUtils也可以。

3.設定LitView滑動時禁止加載圖檔

如果我們在滑動ListView的時候也要加載圖檔,那麼滑動時肯定會卡頓

三、詳解:

1.定義ListView

我們先看一下布局,item的每一項内容

<span style="font-size:14px;"><span style="font-size:14px;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="horizontal"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:paddingBottom="@dimen/middler_space"
              android:background="#fff"
              android:id="@+id/llImage"
        >
        <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <ImageView
                        android:id="@+id/image"
                        android:layout_width="40dp"
                        android:layout_height="40dp"
                        android:background="#cccccc"
                        android:layout_marginLeft="15dp"
                        android:adjustViewBounds="true"
                        android:scaleType="fitXY"
                        android:focusable="false"
                        android:layout_marginTop="@dimen/middler_space"
                        />
                <ImageView
                        android:layout_marginTop="@dimen/middler_space"
                        android:id="@+id/iv_filelist_vedio"
                        android:src="@drawable/luxiangji"
                        android:layout_gravity="left"
                        android:visibility="gone"
                        android:layout_marginLeft="15dp"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"/>
        </RelativeLayout>

        <LinearLayout
                android:layout_marginTop="@dimen/middler_space"
                android:layout_weight="1"
                android:orientation="vertical"
                android:layout_marginRight="10dp"
                android:layout_width="0dp"
                android:layout_height="wrap_content">
            <TextView
                    android:id="@+id/txtText"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="title"
                    android:textSize="15dp"
                    android:singleLine="true"
                    android:textColor="#000"
                    android:layout_marginLeft="10dp"
                    />
            <LinearLayout
                    android:orientation="horizontal"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content">
                <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="content"
                        android:textSize="11dp"
                        android:layout_marginLeft="10dp"
                        android:layout_marginTop="7dp"
                        android:textColor="@color/deep_gray"/>
                
            </LinearLayout>

        </LinearLayout>

</LinearLayout></span></span>           

很簡單就是一個titile,一個content,一張圖檔

2.然後我們自定義Adapter

<span style="font-size:14px;"><span style="font-size:14px;">package com.yzx.youneed.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.yzx.youneed.R;
import com.yzx.youneed.model.AppItem_file;

import java.util.List;

/**
 * Created by Hankkin on 2015/1/30.
 * imagelist類型擴充卡
 */
public class AppImageListAdapter extends BaseAdapter {
    private Context context;
    private List<AppItem_file> data;
    private LayoutInflater inflater;
    public AppImageListAdapter(Context context,List<AppItem_file> data){
        this.data = data;
        this.context = context;
        inflater = LayoutInflater.from(context);
    }
    @Override
    public int getCount() {
        return data.size();
    }
    /**
     * 修改listview首項
     * by Hankkin at:2015-2-15
     * @param i
     * @return
     */
    @Override
    public AppItem_file getItem(int i) {
        if (i<0){
            return null;
        }
        return data.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    
    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ViewHolder holder = null;
        if (view == null){
            holder= new ViewHolder();
            view = inflater.inflate(R.layout.listview_imagelist,null);
            holder.txtTitle = (TextView) view.findViewById(R.id.txtText);
            holder.image = (ImageView) view.findViewById(R.id.image);
            holder.txtContent = (TextView) view.findViewById(R.id.txtSC);
            view.setTag(holder);
        }else {
            holder = (ViewHolder) view.getTag();
            holder.clean();
        }

        AppItem_file af = data.get(i);
        if (i % 2 == 1) {
            view.setBackgroundResource(R.drawable.list_gray_item1);
        } else {
            view.setBackgroundResource(R.drawable.list_gray_item2);
        }
        holder.txtTitle.setText(af.getTitle());
        holder.txtContent.setText(af.getCreate_time());
        ImageLoader.getInstance().displayImage(af.getThumbnail(),holder.image);
        return view;
    }

    public final class ViewHolder{
        public TextView txtTitle,txtContent;
        public ImageView image;
        void clean(){
            txtTitle.setText(null);
            txtContent.setText(null);
            image.setImageBitmap(null);
        }
    }

}</span></span>
           

我們可以看見我們自己定義的ViewHolder,裡面存放着item裡面的三個屬性,在getView的時候如果view為空,我們建立一個view,如果不為空,我們的holder=view.getTag();大家可能注意到還有一個clean()方法,這個方法是為了防止我們每一個item的資料會重複,然後我們每加載一個item之後都把上一個的item的資料源置空。

加載圖檔我們用的是

<span style="font-size:14px;"><span style="font-size:18px;">ImageLoader.getInstance().displayImage(af.getThumbnail(),holder.image);</span></span>           

這句話也就是ImageLoader的異步加載圖檔,隻需要傳進去兩個參數,第一個是圖檔url,第二個是ImageView控件,ImageLoader會自動給我們緩存圖檔的,如果之前加載過了是不會再次下載下傳圖檔,直接加載本地緩存好的圖檔。至于ImageLoader的一些配置資訊,包括預設圖檔,緩存位址等會在下面介紹。

3.接下來我們看一下怎麼在我們的Activity中通路伺服器擷取資訊

因為項目裡面用的是Xutils的HttpUtils,是以通路伺服器我就用它了,隻是給大家舉個例子

<span style="font-size:14px;"><span style="font-size:14px;">public void asyncQF(boolean url1) {
        RequestParams params = new RequestParams();
        params.addBodyParameter("flag",file_group.getFlag());
        HttpUtils http = new HttpUtils();
        http.send(HttpRequest.HttpMethod.POST, "", new RequestCallBack<String>() {
            @Override
            public void onSuccess(ResponseInfo<String> responseInfo) {
                //ToDo:
                //添加資料源,更新擴充卡
            }

            @Override
            public void onFailure(HttpException error, String msg) {
                //ToDo:
                //更新UI
            }
        });
    }</span></span>           

伺服器擷取資料之後添加到List中更新我們的擴充卡就ok了,這時候我們的ListView就已經有資料了。

最後我們還可以設定ListView滑動時不加載圖檔

4.設定滑動不加載圖檔

ImageLoader已經給我們封裝好了方法,我們隻需要設定一下ListView滑動監聽就可以了,看一下代碼:

<span style="font-size:14px;"><span style="font-size:18px;">        lv_appImageList.setOnScrollListener(new PauseOnScrollListener(ImageLoader.getInstance(),false,true));</span>
</span>           

隻需要簡單的這麼一句話就搞定,是不是很友善?

四、ImageLoader配置

<span style="font-size:14px;"><span style="font-size:14px;">public static void initImageLoader(Context context) {
        personOptions = new DisplayImageOptions.Builder()
                .showImageOnFail(R.drawable.usericon)
                .showImageForEmptyUri(R.drawable.usericon)
                .resetViewBeforeLoading(false)  // default
                .delayBeforeLoading(0)
                .cacheInMemory(true) // default
                .cacheOnDisk(true) // default
                .considerExifParams(true) // default
                .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default
                .bitmapConfig(Bitmap.Config.ARGB_8888) // default
                .displayer(new SimpleBitmapDisplayer()) // default
                .handler(new Handler()) // default
                .build();

        DisplayImageOptions options = new DisplayImageOptions.Builder()
                .showImageOnLoading(R.drawable.download)
//                .showImageOnFail(R.drawable.img_fail)
                .resetViewBeforeLoading(false)  // default
                .delayBeforeLoading(0)
                .cacheInMemory(true) // default
                .cacheOnDisk(true) // default
                .considerExifParams(true) // default
                .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default
                .bitmapConfig(Bitmap.Config.ARGB_8888) // default
                .displayer(new SimpleBitmapDisplayer()) // default
                .handler(new Handler()) // default
                .build();

        // This configuration tuning is custom. You can tune every option, you may tune some of them,
        // or you can create default configuration by
        // ImageLoaderConfiguration.createDefault(this);
        // method.
//        File cacheDir = StorageUtils.getCacheDirectory(context);
        File picPath=new File(Environment.getExternalStorageDirectory().getPath()+File.separator+"yourneed"+File.separator+"files");
        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
                .memoryCacheExtraOptions(480, 800) // default = device screen dimensions
                .diskCacheExtraOptions(480, 800, null)
                .threadPoolSize(3) // default
                .threadPriority(Thread.NORM_PRIORITY - 1) // default
                .tasksProcessingOrder(QueueProcessingType.FIFO) // default
                .denyCacheImageMultipleSizesInMemory()
                .memoryCache(new LruMemoryCache(2 * 1024 * 1024))
                .memoryCacheSize(2 * 1024 * 1024)
                .memoryCacheSizePercentage(13) // default
                .diskCache(new UnlimitedDiscCache(picPath)) // default
                .diskCacheSize(50 * 1024 * 1024)
                .diskCacheFileCount(1000)
                .diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default
                .imageDownloader(new BaseImageDownloader(context)) // default
                .imageDecoder(new BaseImageDecoder(true)) // default
                .defaultDisplayImageOptions(options) // default
                .writeDebugLogs()
                .build();
        // Initialize ImageLoader with configuration.
        ImageLoader.getInstance().init(config);
    }</span></span>           

這裡面有加載圖檔失敗的設定,預設圖檔的設定,延遲加載設定,以及緩存陌路設定等等,這裡就不詳細介紹了,一般這個方法基本的配置都有了,大家可以直接拷到你的項目中直接用,可以在application裡面初始化Imageloader。如果大家想要詳細的了解Imageloader架構的話可以看看

Android 開源架構Universal-Image-Loader完全解析(二)--- 圖檔緩存政策詳解 Android 開源架構Universal-Image-Loader完全解析(三)---源代碼解讀

這兩篇部落格,還是比較不錯的。

至于圖檔就不給大家貼了,隻要按照上面的方法做,你的ListView相信不會卡頓的。

繼續閱讀