支持多種類item的RecyclerView適配器

需求

最近在做的項(xiàng)目中需要有多種類item的RecyclerView(以下縮寫為RV),用于在其中插入廣告item,帶提示信息的item等等。 大概看了一下網(wǎng)上的開源代碼,發(fā)現(xiàn)大多過于臃腫(代碼太多功能太雜),或者是與其他控件有沖突,又或者是我搜索的能力還不夠o(╯□╰)o。
于是牙一咬,就決定自己嘗試著寫一個。

分析與實(shí)現(xiàn)

既然已經(jīng)決定要寫了,肯定是要考慮以后在別的地方也能復(fù)用而不僅僅是滿足于當(dāng)前的場景的。 因此這個適配器應(yīng)該要能處理不管什么類型的item。
既然如此,便有兩個問題需要解決:

  • 如何存儲不同類型的數(shù)據(jù)
    答:在Java的世界里,所有類都是Object類派生出來的,因此可以將不同類型的數(shù)據(jù)放到一個Object數(shù)組當(dāng)中
  • 如何區(qū)別不同類型數(shù)據(jù)
    答:在RV的適配器中,ItemViewType是int類型的,而對于不同類型的數(shù)據(jù),可以用其類名( .getClass().getName() )來唯一標(biāo)識。 類名和int之間可以通過Map來進(jìn)行映射。這樣一來就相當(dāng)于以其類名作為適配器中的ItemViewType

于是適配器中的getItemViewType方法可以寫成下面這樣:

    private List<Object> itemList = new ArrayList<>();
    @Override
    public int getItemViewType(int position) {
        String name = itemList.get(position).getClass().getName();
        return name2type.get(name);
    }

當(dāng)然這當(dāng)中的name2type也不是憑空來的,用戶需要讓適配器知道類型信息,同時還要告知不同類型item的處理方法以及對應(yīng)的布局文件。因此需要提供一個類型注冊方法以供用戶提供這些信息
值的注意的是,name2type中的type對應(yīng)著binderInfos中的索引

    /**
     * 注冊item類型,對于每一種在該RecyclerView中出現(xiàn)的item類型,都需要調(diào)用這個函數(shù)進(jìn)行注冊
     * @param rClass item類型的class
     * @param binder 待用戶實(shí)現(xiàn)的數(shù)據(jù)綁定類
     * @param viewId 這種item對應(yīng)的layoutID
     */
    public void registerType(Class rClass, ViewBinder binder, int viewId){
        String className = rClass.getName();
        name2type.put(className, binderInfos.size());
        binderInfos.add(new BinderInfo(binder, viewId));
    }
    /**
     * 留給外部進(jìn)行實(shí)現(xiàn)的數(shù)據(jù)與view的綁定類
     */
    abstract static public class ViewBinder {
        abstract public void bindView(View itemView, Object ob, int position);
    }

這樣一來就建立了以下的關(guān)系:
itemList中的某一個item-----通過name2type----->ViewType------通過binderInfos.get(ViewType)------>binderInfos(其中包含數(shù)據(jù)綁定方法和布局id)

有了上述的關(guān)系之后,在onCreateViewHolder方法中就可以根據(jù)ViewType得到對應(yīng)的數(shù)據(jù)綁定類Binder和布局id

    @Override
    public MTypeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;
        MTypeViewHolder viewHolder;
        ViewBinder binder = binderInfos.get(viewType).binder;
        int viewId = binderInfos.get(viewType).id;
        view = LayoutInflater.from(parent.getContext()).inflate(viewId, parent, false);
        viewHolder = new MTypeViewHolder(binder, view);
        return viewHolder;
    }



因此每個ViewHolder都有了自己的Binder對象可用于做數(shù)據(jù)綁定

    @Override
    public void onBindViewHolder(MTypeViewHolder holder, int position) {
        holder.bindView();
    }

    class MTypeViewHolder extends RecyclerView.ViewHolder {
        ViewBinder binder;
        public MTypeViewHolder(ViewBinder binder, View itemView) {
            super(itemView);
            this.binder = binder;
        }
        public void bindView(){
            Object ob = itemList.get(getAdapterPosition());
            //調(diào)用外部傳入的binder的綁定數(shù)據(jù)的接口
            binder.bindView(itemView, ob, getAdapterPosition());
        }
    }

完整代碼以及示例

完整代碼:

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by LaiXiancheng on 2018/1/27.
 * Email: lxc.sysu@qq.com
 * 多類型的RecyclerView適配器
 * 對于每一種在該RecyclerView中出現(xiàn)的item類型,都需要調(diào)用registerType函數(shù)進(jìn)行注冊
 */

