Android 一起來封裝一個RecyclerViewAdapter

neverwoods發表於2017-12-23

前言

上一篇我們一起封裝了一個還算簡單實用的 Adapter,可惜是 ListView、GridView 用的。在 RecyclerView 大行其道的如今,對應 Adapter 的使用簡單也是一個必然的需求。

正文

按照慣例,先來看看一個原始的 RecyclerViewAdapter 是怎麼寫的

RecyclerView.Adapter adapter = new RecyclerView.Adapter() {
            @Override
            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                View itemView = LayoutInflater.from(context).inflate(layoutId, parent, false);
                return new Holder(itemView);
            }

            @Override
            public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
                Holder holder = (Holder) viewHolder;

                holder.textView.setText(dataSource.get(position));
                holder.button.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        //TODO
                    }
                });
            }

            @Override
            public int getItemCount() {
                return dataSource.size();
            }
        };
複製程式碼
private class Holder extends RecyclerView.ViewHolder {

        public TextView textView;
        public Button button;

        public Holder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(textViewId);
            button = itemView.findViewById(buttonId);
        }
    }
複製程式碼

以上程式碼純屬盲打,如有錯誤敬請諒解。

大致上我們能看到,相對於 ListView,RecyclerView 幫我們實現了 Item 的複用邏輯,不需要我們再寫,但還是每次都要寫一個 ViewHolder 類。之前我們是呼叫一個自己寫的靜態 ViewHolder 類,但是 RecyclerView 需要的 ViewHolder 一定是 RecyclerView.ViewHolder 或其子類,所以我們只能繼承 RecyclerView.ViewHolder 來做封裝工作。

public class RecyclerViewHolder extends RecyclerView.ViewHolder {

    public RecyclerViewHolder(View itemView) {
        super(itemView);
    }

    public <T extends View> T getChildView(int id) {
        return (T) itemView.findViewById(id);
    }
}
複製程式碼

其實只是新增了一個 getChildView 的方法用來獲取子 View 而已。

Adapter 的封裝也很簡單,只是新增了一個繫結資料的抽象方法:

public abstract class RecyclerAdapter<T> extends RecyclerView.Adapter<RecyclerViewHolder> {

    /**
     * 由子類實現繫結方法
     * @param holder
     * @param data
     * @param position
     */
    protected abstract void onBindData(RecyclerViewHolder holder, T data, int position);

    protected Context mContext;
    protected List<T> mData;
    protected LayoutInflater mInflater;
    protected int layoutRes;

    public RecyclerAdapter(Context context, List<T> data, int layoutRes) {
        this.mData = data;
        this.mContext = context;
        this.layoutRes = layoutRes;
        this.mInflater = LayoutInflater.from(mContext);
    }

    public void refresh(List<T> data) {
        try {
            this.mData = data;
            notifyDataSetChanged();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new RecyclerViewHolder(mInflater.inflate(layoutRes, parent, false));
    }

    @Override
    public void onBindViewHolder(RecyclerViewHolder holder, int position) {
        onBindData(holder, mData.get(position), position);
    }

    @Override
    public int getItemCount() {
        return mData.size();
    }
}
複製程式碼

我們來看看現在使用起來方不方便?

RecyclerAdapter adapter = new RecyclerAdapter<String>(this, dataSource, layoutRes) {
            @Override
            protected void onBindData(RecyclerViewHolder holder, String data, int position) {
                TextView textView = holder.getChildView(textViewId);
                Button button = holder.getChildView(buttonId);
                
                textView.setText(data);
                button.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        //TODO
                    }
                });
            }
        };
複製程式碼

有木有一種很親切的感覺?終於又回到了一個方法實現 Adapter 的日子了 T_T ~~

小結

封裝之後才發現,其實 RecyclerView 的 Adapter 是可以封裝得比 ListView 更為簡潔易用的,這應該歸功於 RecyclerView 自身就實現了快取複用。

真·正文

還記得上一篇文章我就說過這還不是極簡嗎?仔細看上面的程式碼,每次重寫繫結方法的時候,幾乎都要先用 holder 的 getChild 方法獲取到 view 例項,再進行填充資料、新增監聽等操作。有沒有一種辦法能一步到位呢?

當然有!

先看程式碼

public class RecyclerViewHolder extends RecyclerView.ViewHolder {

    public RecyclerViewHolder(View itemView) {
        super(itemView);
    }

    public <T extends View> T getChildView(int id) {
        return (T) itemView.findViewById(id);
    }

    public RecyclerViewHolder setText(int viewId, CharSequence charSequence) {
        TextView textView = getChildView(viewId);
        textView.setText(charSequence);
        return this;
    }
    ......
}
複製程式碼

可以看出,我們給 ViewHolder 新增了 setText 方法,傳入了 viewId 和我們想要設定的值,在方法內部去 getChildView 並進行了資料填充。同樣的,設定監聽等方法也可以這樣子去實現。

RecyclerAdapter adapter = new RecyclerAdapter<String>(this, dataSource, layoutRes) {
            @Override
            protected void onBindData(RecyclerViewHolder holder, String data, int position) {
                holder.setText(textViewId, data)
                        .setOnClickListener(buttonId, new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                //TODO
                            }
                        });
            }
        };
複製程式碼

再安利一下 lambda 表示式

RecyclerAdapter adapter = new RecyclerAdapter<String>(this, dataSource, layoutRes) {
            @Override
            protected void onBindData(RecyclerViewHolder holder, String data, int position) {
                holder.setText(textViewId, data)
                        .setOnClickListener(buttonId, (v) -> {/** TODO */});
            }
        };
複製程式碼

是不是感覺棒棒噠?很多常用的方法都可以放到 ViewHolder 中去,比如 setTextSize、setTextColor、setVisibility、setImageResource、setBackgroundColor 等。

因為篇幅的關係,原本計劃一起寫的 ItemType 處理很難再塞進來。再加上 databinding 模式下的 Adapter 封裝,感覺 Adapter 這個系列就夠我寫好一陣了,會不會有點狗血?

感謝

鴻揚大神:https://github.com/hongyangAndroid/baseAdapter 以及我自己的開源專案:https://github.com/neverwoodsS/zy-open

相關文章