Android 可分组的RecyclerViewAdapter

需求


image.png

用listView有分组的展示是ExpandableListView,但是现在都已经不用listview做列表展示了,RecyclerView也有分组展示的Adapter RecyclerViewAdapter。同时也支持头部悬浮吸顶功能

1、引入依赖

在Project的build.gradle在添加以下代码

  allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }

在Module的build.gradle在添加以下代码

compile 'com.github.donkingliang:GroupedRecyclerViewAdapter:1.3.0'
2、继承GroupedRecyclerViewAdapter
public class GroupedListAdapter extends GroupedRecyclerViewAdapter {
}
3、实现GroupedRecyclerViewAdapter里的方法

GroupedRecyclerViewAdapter是一个抽象类,它提供了一系列需要子类去实现的方法。

//返回组的数量
    public abstract int getGroupCount();

    //返回当前组的子项数量
    public abstract int getChildrenCount(int groupPosition);

    //当前组是否有头部
    public abstract boolean hasHeader(int groupPosition);

    //当前组是否有尾部
    public abstract boolean hasFooter(int groupPosition);

    //返回头部的布局id。(如果hasHeader返回false,这个方法不会执行)
    public abstract int getHeaderLayout(int viewType);

    //返回尾部的布局id。(如果hasFooter返回false,这个方法不会执行)
    public abstract int getFooterLayout(int viewType);

    //返回子项的布局id。
    public abstract int getChildLayout(int viewType);

    //绑定头部布局数据。(如果hasHeader返回false,这个方法不会执行)
    public abstract void onBindHeaderViewHolder(BaseViewHolder holder, int groupPosition);

    //绑定尾部布局数据。(如果hasFooter返回false,这个方法不会执行)
    public abstract void onBindFooterViewHolder(BaseViewHolder holder, int groupPosition);

    //绑定子项布局数据。
    public abstract void onBindChildViewHolder(BaseViewHolder holder,
                                               int groupPosition, int childPosition);
4、设置点击事件的监听

GroupedRecyclerViewAdapter提供了对列表的点击事件的监听方法。

  //设置组头点击事件
    public void setOnHeaderClickListener(OnHeaderClickListener listener) {
        mOnHeaderClickListener = listener;
    }

    //设置组尾点击事件
    public void setOnFooterClickListener(OnFooterClickListener listener) {
        mOnFooterClickListener = listener;
    }

    // 设置子项点击事件
    public void setOnChildClickListener(OnChildClickListener listener) {
        mOnChildClickListener = listener;
    }
5、对列表操作的注意

RecyclerView.Adapter提供了一系列对列表进行操作的方法。如:

//更新操作
public final void notifyDataSetChanged();
public final void notifyItemChanged(int position);
public final void notifyItemChanged(int position, Object payload);
public final void notifyItemRangeChanged(int positionStart, int itemCount);
public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload);

//插入操作
public final void notifyItemInserted(int position);
public final void notifyItemRangeInserted(int positionStart, int itemCount);

//删除操作
public final void notifyItemRemoved(int position)
public final void notifyItemRangeRemoved(int positionStart, int itemCount);

