RecyclerView Adapter简单封装

Posted freeCodeSunny

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RecyclerView Adapter简单封装相关的知识,希望对你有一定的参考价值。

前言

       今天是新年第一天,本来打算在上一年的最后一天写下来的,但是由于玩的时间长了一点,所以今天才写,算是在新的一年开一个好头,新年新气象嘛!

       至于为什么要写这个文章呐!由于项目中以前都是用ListView实现的列表,很多东西都已经习惯化了,每次都是由的新的模块或者功能才使用RecyclerView,因此一直都没有完整的总结一遍,这次也是在一个新的项目中,准备完全采用RecyclerView来实现列表,因此初步的封装了一下。

       为什么要封装呐?事实上,系统对该控件已经封装的很好了,直接拿过来用就行,使用方式也跟以前差不多,构造一个RecyclerView,再构造一个Adapter,当连续写了2个之后就不再想重复劳动了,因此对Adapter进行的简单的封装,

BaseAdapter

       这里就大致罗列一下封装的过程,只是实现了初步的功能,后续的功能可以逐步添加,在最后会给出一个使用样例。

BaseAdapter

       既然是对Adapter的简单封装,那第一步就是先来实现基类的Adapter,代码如下:

public class BaseAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> 

    /**
     * data source
     */
    public List<T> dataList;

    /**
     * onClick onLongClick callback
     */
    public onItemClickListener listener;

    /**
     * constructor view holder delegate
     */
    public BaseDelegate delegate;

    /**
     * constructor
     *
     * @param dataList
     * @param delegate
     */
    public BaseAdapter(List<T> dataList, BaseDelegate delegate) 
        this(dataList, delegate, null);
    

    /**
     * constructor
     *
     * @param dataList
     * @param delegate
     * @param listener
     */
    public BaseAdapter(List<T> dataList, BaseDelegate delegate, onItemClickListener listener) 
        checkData(dataList);
        this.delegate = delegate;
        this.listener = listener;
    

    /**
     * just is empty
     *
     * @param dataList
     */
    private void checkData(List<T> dataList) 
        if (dataList == null) 
            dataList = Collections.emptyList();
        
        this.dataList = dataList;
    

    /**
     * set onclick & onLongClick callback
     *
     * @param listener
     */
    public void setOnItemClickListener(onItemClickListener listener) 
        this.listener = listener;
    

    /**
     * create view holder
     *
     * @param parent
     * @param viewType
     * @return
     */
    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
        return delegate.onCreateViewHolder(parent, viewType);
    

    /**
     * bind view holder
     *
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(BaseViewHolder holder, final int position) 
        holder.onBindViewHolder(dataList.get(position));
        if (listener != null && holder.enable()) 
            holder.itemView.setOnClickListener(new View.OnClickListener() 
                @Override
                public void onClick(View v) 
                    listener.onClick(v, dataList.get(position));
                
            );
            holder.itemView.setOnLongClickListener(new View.OnLongClickListener() 
                @Override
                public boolean onLongClick(View v) 
                    return listener.onLongClick(v, dataList.get(position));
                
            );
        
    

    /**
     * get item count
     *
     * @return
     */
    @Override
    public int getItemCount() 
        return dataList.size();
    

    /**
     * get item view type
     *
     * @param position
     * @return
     */
    @Override
    public int getItemViewType(int position) 
        return delegate.getItemViewType(dataList.get(position));
    

1:这里首先是继承RecyclerView.Adapter,这里指定了一个BaseViewHolder,后续我们就来构建基类的ViewHolder。
2:传入需要操作的数据列表,这里加入了泛型,后续跟据实际使用的数据类进行使用。
3:由于RecyclerView不像ListView一样,默认实现了Item的点击事件,因此这里加入了点击事件的回调。
4:我们可以发现onCreateViewHolder,我们采用了代理来实现,等会我们会实现代理
5:在onBindViewHolder中,我们直接调用了Holder的bind过程,其次实现了点击事件。

BaseViewHolder

       前面我们提到了统一实现一个基类ViewHolder,这里我们就看看BaseViewHolder怎么实现的。

public abstract class BaseViewHolder<T> extends RecyclerView.ViewHolder 

    /**
     * TODO
     * single view may be direct construction, eg: TextView view = new TextView(context);
     *
     * @param parent current no use, may be future use
     * @param view
     */
    public BaseViewHolder(ViewGroup parent, View view) 
        super(view);
        findViews();
    

    /**
     * find all views
     */
    public abstract void findViews();

    /**
     * bind view holder
     *
     * @param data
     */
    public abstract void onBindViewHolder(T data);

    /**
     * holder click enable
     *
     * @return
     */
    public boolean enable() 
        return true;
    

