天天看點

ListView的“終極優化”,打造你的萬能擴充卡

本篇文章你會了解以下内容:

1、抽取ViewHolder,實作優化第一步

2、抽取公共Adapter,告别備援方法

3、控件直接指派,讓Adapter再次優化

執筆不易,覺得不錯,請轉發分享(更多Android文章,還請關注左側欄中我的公衆号)。

ListView不得不說是Android開發當中出現率相當高的一個控件,什麼資訊清單,聯系人清單,消息清單等等,無不有它的身影存在。既然項目中有太多的地方會用到它,傳統的代碼邏輯我們是怎麼使用的呢?每寫一個ListView,都要去寫一個Adapter類,一個ViewHolder類,這幾乎是我們必須要操作的,以緻于有太多太多的備援代碼,讓我們感到真的不厭其煩,一個兩個還可以,十個八個,就真的有點太崩潰了,不僅代碼繁瑣,還會占用記憶體,為了解決這樣的一個問題,下面就要開始對其抽取優化,代碼之間從上到下進行銜接,不要跳過看啊。

1、抽取ViewHolder,實作優化第一步

我們知道正常的ViewHolder,會在Adapter的getView方法裡去操作很多事情,建立執行個體,指派控件,setTag和getTag等,如下代碼所示,是我們最常用的模式,這裡我隻用了兩個控件,其實開發中有很多的View,對ViewHolder的優化,就是把這些代碼封裝進行抽取。

@Override

public View getView(int position, View convertView, ViewGroup parent) {

    View view;

    ViewHolder viewHolder;

    if (convertView == null) {

        view = View.inflate(mContext, R.layout.item_view, null);

        viewHolder = new ViewHolder();

        viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title);

        viewHolder.tvContent = (TextView) view.findViewById(R.id.tv_content);

        view.setTag(viewHolder);

    } else {

        view = convertView;

        viewHolder = (ViewHolder) view.getTag();

    }

viewHolder.tvTitle.setText(mDatas.get(position).getTitle());

viewHolder.tvContent.setText(mDatas.get(position).getContent());

    return view;

}

private static class ViewHolder {

    TextView tvTitle, tvContent;

}

自定義一個ViewHolder類,實作構造方法,既然要實作對以上的代碼進行一個抽取,那麼getView裡的參數,ViewHolder類大部分都需要用到,在ViewHolder類中,寫一個靜态方法,用于擷取ViewHolder,其判斷邏輯和以上代碼思路一緻,convertView若不存在就去建立ViewHolder,若存在就複用。

public static ViewHolder getViewHolder(Context context, int position,

                                     View convertView, ViewGroup parent, int layoutId) {

    if (convertView == null) {

        return new ViewHolder(context, position, parent, layoutId);

    } else {

        ViewHolder viewHolder = (ViewHolder) convertView.getTag();

        viewHolder.mPosition = position;

        return viewHolder;

    }

}

實作其構造方法,用于convertView不存在時進行對其執行個體:

private SparseArray<View> mView;

private int mPosition;

private View mConvertView;

public ViewHolder(Context context, int position, ViewGroup parent, int layoutId) {

    mPosition = position;

    mView = new SparseArray<View>();

    mConvertView = View.inflate(context, layoutId, null);

    mConvertView.setTag(this);

}

mView是用于存儲各個控件View,在這裡為什麼要用SparseArray而不用Map,我們可以去檢視下源碼,其實對于鍵為int類型,SparseArray要比map更加有效的使用記憶體。

mPosition是記錄索引位置。

mConverView就是建立的View對象,注意看上述代碼,它是在convertView為null的情況下,才去建立的。

寫一個方法,用于通過ViewHolder來擷取各個控件:

public <T extends View> T getView(int layoutId) {

    View view = mView.get(layoutId);

    if (view == null) {

        view = getmConvertView().findViewById(layoutId);

        mView.put(layoutId, view);

    }

    return (T) view;

}

再寫一個擷取layout的方法:

public View getmConvertView() {

    return mConvertView;

}

經過以上代碼,對于ViewHolder,我們基本上就抽取完成了,回過來,我們再看adapter裡的getView方法(注意,原來adapter裡的ViewHolder類可以對其删除),是不是一下整潔了許多。