在GroupedRecyclerViewAdapter不建议使用RecyclerView.Adapter的任何对列表的操作方法,因为这些方法都是基于列表的操作,它的position是相对于整个列表而言的,而GroupedRecyclerViewAdapter是分组的列表,它对列表的操作应该是基于组的。同时GroupedRecyclerViewAdapter使用了组结构来维护整个列表的结构,使我们可以对列表进行组的操作,在列表发生变化时GroupedRecyclerViewAdapter需要及时对组结构进行调整,如果使用了RecyclerView.Adapter中的方法对列表进行更新,GroupedRecyclerViewAdapter可能因为无法及时调整组结构而发生异常。所以在使用中应该避免使用这些方法。GroupedRecyclerViewAdapter同样提供了一系列对列表进行操作的方法,我们应该使用GroupedRecyclerViewAdapter所提供的方法。

   //****** 刷新操作 *****//

    //通知数据列表刷新。对应 notifyDataSetChanged();
    public void notifyDataChanged();

    //通知一组数据刷新,包括组头,组尾和子项
    public void notifyGroupChanged(int groupPosition);

    //通知多组数据刷新,包括组头,组尾和子项
    public void notifyGroupRangeChanged(int groupPosition, int count);

    // 通知组头刷新
    public void notifyHeaderChanged(int groupPosition);

    // 通知组尾刷新
    public void notifyFooterChanged(int groupPosition);

    // 通知一组里的某个子项刷新
    public void notifyChildChanged(int groupPosition, int childPosition);

    // 通知一组里的多个子项刷新
    public void notifyChildRangeChanged(int groupPosition, int childPosition, int count);

    // 通知一组里的所有子项刷新
    public void notifyChildrenChanged(int groupPosition);

    //****** 删除操作 *****//
    // 通知所有数据删除
    public void notifyDataRemoved();

    // 通知一组数据删除,包括组头,组尾和子项
    public void notifyGroupRemoved(int groupPosition);

    // 通知多组数据删除,包括组头,组尾和子项
    public void notifyGroupRangeRemoved(int groupPosition, int count);

    // 通知组头删除
    public void notifyHeaderRemoved(int groupPosition);

    // 通知组尾删除
    public void notifyFooterRemoved(int groupPosition);

    // 通知一组里的某个子项删除
    public void notifyChildRemoved(int groupPosition, int childPosition);

    // 通知一组里的多个子项删除
    public void notifyChildRangeRemoved(int groupPosition, int childPosition, int count);

    // 通知一组里的所有子项删除
    public void notifyChildrenRemoved(int groupPosition);
    
    //****** 插入操作 *****//
    // 通知一组数据插入
    public void notifyGroupInserted(int groupPosition);

    // 通知多组数据插入
    public void notifyGroupRangeInserted(int groupPosition, int count);

    // 通知组头插入
    public void notifyHeaderInserted(int groupPosition);
    
    // 通知组尾插入
    public void notifyFooterInserted(int groupPosition);

    // 通知一个子项到组里插入
    public void notifyChildInserted(int groupPosition, int childPosition);

    // 通知一组里的多个子项插入
    public void notifyChildRangeInserted(int groupPosition, int childPosition, int count);

    // 通知一组里的所有子项插入
    public void notifyChildrenInserted(int groupPosition);
6、注意
//返回头部的布局id。(如果hasHeader返回false,这个方法不会执行)
    public abstract int getHeaderLayout(int viewType);

    //返回尾部的布局id。(如果hasFooter返回false,这个方法不会执行)
    public abstract int getFooterLayout(int viewType);

    //返回子项的布局id。
    public abstract int getChildLayout(int viewType);

如果有头布局,脚布局hasHeader 和hasFooter一定要返回true

如果要使用GridLayoutManager,一定要使用项目中所提供的GroupedGridLayoutManager。因为分组列表如果要使用GridLayoutManager实现网格布局,就要保证组的头部和尾部是要单独占用一行的。否则组的头、尾可能会跟子项混着一起,造成布局混乱。同时GroupedGridLayoutManager提供了对子项的SpanSize的修改方法,使用GroupedGridLayoutManager可以实现更多的复杂列表布局。

    //直接使用GroupedGridLayoutManager实现子项的Grid效果
    GroupedGridLayoutManager gridLayoutManager = new GroupedGridLayoutManager(this, 2, adapter);
   rvList.setLayoutManager(gridLayoutManager);
   

   GroupedGridLayoutManager gridLayoutManager = new GroupedGridLayoutManager(this, 4, adapter){
       //重写这个方法 改变子项的SpanSize。
       //这个跟重写SpanSizeLookup的getSpanSize方法的使用是一样的。
       @Override
       public int getChildSpanSize(int groupPosition, int childPosition) {
            if(groupPosition % 2 == 1){
                 return 2;
            }
            return super.getChildSpanSize(groupPosition, childPosition);
       }
   };
   rvList.setLayoutManager(gridLayoutManager);

完整代码

package com.jingdata.investment.ui.schedule.adapter;

import android.content.Context;
import android.view.View;

import com.donkingliang.groupedadapter.adapter.GroupedRecyclerViewAdapter;
import com.donkingliang.groupedadapter.holder.BaseViewHolder;
import com.jingdata.investment.R;
import com.jingdata.investment.network.bean.ScheduleBean;
import com.jingdata.investment.network.bean.ScheduleListByBusinessBean;
import com.jingdata.investment.network.bean.ScheduleUserVOSBean;
import com.jingdata.investment.utils.DateFormatUtil;
import com.jingdata.investment.utils.GlobalMethod;

import java.util.ArrayList;
import java.util.List;

public class GroupedListAdapter extends GroupedRecyclerViewAdapter {
    List<ScheduleListByBusinessBean.DataScheduleBean> mGroups = new ArrayList() {
    };

    public GroupedListAdapter(Context context, List<ScheduleListByBusinessBean.DataScheduleBean> newResult) {
        super(context);
        mGroups.clear();
        mGroups.addAll(newResult);
    }