public class MultiTypeRVAdapter extends RecyclerView.Adapter<MultiTypeRVAdapter.MTypeViewHolder> {
    private List<Object> itemList = new ArrayList<>();
    private Map<String, Integer> name2type = new HashMap<>();
    private List<BinderInfo> binderInfos= new ArrayList<>();

    public MultiTypeRVAdapter(List<Object> itemList) {
        this.itemList = itemList;
    }

    @Override
    public int getItemViewType(int position) {
        String name = itemList.get(position).getClass().getName();
        return name2type.get(name);
    }

    /**
     * 注冊item類型,對于每一種在該RecyclerView中出現(xiàn)的item類型,都需要調(diào)用這個函數(shù)進(jìn)行注冊
     * @param rClass item類型的class
     * @param binder 待用戶實(shí)現(xiàn)的數(shù)據(jù)綁定類
     * @param viewId 這種item對應(yīng)的layoutID
     */
    public void registerType(Class rClass, ViewBinder binder, int viewId){
        String className = rClass.getName();
        name2type.put(className, binderInfos.size());
        binderInfos.add(new BinderInfo(binder, viewId));
    }


    @Override
    public MTypeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;
        MTypeViewHolder viewHolder;
        ViewBinder binder = binderInfos.get(viewType).binder;
        int viewId = binderInfos.get(viewType).id;
        view = LayoutInflater.from(parent.getContext()).inflate(viewId, parent, false);
        viewHolder = new MTypeViewHolder(binder, view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(MTypeViewHolder holder, int position) {
        holder.bindView();
    }

    @Override
    public int getItemCount() {
        return itemList.size();
    }


    class MTypeViewHolder extends RecyclerView.ViewHolder {
        ViewBinder binder;
        public MTypeViewHolder(ViewBinder binder, View itemView) {
            super(itemView);
            this.binder = binder;
        }
        public void bindView(){
            Object ob = itemList.get(getAdapterPosition());
            //調(diào)用外部傳入的binder的綁定數(shù)據(jù)的接口
            binder.bindView(itemView, ob, getAdapterPosition());
        }
    }

    /**
     * 留給外部進(jìn)行實(shí)現(xiàn)的數(shù)據(jù)與view的綁定類
     */
    abstract static public class ViewBinder {
        abstract public void bindView(View itemView, Object ob, int position);
    }

    private class BinderInfo{
        ViewBinder binder;
        int id;
        BinderInfo(ViewBinder binder, int id) {
            this.binder = binder;
            this.id = id;
        }
    }
}



示例:

recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerView_user);
itemList = new ArrayList<>();
itemList.add(new HintTypeItem("感興趣的用戶"));
userAdapter = new MultiTypeRVAdapter(itemList);

// ***************  注冊item類型  ***************
userAdapter.registerType(User.class, new MultiTypeRVAdapter.ViewBinder(){
    //注冊User類型的item
    @Override
    public void bindView(View itemView, Object ob, int position) {
        TextView tv_nickname = itemView.findViewById(R.id.tv_nickname);
        TextView tv_fade_name = itemView.findViewById(R.id.tv_fade_name);
        ImageView iv_header = itemView.findViewById(R.id.iv_header);
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(context,OtherActivity.class);
                intent.putExtra("user_id",user.getUser_id());
                activity.startActivity(intent);
            }
        });
        User user = (User)ob;
        ViewGroup.LayoutParams layoutParams1 = tv_nickname.getLayoutParams();
        layoutParams1.height = 80;
        tv_nickname.setLayoutParams(layoutParams1);
        tv_nickname.setText(Html.fromHtml(user.getNickname()));
        tv_fade_name.setText(Html.fromHtml(user.getFade_name()));
        Glide.with(context).load(Const.BASE_IP + user.getHead_image_url()).into(iv_header);
    }
}, R.layout.item_user);

userAdapter.registerType(HintTypeItem.class, new MultiTypeRVAdapter.ViewBinder() {
    //注冊HintTypeItem類型的item
    @Override
    public void bindView(View itemView, Object ob, int position) {
        TextView textView = itemView.findViewById(R.id.tv_hint);
        textView.setText(((HintTypeItem)ob).getHint());
    }
}, R.layout.random_item);
// ***************  結(jié)束注冊item類型  ***************

userLinearManager = new LinearLayoutManager(context);
recyclerView.setLayoutManager(userLinearManager);
userAdapter.notifyDataSetChanged();

recyclerView.setAdapter(userAdapter);

上述代碼注冊了兩種類型,分別是User類型和HintTypeItem類型,于是就可以往itemList中添加這兩種數(shù)據(jù)了,類似這樣

itemList.add(user);
itemList.add(new HintTypeItem("感興趣的用戶"));
itemList.add(user);


最終的效果
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容