资讯专栏INFORMATION COLUMN

RecyclerView中Adapter和ViewHolder的封装

smartlion / 2444人阅读

摘要:前情提要最近项目我在项目中使用了代替了由于项目中有多出列表项使用这就导致需要写多个和其实怎么说呢就是懒想少写代码所以想研究一下能否简化一下具体实现封装分为和两部分如下所示抽象类继承并依赖注入的数据类型即和绑定的数据类型为该抽象类包含一个构造

前情提要

最近项目我在项目中使用了RecyclerView代替了ListView.由于项目中有多出列表项使用RecyclerView,这就导致需要写多个AdapterViewHolder.

其实,怎么说呢?就是懒,想少写代码,所以想研究一下能否简化一下.

具体实现

封装分为AdapterViewHolder两部分,如下所示.

ViewHolder

抽象类BaseHolder继承RecyclerView.ViewHolder,并依赖注入的数据类型M,即和ViewHolder绑定的数据类型为M.

该抽象类包含一个构造方法,用于获取item对应的布局.一个抽象函数用于将数据设置到item上面.

/**
 * 基础的ViewHolder
 * Created by zyz on 2016/5/17.
 */
public abstract class BaseHolder extends RecyclerView.ViewHolder {

    public BaseHolder(ViewGroup parent, @LayoutRes int resId) {
        super(LayoutInflater.from(parent.getContext()).inflate(resId, parent, false));
    }

    /**
     * 获取布局中的View
     * @param viewId view的Id
     * @param  View的类型
     * @return view
     */
    protected T getView(@IdRes int viewId){
        return (T) (itemView.findViewById(viewId));
    }

    /**
     * 获取Context实例
     * @return context
     */
    protected Context getContext() {
        return itemView.getContext();
    }

    /**
     * 设置数据
     * @param data 要显示的数据对象
     */
    public abstract void setData(M data);
}
Adapter

Adapter类也为抽象类,继承于RecyclerView.Adapter,并绑定了两个泛型:

M : 用于该 Adapter 的列表的数据类型,即List.

H : 即和 Adapter 绑定的 Holder 的类型.

并且,该 Adapter 自带 List 数据集合,声明时可以不用传递数据集合.也包含了 List 的相关操作.同时还给该 Adapter 绑定了一个 item 的点击事件,且为可选操作,不需要点击操作,直接传null即可.

/**
 * 基础的Adapter
 * Created by zyz on 2016/5/17.
 */
public abstract class BaseAdapter> extends RecyclerView.Adapter {

    protected List dataList;
    protected OnItemClickListener listener;

    /**
     * 设置数据,并设置点击回调接口
     *
     * @param list 数据集合
     * @param listener 回调接口
     */
    public BaseAdapter(@Nullable List list, @Nullable OnItemClickListener listener) {
        this.dataList = list;
        if (this.dataList == null) {
            this.dataList = new ArrayList<>();
        }

        this.listener = listener;
    }

    @Override
    public void onBindViewHolder(final H holder, int position) {
        holder.setData(dataList.get(position));
        if (listener != null) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    listener.onItemClick(holder);
                }
            });
        }
    }

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

    /**
     * 填充数据,此方法会清空以前的数据
     *
     * @param list 需要显示的数据
     */
    public void fillList(List list) {
        dataList.clear();
        dataList.addAll(list);
    }

    /**
     * 更新数据
     *
     * @param holder item对应的holder
     * @param data   item的数据
     */
    public void updateItem(H holder, M data) {
        dataList.set(holder.getLayoutPosition(), data);
    }

    /**
     * 获取一条数据
     *
     * @param holder item对应的holder
     * @return 该item对应的数据
     */
    public M getItem(H holder) {
        return dataList.get(holder.getLayoutPosition());
    }

    /**
     * 获取一条数据
     *
     * @param position item的位置
     * @return item对应的数据
     */
    public M getItem(int position) {
        return dataList.get(position);
    }

    /**
     * 追加一条数据
     *
     * @param data 追加的数据
     */
    public void appendItem(M data) {
        dataList.add(data);
    }

    /**
     * 追加一个集合数据
     *
     * @param list 要追加的数据集合
     */
    public void appendList(List list) {
        dataList.addAll(list);
    }

    /**
     * 在最顶部前置数据
     *
     * @param data 要前置的数据
     */
    public void preposeItem(M data) {
        dataList.add(0, data);
    }

    /**
     * 在顶部前置数据集合
     *
     * @param list 要前置的数据集合
     */
    public void preposeList(List list) {
        dataList.addAll(0, list);
    }
}
使用范例