    public void setData(List<ScheduleListByBusinessBean.DataScheduleBean> newResult) {
        this.mGroups.clear();
        this.mGroups.addAll(newResult);
        notifyDataChanged();
    }

    //返回组的数量
    @Override
    public int getGroupCount() {
        return mGroups == null ? 0 : mGroups.size();
    }

    //返回当前组的子项数量
    @Override
    public int getChildrenCount(int groupPosition) {
        List<ScheduleBean.ResultBean> dataList = mGroups.get(groupPosition).dataList;
        return dataList == null ? 0 : dataList.size();
    }

    //当前组是否有头部
    @Override
    public boolean hasHeader(int groupPosition) {
        return true;
    }

    //当前组是否有尾部
    @Override
    public boolean hasFooter(int groupPosition) {
        return false;
    }

    //返回头部的布局id。(如果hasHeader返回false,这个方法不会执行)
    @Override
    public int getHeaderLayout(int viewType) {
        return R.layout.item_head_group_schedule;
    }

    //返回尾部的布局id。(如果hasFooter返回false,这个方法不会执行)
    @Override
    public int getFooterLayout(int viewType) {
        return 0;
    }

    //返回子项的布局id。
    @Override
    public int getChildLayout(int viewType) {
        return R.layout.item_child_group_schedule;
    }

    //绑定头部布局数据。(如果hasHeader返回false,这个方法不会执行)
    @Override
    public void onBindHeaderViewHolder(BaseViewHolder holder, int groupPosition) {
        ScheduleListByBusinessBean.DataScheduleBean dataScheduleBean = mGroups.get(groupPosition);
        holder.setText(R.id.tv_head_data, dataScheduleBean.mData);

    }

    //绑定尾部布局数据。(如果hasFooter返回false,这个方法不会执行)
    @Override
    public void onBindFooterViewHolder(BaseViewHolder holder, int groupPosition) {

    }

    //绑定子项布局数据。
    @Override
    public void onBindChildViewHolder(BaseViewHolder holder, int groupPosition, int childPosition) {
        //1.设置标题
        ScheduleBean.ResultBean resultBean = mGroups.get(groupPosition).dataList.get(childPosition);
        holder.setText(R.id.tv_title, resultBean.title);
        //2.设置日期
        if (resultBean.startTime != null && resultBean.startTime != null) {
            String startDateString = DateFormatUtil.transForDate(resultBean.startTime, "yyyy-MM-dd HH:mm:ss");
            String endDateString = DateFormatUtil.transForDate(resultBean.endTime, "yyyy-MM-dd HH:mm:ss");
            holder.setText(R.id.tv_data, startDateString + "\n" + endDateString);
        }
        //3.设置参与人
        if (GlobalMethod.isEmpty(resultBean.scheduleUserVOS)) {
            holder.get(R.id.tv_person).setVisibility(View.INVISIBLE);
        } else {
            StringBuffer stringBuffer = new StringBuffer("参与人:");
            for (ScheduleUserVOSBean bean : resultBean.scheduleUserVOS) {
                stringBuffer.append(bean.firstName);
                stringBuffer.append("、");
            }
            stringBuffer.deleteCharAt(stringBuffer.length() - 1);
            holder.setText(R.id.tv_person, stringBuffer.toString());
            holder.get(R.id.tv_person).setVisibility(View.VISIBLE);
        }
    }

}

activity 中初始化

  mScheduleAdapter = new GroupedListAdapter(mActivity, newResult);
        mScheduleRecyclerView.setLayoutManager(new LinearLayoutManager(mActivity));
        mScheduleRecyclerView.setAdapter(mScheduleAdapter);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容

  • 转载请说明出处:http://www.jianshu.com/p/302d24db5423 今天给大家介绍的是一个...
    donkingliang阅读 19,329评论 54 20
  • 抽屉菜单 MaterialDrawer★7337 - 安卓抽屉效果实现方案 Side-Menu.Android★3...
    彬哥狠逍遥阅读 5,882评论 4 59
  • “这几天一直淅淅沥沥地下雨,又不能出去跑步了,一点也不喜欢。” “那用你的小太阳把雨水蒸发掉啊。” 01 一次偶然...
    阿钵阅读 293评论 0 1
  • 人间四月天 总是一画就画好多张。 人家视频里的,或简友圈的,只要好看的画都想模仿一番。 刚开始画...
    大U同学阅读 135评论 0 1
  • 《终身成长》是一本还没正式上线就十分期待的书。主要围绕两种思维方式,固定性思维方式和成长型思维方式而展开。学习之后...
    MAODSXX阅读 223评论 2 3