@Override

public View getView(int position, View convertView, ViewGroup parent) {

    ViewHolder viewHolder=ViewHolder.getViewHoder(mContext,position,

            convertView,parent,R.layout.item_view);

    ((TextView)viewHolder.getView(R.id.tv_title)).

            setText(mDatas.get(position).getTitle());

    ((TextView)viewHolder.getView(R.id.tv_content)).

            setText(mDatas.get(position).getContent());

    return viewHolder.getmConvertView();

}

2、抽取公共Adapter,告别備援方法

ViewHolder抽取完之後,省去了太多的代碼,但是我們會發現Adapter裡除了getView方法,其它三個也是重複的,那麼接下來,我們就對Adapter進行抽取優化,建立一個萬能的Adapter類,繼承于BaseAdapter,記住要是抽象的,把getView方法設成抽象方法,這樣其它Adapter繼承這個萬能Adapter,隻需要重寫getView方法即可。

public abstract class UniversalAdapter<T> extends BaseAdapter {

    protected List<T> mDatas;

    protected Context context;

    private int layoutId;

    public UniversalAdapter(Context context, List<T> mDatas, int layoutId) {

        this.mDatas = mDatas;

        this.context = context;

        this.layoutId = layoutId;

    }

    @Override

    public int getCount() {

        return mDatas.size();

    }

    @Override

    public Object getItem(int position) {

        return mDatas.get(position);

    }

    @Override

    public long getItemId(int position) {

        return position;

    }

    @Override

    public abstract View getView(int position, View convertView, ViewGroup parent);

}

在上述代碼中,細心的同志的可能看到了,實作其構造方法時,我傳入了一個layoutId,其實就是一個XML資源,引入它,說白了,在getView方法裡我連 ViewHolder viewHolder=ViewHolder.getViewHolder(mContext,position,convertView,parent,R.layout.item_view);這段代碼我也不想去寫,怎麼辦呢?很簡單getView方法不去抽象化,而引出一個抽象的方法,具體可以改成以下方式:

@Override

public View getView(int position, View convertView, ViewGroup parent) {

    ViewHolder viewHoder = ViewHolder.getViewHolder(context, position, convertView, parent, layoutId);

    convert(viewHolder, mDatas.get(position));

    return viewHolder.getmConvertView();

}

protected abstract void convert(ViewHolder viewHolder, Object item);

以後我們所有的Adapter繼承這個萬能的Adapter之後,隻需要重寫convert這個方法就好了,Object item是一個Bean,擷取時要進行強轉。

public class MyAdapter extends CourAdapter {

    public MyAdapter(List<Bean> data, Context context, int layoutId) {

        super(context, data, layoutId);

    }

    @Override

    protected void convert(ViewHoder viewHoder, Object item) {

        ((TextView)viewHolder.getView(R.id.tv_title)).

                setText(((Bean)item).getTitle());

        ((TextView)viewHolder.getView(R.id.tv_content)).

                setText(((Bean)item).getContent());

    }

}

看看以上的代碼,是不是就簡單了很多呢。

3、控件直接指派,讓Adapter再次優化

經過以上的優化之後,代碼大大的減少了,但是,我還想進行對其優化,也就是說,我連setText都不想去那麼麻煩,這樣的話,我們就是可以在ViewHolder這個類中去寫控件指派方法:

//TextView設定資料

public ViewHolder setText(int viewId, String txt) {

    TextView mTextView = getView(viewId);

    mTextView.setText(txt);

    return this;

}

再看convert方法,是不是又一下簡單的很多。

@Override

protected void convert(ViewHolder viewHolder, Object item) {

    viewHolder.setText(R.id.tv_title,((Bean)item).getTitle())

            .setText(R.id.tv_content,((Bean)item).getContent());

}

當然了對于其它控件你也可以這樣去做,比如ImageView:

public ViewHolder setPic(int viewId, String url) {

    ImageView mImageView = getView(viewId);

    //ImageLoader.getInstance().loadImage(url,mImageView);

    return this;

}

好了同志們,以上就是今天分享的所有内容,更多Android文章還請關注左側欄中我的微信公衆号,好累啊,拜拜了。