使用范例为一种Item和多种Item这两种类型.

一种Item

运行结果如下图所示:

单个Item类型的ViewHolder如下:

/**
 * 一种View的Holder
 * Created by zyz on 2016/5/17.
 */
public class SingleHolder extends BaseHolder {

    TextView nameView;
    TextView ageView;

    public SingleHolder(ViewGroup parent, @LayoutRes int resId) {
        super(parent, resId);

        nameView = getView(R.id.name_tv);
        ageView = getView(R.id.age_tv);
    }

    @Override
    public void setData(Person data) {
        nameView.setText(data.getName());
        ageView.setText(String.valueOf(data.getAge()));
    }
}

与之对应的Adapter如下:

/**
 * 一种item的Adapter
 * Created by zyz on 2016/5/17.
 */
public class SingleAdapter extends BaseAdapter {

    public SingleAdapter(SingleItemClickListener listener) {
        super(null, listener);
    }

    @Override
    public SingleHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new SingleHolder(parent, R.layout.item_single);
    }

    @Override
    public void onBindViewHolder(final SingleHolder holder, int position) {
        super.onBindViewHolder(holder, position);
        holder.nameView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ((SingleItemClickListener) listener).onNameClick(getItem(holder).getName());
            }
        });

        holder.ageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ((SingleItemClickListener) listener).onAgeClick(getItem(holder).getAge());
            }
        });
    }

    public interface SingleItemClickListener extends OnItemClickListener {

        void onNameClick(String name);

        void onAgeClick(int age);
    }
}
多种Item

运行结果如下图所示:

多个Item的ViewHolder的写法,可以根据Item的View重合度来写:

如果多个item完全没有相同的部分,则多带带继承ViewHolder

如果Item之间有相同的部分,可以抽出来一个父类来继承ViewHolder

这里的范例Item是具有重合部分的.模型来自聊天界面.

Holder部分如下:
|-ChatHolder //聊天View的Holder,包含公共部分
    |-TextHolder //文字消息的Holder,包含文字特有的部分
    |-ImageHolder //图片消息的Holder,包含图片特有的部分.
    
数据部分如下:
|-ChatMsg //代表一条聊天消息
    |-TextMsg //代表一条文字消息
    |-ImageMsg //代表一条图片消息

ChatHolder代码如下,包含发送者的名称和时间:

/**
 * 聊天界面的ViewHolder
 * Created by zyz on 2016/5/18.
 */
public class ChatHolder extends BaseHolder {

    TextView senderNameTv;
    TextView createTimeTv;

    public ChatHolder(ViewGroup parent, @LayoutRes int resId) {
        super(parent, resId);

        senderNameTv = getView(R.id.name_tv);
        createTimeTv = getView(R.id.create_time_tv);
    }

    @Override
    public void setData(ChatMsg data) {
        senderNameTv.setText(data.getSenderName());
        createTimeTv.setText(data.getCreateTime());
    }
}

TextHolder的代码如下,包含文本显示的View

/**
 * 文本消息的Holder
 * Created by zyz on 2016/5/18.
 */
public class TextHolder extends ChatHolder {

    TextView contentTv;

    public TextHolder(ViewGroup parent, @LayoutRes int resId) {
        super(parent, resId);

        contentTv = getView(R.id.content_tv);
    }

    @Override
    public void setData(ChatMsg data) {
        super.setData(data);
        contentTv.setText(((TextMsg)data).getText());
    }
}

其中的setData()方法默认调用父类的方法,可以直接设置发送者的名称和时间.

ImageHolder的代码如下,包含显示图片的View

/**
 * 表情消息的Holder
 * Created by zyz on 2016/5/18.
 */
public class ImageHolder extends ChatHolder {

    ImageView contentIv;

    public ImageHolder(ViewGroup parent, @LayoutRes int resId) {
        super(parent, resId);
        contentIv = getView(R.id.content_iv);
    }

    @Override
    public void setData(ChatMsg data) {
        super.setData(data);
        contentIv.setImageResource(((ImageMsg)data).getResId());
    }
}

