天天看點

封裝一個簡單的RecyclerView.Adapter對Adapter封裝進行需求分析Adapter簡單實作

Google推出控件RecyclerView推出以後。ListView逐漸被RecyclerView取代。

那麼RecyclerView 和ListView相比有哪些優點呢?

  1. RecylerView封裝了viewholder的回收複用,也就是說RecylerView标準化了ViewHolder,編寫Adapter面向的是ViewHolder而不再是View了,複用的邏輯被封裝了,寫起來更加簡單。
  2. 高度的解耦,擴充性強,針對一個Item的顯示RecylerView專門抽取出了相應的類,來控制Item的顯示,使其的擴充性非常強。
  3. 可以控制Item增删的動畫,可以通過ItemAnimator這個類進行控制,當然針對增删的動畫,RecylerView有其自己預設的實作。
前面講到RecylerView封裝了viewholder的回收複用,在我們使用RecyclerView就會用到RecyclerView.Adapter,而每建立一個adapter就要建立一個RecyclerView.ViewHolder類,實際開發中由于會用到不止一個RecyclerView,不斷重複以上步奏使得代碼臃腫,實作起來非常費勁。大大的增加了程式員的代碼工作量。這顯然不是我們想要的。

對Adapter封裝進行需求分析

繼承RecyclerView.BaseAdapter需要實作3個抽象方法:
    1. onCreateViewHolder   
    2. onBindViewHolder   
    3. getItemCount
需要一個RecyclerView.ViewHolder的實作類       

    其中getItemCount()可以構造方法傳入資料List,直接實作。

    而onCreateViewHolder則需要傳回一個ViewHolder的對象,而建立ViewHolder對象需要一個子條目的根View。考慮到一個Adapter可能有多個activity用到,而布局可能不同,是以我們在構造方法中傳入layoutId用來建立view。
           

Adapter簡單實作

public class SimpleQuickAdapter<B> extends RecyclerView.Adapter<SimpleQuickAdapter.SimpleViewHolder> {
    private int layoutId;
    protected List<B> mData;

    /**
     * @param layoutId 子條目的布局
     * @param list      資料
     */
    public SimpleQuickAdapter(@LayoutRes int layoutId, List<B> list) {
        this.layoutId = layoutId;
        this.mData = list != null ? list : new ArrayList<B>();
    }


    @Override
    public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(SimpleViewHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return mData.size();
    }
}
public class SimpleViewHolder extends RecyclerView.ViewHolder{
    public SimpleViewHolder(View itemView) {
        super(itemView);
    }
}
           

我們先建立一個SimpleViewHolder類繼承RecyclerView.ViewHolder作為Adapter的泛型,因為資料的類型是不确定的,我們這裡也設計為泛型。

構造方法傳入layoutId後,我們就可以建立ViewHolder,然後實作onCreateViewHolder方法,考慮到Context參數經常在綁定資料的時候用到,而view又能拿到context,是以我們在實作onCreateViewHolder方法的時候順便拿到Context對象

protected Context mContext;
    @Override
    public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        mContext = parent.getContext();
        LayoutInflater layoutInflater = LayoutInflater.from(mContext);
        View view = layoutInflater.inflate(layoutId, parent, false);
        SimpleViewHolder holder = new SimpleViewHolder(view);
        return holder;
    }
           

在onBindViewHolder方法中,我們可以進行資料綁定,事件監聽。

資料綁定顯然要交給子類去處理,我們把類改為抽象類,然後定義抽象方法。事件綁定我們就寫一個最常用的字條目點選事件。

private OnItemClickListener onItemClickListener;

   @Override
    public void onBindViewHolder(final SimpleViewHolder holder, final int position) {
        if (this.onItemClickListener != null) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onItemClickListener.onItemClick(SimpleQuickAdapter.this, holder.itemView, mData.get(position), position);
                }
            });
        }
        convert(holder, position, mData.get(position));
    }

    protected abstract void convert(SimpleViewHolder holder, int position, B b);

    public  interface OnItemClickListener {
        void onItemClick(SimpleQuickAdapter adapter, View itemView, Object b, int position);
    }
    public void setOnItemClickListener(OnItemClickListener onItemClickListener){
        this.onItemClickListener = onItemClickListener;
    }
           

經過以上分裝adapter基本上已經可以用了

但是每次用viewHolder綁定資料時,都要用holder.itemView.findViewById(viewId)找到控件後才能對控件進行操作,這就顯得有點繁瑣。是以我們有必要對Holder進行簡單的封裝。

public class SimpleViewHolder extends RecyclerView.ViewHolder {
    public SimpleViewHolder(View itemView) {
        super(itemView);
    }

    public <V extends View>V getView(int viewId){
        return itemView.findViewById(viewId);
    }

    public SimpleViewHolder setText(int viewId, String text) {
        TextView view = getView(viewId);
        view.setText(text);
        return this;
    }
    public SimpleViewHolder setText(@IdRes int viewId, @StringRes int strId) {
        TextView view = getView(viewId);
        view.setText(strId);
        return this;
    }
}
           

我們可以在這裡對圖檔設定,背景設定,設定是否顯示…等常用功能進行封裝,原理差不多,就不多寫了。

我們每次getView都會去通路itemView.findViewById(viewId),如果對同一控件進行多次操作就沒這個必要。我們可以拿個容器去存儲用過的view來提升效率。

private SparseArray<View> views;
    public SimpleViewHolder(View itemView) {
        super(itemView);
        this.views = new SparseArray<>();
    }
    //當集合views中有view時直接傳回view,當集合中沒有view的時候findViewById擷取view,并且把這個view存入集合
    public <V extends View>V getView(int viewId){
        View view = views.get(viewId);
        if (view == null) {
            view = itemView.findViewById(viewId);
            views.put(viewId, view);
        }
        return (V) view;
    }
           

github傳送門

繼續閱讀