1:这里首先还是继承自RecyclerView.ViewHolder
2:在构造函数中加入了控件的查找
3:BaseViewHolder是一个抽象类,有一个抽象方法onBindViewHolder,主要是为什么了实现界面绑定功能
4:最后有一个enable函数,这里主要是item是否可以有点击效果

onItemClickListener

       由于RecyclerView默认没有点击事件的回调,因此需要我们自定义来实现该效果。这里我们加入点击事件的回调:

public interface onItemClickListener<T> 
    void onClick(View v, T data);

    boolean onLongClick(View v, T data);

1:功能能很简单,主要是实现了单击和长按的回调。也可以实现更改的回调

BaseDelegate

       BaseDelegate也是一个抽象类,他主要是代理了Adapter中的create过程,代码如下:

public abstract class BaseDelegate<T> 

    /**
     * crate view holder by view type
     *
     * @param parent
     * @param viewType
     * @return
     */
    public abstract BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType);

    /**
     * get view type by data
     *
     * @param data
     * @return
     */
    public abstract int getItemViewType(T data);

    /**
     * get layout id by view type
     *
     * @param viewType
     * @return
     */
    public abstract int getLayoutId(int viewType);

    /**
     * get item view
     *
     * @param parent
     * @param viewType
     * @return
     */
    public View getItemView(ViewGroup parent, int viewType) 
        return LayoutInflater.from(parent.getContext()).inflate(getLayoutId(viewType), parent, false);
    

1:这里主要是代理了onCreateViewHolder,返回一个具体的ViewHolder
2:还代理了getItemViewType,具体根据实现返回type类型

使用

       上面的代码都很简单,简单的封装了整个使用过程,那在项目中又是怎么来使用的呐?是否使用变动更麻烦了?比如我们要实现如下效果:

界面布局

       这里我们首先来创一个Activity,布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff">

    <include
        android:id="@+id/my_toolbar"
        layout="@layout/toolbar_title_layout"/>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/setting_info"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/my_toolbar">

    </android.support.v7.widget.RecyclerView>

</RelativeLayout>

主要包含了一个标题与一个RecyclerView

界面代码

public class MainActivity extends AppCompatActivity 

    private RecyclerView settingInfo;

    private List<ItemData> datas;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViews();
        initTitle();
        initData();
        initRecyclerView();
    

    /**
     * find views
     */
    private void findViews() 
        settingInfo = (RecyclerView) findViewById(R.id.setting_info);
    

    /**
     * init title bar
     */
    private void initTitle() 
        Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
        myToolbar.setNavigationIcon(android.support.v7.appcompat.R.drawable.abc_ic_ab_back_material);
        myToolbar.setNavigationOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                setResult(RESULT_CANCELED);
                finish();
            
        );
        ToolbarHelper.layoutTitleCenter(this, myToolbar, getString(R.string.title));
    

    private void initData() 
        datas = new ArrayList<>(20);
        datas.add(new ItemData(0, SettingDelegate.SELF_INFO));
        datas.add(new ItemData(0, SettingDelegate.SEPARATE_TYPE));
        for (int i = 0; i < 3; ++i) 
            datas.add(new ItemData(0, SettingDelegate.ARROW_TYPE, "我是箭头"));
            datas.add(new ItemData(0, SettingDelegate.CHECK_TYPE, "选中我"));
            datas.add(new ItemData(0, SettingDelegate.TOGGLE_TYPE, "开启"));
            datas.add(new ItemData(0, SettingDelegate.SEPARATE_TYPE));
        
        datas.add(new ItemData(0, SettingDelegate.LOGOUT_TYPE));
    

    /**
     * init
     */
    private void initRecyclerView() 
        settingInfo.setHasFixedSize(true);
        DividerItemDecoration decor = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
        settingInfo.addItemDecoration(decor);
        settingInfo.setLayoutManager(new LinearLayoutManager(this));
        settingInfo.setAdapter(new BaseAdapter(datas, new SettingDelegate(), new onItemClickListener() 
            @Override
            public void onClick(View v, Object data) 
                ArrowActivity.start(MainActivity.this);
            

            @Override
            public boolean onLongClick(View v, Object data) 
                return false;
            
        ));
    

1:查找了页面控件,并且设置了标题,构造了数据源
2:对RecyclerView设置了分割线,同时设置了布局管理器,这些都是基本功能,最后设置了Adapter,这里主要指定了数据源,同时设置了代理, 最后传入了点击事件的回调。

SettingDelegate

       前面传入了SettingDelegate,这里我们看看SettingDelegate怎么实现的:

public class SettingDelegate extends BaseDelegate<ItemData> 

    public static final int SEPARATE_TYPE = 0;

    public static final int SELF_INFO = 1;

    public static final int ARROW_TYPE = 2;

    public static final int CHECK_TYPE = 3;

    public static final int TOGGLE_TYPE = 4;

    public static final int LOGOUT_TYPE = 5;

    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
        switch (viewType) 
            case SEPARATE_TYPE:
                return new SeparateViewHolder(parent, getItemView(parent, viewType));
            case SELF_INFO:
                return new SelfInfoViewHolder(parent, getItemView(parent, viewType));
            case ARROW_TYPE:
                return new ArrowViewHolder(parent, getItemView(parent, viewType));
            case CHECK_TYPE:
                return new CheckViewHolder(parent, getItemView(parent, viewType));
            case TOGGLE_TYPE:
                return new ToggleViewHolder(parent, getItemView(parent, viewType));
            case LOGOUT_TYPE:
                return new LogoutViewHolder(parent, getItemView(parent, viewType));
        
        return null;
    

    @Override
    public int getItemViewType(ItemData data) 
        return data.holderType;
    

    @Override
    public int getLayoutId(int viewType) 
        switch (viewType) 
            case SEPARATE_TYPE:
                return R.layout.view_holder_setting_separate;
            case SELF_INFO:
                return R.layout.view_holder_setting_self_info;
            case ARROW_TYPE:
                return R.layout.view_holder_setting_arrow;
            case CHECK_TYPE:
                return R.layout.view_holder_setting_check;
            case TOGGLE_TYPE:
                return R.layout.view_holder_setting_toggle;
            case LOGOUT_TYPE:
                return R.layout.view_holder_setting_logout;
        
        return 0;
    

1:根据图片我们可以看到有多种类型,getItemViewType根据数据返回不同的type
2:onCreateViewHolder根据type返回每一种ViewHolder
3:这里我们指定了具体的数据类型ItemData,这个是随便构造的,真正项目中都是根据实际的数据来构造的

ItemData

       这里的ItemData只是为了掩饰而构造的数据类型,代码如下:

public class ItemData 

    public int tag;

    public int holderType;

    public String itemDesc;

    public Object data;

    public ItemData(int tag, int holderType) 
        this.tag = tag;
        this.holderType = holderType;
    

    public ItemData(int tag, int holderType, String itemDesc) 
        this.tag = tag;
        this.holderType = holderType;
        this.itemDesc = itemDesc;
    

    public ItemData(int tag, int holderType, Object data) 
        this.tag = tag;
        this.holderType = holderType;
        this.data = data;
    

SelfInfoViewHolder

       这里有5种类型,我们就随机挑选一种来进行演示,这里我们选择了头像部分。代码如下:

public class SelfInfoViewHolder extends BaseViewHolder<ItemData> 

    private ImageView headView;

    private TextView nameGender;

    private TextView birthday;

    /**
     * @param parent
     * @param view
     */
    public SelfInfoViewHolder(ViewGroup parent, View view) 
        super(parent, view);
    

    @Override
    public void findViews() 
        headView = (ImageView) itemView.findViewById(R.id.self_info_head_view);
        nameGender = (TextView) itemView.findViewById(R.id.self_name_gender);
        birthday = (TextView) itemView.findViewById(R.id.birthday);
    

    @Override
    public void onBindViewHolder(ItemData data) 

    

    @Override
    public boolean enable() 
        return false;
    

1:首先是继承自BaseViewHolder,指定了数据类型
2:查找了页面控件,保证在bind的时候使用。
3:onBindViewHolder进行数据与控件的绑定展示
4:enable指定了该条目不需要点击事件

总结

       上面只是对Adapter的简单封装,不过大部分的功能已经够用了,很多人可能会在每一个ViewHolder构造的时候,传入Layout布局,这里采用Delegate来统一返回布局,将布局放在了一起,同时针对每一个页面可以实现直接的delegate,这样就将类型,处理与布局统一管理。方便改代码的时候查找代码。

代码

       这里只是大致列出了其中一部分代码,为了方便使用最后给出代码地址:传送门

后记

       本来开始写的时候还是新年第一天,写完的时候已经变成了新年第二天,要是昨天开始写,那岂不是就写了一年。

以上是关于RecyclerView Adapter简单封装的主要内容,如果未能解决你的问题,请参考以下文章

android RecyclerView adapter 封装

android RecyclerView adapter 封装

android RecyclerView adapter 封装

RecyclerView 知识梳理 - Adapter

xml SectionedGridRecyclerViewAdapter:使用此类实现一个简单的分段网格`RecyclerView.Adapter`。

Adapter的封装之路