最后是我们的Adapter,代码不多.

/**
 * 聊天界面的Adapter
 * Created by zyz on 2016/5/18.
 */
public class ChatAdapter extends BaseAdapter {

    private static final int VIEW_TEXT = 0;
    private static final int VIEW_IMAGE = 1;

    public ChatAdapter(OnItemClickListener listener) {
        super(null, listener);
    }

    @Override
    public ChatHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ChatHolder holder;
        if (viewType == VIEW_IMAGE) {
            holder = new ImageHolder(parent, R.layout.item_msg_img_left);
        } else {
            holder = new TextHolder(parent, R.layout.item_msg_text_left);
        }
        return holder;
    }

    @Override
    public int getItemViewType(int position) {
        if (getItem(position).getMsgType() == ChatMsg.TYPE_TEXT) {
            return VIEW_TEXT;
        } else {
            return VIEW_IMAGE;
        }
    }
}

上述设置的有时间监听,则对应的事件处理在Activity中完成

chatAdapter = new ChatAdapter(new OnItemClickListener() {
    @Override
    public void onItemClick(ChatHolder holder) {
        //处理事件
    }
});

以上就是对ViewHolder和Adapter的简易封装,以后会根据需要继续封装简化.

代码地址如下:https://github.com/Dev-Wiki/RecyclerView

更多文章请移步我的博客:DevWiki Blog

重要说明

想随时获取最新博客文章更新,请关注公共账号DevWiki,或扫描下面的二维码:

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/12832.html

相关文章

  • RecyclerView问题汇总

    摘要:缺点自动装箱的存在意味着每一次插入都会有额外的对象创建。对象本身是一层额外需要被创建以及被垃圾回收的对象。相较于我们舍弃了和类型的放弃了并依赖于二分法查找。 目录介绍 25.0.0.0 请说一下RecyclerView?adapter的作用是什么,几个方法是做什么用的?如何理解adapter订阅者模式? 25.0.0.1 ViewHolder的作用是什么?如何理解ViewHolder...

    boredream 评论0 收藏0
  • RecyclerView用法源码深度解析

    摘要:此方法应由实现使用,以获取视图来表示来自的数据。如果适配器没有指示给定位置上的数据已更改,则回收程序将尝试发回一个以前为该数据初始化的报废视图,而不进行重新绑定。如果它只附加了一个适配器,并且新适配器使用与不同的,则将清除其缓存。 目录介绍 1.RecycleView的结构 2.Adapter 2.1 RecyclerView.Adapter扮演的角色 2.2 重写的方法 2.3...

    ShowerSun 评论0 收藏0
  • RecyclerView封装综合案例【包含25篇博客】

    摘要:支持复杂页面,例如添加自定义头部和底部布局,支持横向滑动,还可以支持粘贴头部类似微信好友分组,支持不规则瀑布流效果,支持侧滑删除功能。支持粘贴头部的需求效果,这种效果类似微信好友分组的那种功能界面。 目录介绍 1.复杂页面库介绍 2.本库优势亮点 2.1 支持多种状态切换管理 2.2 支持添加多个header和footer 2.3 支持侧滑功能和拖拽移动 2.4 其他亮点介绍 ...

    silenceboy 评论0 收藏0
  • 安卓开发笔记(十):升级ListView为RecylerView使用

    摘要:的优点并不会完全替代这点从没有被标记为可以看出,两者的使用场景不一样。基本使用引用在文件中引入该类。会根据情况,将原来的放入或,从而在复用时提升效率。概述 RecyclerView是什么 从Android 5.0开始,谷歌公司推出了一个用于大量数据展示的新控件RecylerView,可以用来代替传统的ListView,更加强大和灵活。RecyclerView的官方定义如下: A flexi...

    番茄西红柿 评论0 收藏0
  • RecyclerView实现多type页面

    摘要:,无法复用,假如有多个页面有多个,那么就要写多个。绑定,主要作用是绑定数据到正确的视图上。可维护性不同的列表类型由添加处理,哪怕添加多个,相互之间互不干扰,代码简洁,维护成本低。 目录介绍 01.先看看实际需求 02.adapter实现多个type 03.这样写的弊端 04.如何优雅实现adapter封装 好消息 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,...

    testHs 评论0 收藏0

发表评论

0条评论

smartlion

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<