Android開發技巧——BaseAdapter的另外一種優雅封裝

RecyclerView雖然因其靈活性、高效性等特色而備受好評,但也不是必定得用它把ListView給替代掉。在某些場景中,ListView仍是相對更適合的。好比數據量不大,不頻繁更新,而且須要簡單地設置一下divider或header、footer的時候,相對於RecyclerView的繁瑣,ListView在實現上則表現得更方便和簡潔。java

過去的封裝

在使用ListView的過程當中,爲了複用ListView中的convertView以及優化getView()時的findViewById操做,咱們一般會引入一個ViewHolder類,來持有itemView的子view。可是不便的是,咱們須要爲每一種顯示不一樣數據的ListView都重寫其BaseAdapter的getView(),因而就有了對其的一種封裝:
建立一個一般的ViewHolder,裏面用SparseArray<View>來緩存,以下:android

public class ViewHolder {
    private final View itemView;
    private SparseArray<View> mHolderViews;

    public ViewHolder(View view) {
        itemView = view;
        view.setTag(this);
        mHolderViews = new SparseArray<>();
    }

    public void hold(int... resIds) {
        for (int id : resIds) {
            mHolderViews.put(id, itemView.findViewById(id));
        }
    }

    public <V> V get(int id) {
        return (V) mHolderViews.get(id);
    }
}

再經過重寫BaseAdapter的getView方法,做以下封裝:git

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final ViewHolder holder;
    if (convertView == null) {
        holder = createHolder(position, parent);
        convertView = holder.itemView;
    } else {
        holder = (ViewHolder) convertView.getTag();
    }
    bindData(position, holder, getItem(position));
    return convertView;
}

public abstract ViewHolder createHolder(int position, ViewGroup parent);

public abstract void bindData(int position, ViewHolder holder, T data);

這樣使用的時候只須要繼承並實現兩個抽象方法便可。github

這樣寫法,在後來我以爲仍是有些彆扭的地方。一是使用的時候仍是須要去繼承咱們封裝過的Adapter;二是在bindData()方法中,須要對每個咱們要設置的控件調用holder.get(id)方法,才能開始賦值或進行其餘的設置,當咱們的item的控件較多時,這些調用會使bindData()顯得臃腫而不夠純粹,因而我又對其進行了另外一種封裝。緩存

另外一種優雅封裝

這裏我主要是經過接口解決重寫Adapter的問題,而後再借鑑RecyclerView.ViewHolder,封裝結果以下:markdown

假設咱們重寫BaseAdapter的類爲BaseListAdapter,那麼首先定義它的一個靜態內部類:ide

public static abstract class ViewHolder {
        public final View itemView;

        public ViewHolder(View itemView) {
            this.itemView = itemView;
            itemView.setTag(this);
        }
    }

而後咱們定義一個接口,用於建立ViewHolder以及綁定數據,以下:優化

package com.githang.android.snippet.demo.adapter.adapter;

import android.view.ViewGroup;

public interface ViewCreator<T, H extends BaseListAdapter.ViewHolder> {

    H createHolder(int position, ViewGroup parent);

    /** * 設置列表裏的視圖內容 * * @param position 在列表中的位置 * @param holder 該位置對應的視圖 */
    void bindData(int position, H holder, T data);
}

這裏定義了兩個泛型 ,一個是T,表示咱們Adapter裏的數據對象,另外一個是H,爲咱們的ViewHolder的子類,表示咱們最終建立出來的ViewHolder。this

接下來,修改咱們的BaseListAdapter,以下:spa

public class BaseListAdapter<T, H extends BaseListAdapter.ViewHolder> extends BaseAdapter {
    private final List<T> mData;
    private final ViewCreator<T, H> mViewCreator;

    public BaseListAdapter(ViewCreator<T, H > creator) {
        this(new ArrayList<T>(), creator);
    }

    public BaseListAdapter(List<T> data, ViewCreator<T, H> creator) {
        mData = data == null ? new ArrayList<T>() : data;
        mViewCreator = creator;
    }

    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public T getItem(int position) {
        return mData.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final H holder;
        if (convertView == null) {
            holder = mViewCreator.createHolder(position, parent);
            convertView = holder.itemView;
        } else {
            holder = (H) convertView.getTag();
        }
        mViewCreator.bindData(position, holder, getItem(position));
        return convertView;
    }

    public void update(List<T> data) {
        mData.clear();
        addData(data);
    }

    public void addData(List<T> data) {
        if (data != null) {
            mData.addAll(data);
        }
        notifyDataSetChanged();
        }

    public static abstract class ViewHolder {
        public final View itemView;

        public ViewHolder(View itemView) {
            this.itemView = itemView;
            itemView.setTag(this);
        }
    }
}

在這個BaseListAdapter裏,一樣定義了兩個泛型,與咱們的ViewCreator相同。咱們建立ViewHolder及綁定數據的操做,經過調用該接口來執行(見getView方法),而該接口實例則經過構造方法傳入。
這樣,咱們在使用的時候就能夠先像使用RecyclerView同樣,定義一個實現咱們ViewHolder的子類,而後使咱們的Fragment或Activity實現這個ViewCreator接口就能夠了。
最後,咱們還能夠繼承ViewHolder,封裝一個通用的ViewHolder,代碼以下:

public static class DefaultViewHolder extends ViewHolder {
        private SparseArray<View> mHolderViews;

        public DefaultViewHolder(View view) {
            super(view);
            mHolderViews = new SparseArray<>();
        }

        public void hold(int... resIds) {
            for (int id : resIds) {
                mHolderViews.put(id, itemView.findViewById(id));
            }
        }

        public <V> V get(int id) {
            return (V) mHolderViews.get(id);
        }
    }

接下來是使用示例:

public class SampleFragment extends Fragment implements ViewCreator<User,SampleFragment.UserViewHolder> {

    private BaseListAdapter<User, UserViewHolder> mAdapter = new BaseListAdapter<User, UserViewHolder>(this);

    @Override
    public UserViewHolder createHolder(int position, ViewGroup parent) {
        return new UserViewHolder(LayoutInflater.from(getActivity()).inflate(R.layout.item_user, parent, false));
    }

    @Override
    public void bindData(int position, UserViewHolder holder, User user) {
        holder.name.setText(user.name);
        holder.email.setText(user.email);
    }

    static class UserViewHolder extends BaseListAdapter.ViewHolder {
        public final TextView name;
        public final TextView email;

        public UserViewHolder(View itemView) {
            super(itemView);
            name = (TextView) itemView.findViewById(R.id.name);
            email = (TextView) itemView.findViewById(R.id.email);
        }
    }
}

所有的封裝代碼加上註釋等也不過一百來行,可是能夠看到,在這樣的封裝下,咱們的代碼已經變得很清晰。

本文的代碼見:http://download.csdn.net/detail/maosidiaoxian/9700700
更詳細的封裝及相關代碼見個人開源項目:https://github.com/msdx/AndroidSnippet/tree/master/library/src/main/java/com/githang/android/snippet/adapter

相關文章
相關標籤/搜索