Android簡便通用的SimpleBaseAdapter

yangxi_001發表於2015-10-05

在Android開發中經常用到ListView、GridView等列表,於是Adapter也就比較常用的了, 而實際專案中稍微複雜點的基本都會用到BaseAdapter, 大家都知道繼承自BaseAdapter必須要重寫getCount(), getItem(), getItemId(), getView()這幾個方法, 而且可能為了優化列表的載入還還會經常採用ViewHoder模式, 試想列表多了估計都寫煩了吧,那麼今天就來教大家一種通用、簡潔的Adapter,以後你就可以幾行程式碼搞定一個複雜的Adapter了。

通用的SimpleBaseAdapter

首先我們來解決每次都重寫BaseAdapter的那幾個方法的問題,解決方案很簡單,直接寫一個抽象的SimpleBaseAdapter,程式碼如下

public abstract class SimpleBaseAdapter<T> extends BaseAdapter {

    protected Context context;
    protected List<T> data;

    public SimpleBaseAdapter(Context context, List<T> data) {
        this.context = context;
        this.data = data == null ? new ArrayList<T>() : data;
    }

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

    @Override
    public Object getItem(int position) {
        if (position >= data.size())
            return null;
        return data.get(position);
    }

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

上面程式碼我不就過多解釋了,相信大家都能看懂。之後建立Adapter的時候只需要繼承自SimpleBaseAdapter,把List資料來源傳進去,再重寫getView方法就好了。

通用的ViewHolder

上面的SimpleBaseAdapter已經大大減少了不少的重複性工作,但是對於ViewHolder的優化依然還有很多重複性的程式碼要寫,那麼下面就來看看如何打造一個通用的ViewHolder。

關於ListView的優化這裡再多說下吧,主要是兩點:一是判斷convertView為空的時候才會inflate,複用item view,二便是使用ViewHoder模式,通過convertView.setTag與convertView進行繫結,然後當convertView複用時,直接從與之對於的ViewHolder(getTag)中拿到convertView佈局中的控制元件,省去了findViewById的時間~

下面看我們的ViewHolder類:

public class ViewHolder {
    private SparseArray<View> views = new SparseArray<View>();
    private View convertView;

    public ViewHolder(View convertView) {
        this.convertView = convertView;
    }

    public <T extends View> T getView(int resId) {
        View v = views.get(resId);
        if (null == v) {
            v = convertView.findViewById(resId);
            views.put(resId, v);
        }
        return (T) v;
    }
}

可以看到這裡定義的ViewHolder程式碼很簡單,使用起來也是非常的輕便。SparseArray在程式碼理解上等價於HashMap<Integer, View>, SparseArray是Android提供的一個資料結構,但是比Map的效率更高。

最終的SimpleBaseAdapter

廢話不多說了,我們結合以上兩點,直接上個最終的SimpleBaseAdapter

public abstract class SimpleBaseAdapter<T> extends BaseAdapter {

    protected Context context;
    protected List<T> data;

    public SimpleBaseAdapter(Context context, List<T> data) {
        this.context = context;
        this.data = data == null ? new ArrayList<T>() : new ArrayList<T>(data);
    }

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

    @Override
    public Object getItem(int position) {
        if (position >= data.size())
            return null;
        return data.get(position);
    }

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

    /**
     * 該方法需要子類實現,需要返回item佈局的resource id
     * 
     * @return
     */
    public abstract int getItemResource();

    /**
     * 使用該getItemView方法替換原來的getView方法,需要子類實現
     * 
     * @param position
     * @param convertView
     * @param parent
     * @param holder
     * @return
     */
    public abstract View getItemView(int position, View convertView, ViewHolder holder);

    @SuppressWarnings("unchecked")
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (null == convertView) {
            convertView = View.inflate(context, getItemResource(), null);
            holder = new ViewHolder(convertView);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        return getItemView(position, convertView, holder);
    }

    public class ViewHolder {
        private SparseArray<View> views = new SparseArray<View>();
        private View convertView;
		
        public ViewHolder(View convertView) {
            this.convertView = convertView;
        }

        @SuppressWarnings("unchecked")
        public <T extends View> T getView(int resId) {
            View v = views.get(resId);
            if (null == v) {
                v = convertView.findViewById(resId);
                views.put(resId, v);
            }
            return (T) v;
        }
    }

    public void addAll(List<T> elem) {
        data.addAll(elem);
        notifyDataSetChanged();
    }
	
    public void remove(T elem) {
        data.remove(elem);
        notifyDataSetChanged();
    }

    public void remove(int index) {
        data.remove(index);
        notifyDataSetChanged();
    }

    public void replaceAll(List<T> elem) {
        data.clear();
        data.addAll(elem);
        notifyDataSetChanged();
    }
}

使用

自己的Adapter需要繼承SimpleBaseAdapter,並且實現getItemResource和getItemView兩個方法就好了, 下面看下例子:

public class TestFoodListAdapter extends SimpleBaseAdapter<String> {

    public TestFoodListAdapter(Context context, List<String> data) {
        super(context, data);
    }

    @Override
    public int getItemResource() {
        return R.layout.listitem_test;
    }

    @Override
    public View getItemView(int position, View convertView, ViewHolder holder) {
        TextView text = holder.getView(R.id.text);
        text.setText(getItem(position));
        return convertView;
    }
}

相信大家都能看懂,具體的xml佈局檔案就不寫出來了,上面的示例和google推薦的寫法類似,只不過稍加變通之後減少了很多重複的程式碼, 是不是清爽了很多呢?

相關文章