/RecyclerView

RecyclerView功能集封装 - 支持上拉下拉、Header、Footer、Empty、单击 & 长按 & 滑动删除 & 拖拽换位 & 侧滑菜单功能 & 多层分组 & 悬浮分组头

Primary LanguageJava

RecyclerView

RecyclerView功能集封装

如要了解功能实现,请运行app程序查看控制台日志和源代码!

gif gif gif gif gif

依赖


  • AndroidStudio
	allprojects {
            repositories {
                ...
                maven { url 'https://jitpack.io' }
            }
	}
	 compile 'com.github.AcmenXD:RecyclerView:3.3'

功能


####v3.3 新增

  • 新增分组能否悬浮的开关

####v3.2 优化

  • 优化 事件监听 逻辑

####v3.1 优化

  • 修复 viewPosition=-1 导致的崩溃问题

####v3.0 优化

  • 整体优化及bug修复

####v2.1 优化

####v2.0 全新升级

####v1.8 修复问题(修复问题较多,建议升到1.8版本使用):

  • Adatper无数据时,导致GroupHeadLayout计算时出现OOM问题
  • LoadMoreLayout初始化状态,ui显示异常问题
  • Adatper.mDatas对象变化后,导致更新ui异常的问题
  • Adatper新增setDatas函数, 方便更新数据

####v1.5 新增功能有:

  • 调整兼容版本,支持4.0(含)以上系统

####v1.4 支持功能如下

  • 支持多层级分组功能(支持垂直|水平布局)(支持LinearLayoutManager/GridLayoutManager/StaggeredGridLayoutManager)
  • 优化item各种事件
  • 特别说明:多层级分组的GroupHeadLayout&GroupItemLayout暂不支持Margin及Padding设置,显示效果会有影响

####v1.3 支持功能如下

  • 支持分组功能
  • 支持分组头布局悬浮RecyclerView顶部功能

####v1.0 支持功能如下

  • 支持下拉刷新
  • 支持LoadMore(上拉加载更多)
  • 支持添加Header、Footer、Empty(头、尾、空)视图
  • 支持一个Adapter自定义多种Item类型
  • 简化RecyclerView.Adapter及ViewHolder的实现
  • LoadMore 和 Empty支持点击回调
  • Adapter链式调用,易读、易懂、易用
  • 支持item事件:单击 & 长按 & 滑动删除 & 拖拽换位 & 侧滑菜单功能(事件无任何冲突)
  • 此封装库未对RecyclerView进行任何更改,布局或代码中使用原生RecyclerView即可

使用 -> 以下代码 注释很详细、很重要很重要很重要!!!


  • xml布局
// 定义下拉刷新控件SwipeRefreshLayout 及 RecyclerView列表控件
<android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/srl"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</android.support.v4.widget.SwipeRefreshLayout>
  • 初始RecyclerView
/*
 * recyclerView 需设置布局管理器
 * * 通常只需要一个管理器即可,由于演示代码,固配置了所有管理器
 */
RecyclerView rv = (RecyclerView) findViewById(R.id.rv);
// 线性布局
LinearLayoutManager manager1 = new LinearLayoutManager(this);
// 网格布局  参数:1.上下文对象  2.设置 列/行 数
GridLayoutManager manager2 = new GridLayoutManager(this, 3);
// 瀑布流布局 参数:1.设置 列/行 数  2.横/纵 向排列
StaggeredGridLayoutManager manager3 = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
// 设置管理器的 横/纵 向排列
manager1.setOrientation(OrientationHelper.VERTICAL);
manager2.setOrientation(OrientationHelper.VERTICAL);
manager3.setOrientation(OrientationHelper.VERTICAL);
// 将管理器绑定到recyclerView
rv.setLayoutManager(manager1);
// 设置item之间的分隔线(默认提供三种分隔线,对应三种局部.如有其它需求,可自行实现)
rv.addItemDecoration(new LinearLayoutDecoration(this));
// rv.addItemDecoration(new GridLayoutDecoration(this));
// rv.addItemDecoration(new StaggeredGridLayoutDecoration(this));
// 设置增加或删除item项的动画
rv.setItemAnimator(new DefaultItemAnimator());

下拉刷新

/**
 * 下载刷新用系统提供的SwipeRefreshLayout,并未使用PullToRefresh
 */
SwipeRefreshLayout srl = (SwipeRefreshLayout) findViewById(R.id.srl);
// 设置刷新控件转圈圈的动画颜色,每转一圈一个颜色
srl.setColorSchemeColors(Color.RED, Color.GREEN, Color.BLUE);
// 设置转圈圈的背景色
srl.setProgressBackgroundColorSchemeColor(Color.YELLOW);
// 设置转圈圈的大小,默认是DEFAULT
srl.setSize(SwipeRefreshLayout.DEFAULT);//SwipeRefreshLayout.LARGE
// 参数:1.下拉圈圈是否缩放  2.圈圈下拉的高度
srl.setProgressViewEndTarget(true, 200);
// 设置刷新的事件监听器
srl.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
    @Override
    public void onRefresh() {
        datas.clear(); // 清理输出
        addNewData(); // 添加新数据
        refreshAdapter(); // 刷新Adapter
        srl.setRefreshing(false); // 关闭圈圈
    }
});

Adapter - 单类型Item

/**
 * 创建SimpleAdapter
 * 泛型:数据的类型
 * 参数:1.上下文对象  2.recyclerview实例  3.item布局  4.数据集List
 */
SimpleAdapter mAdapter = new SimpleAdapter<Data>(R.layout.activity_recycler_item, datas) {
    @Override
    public void convert(ViewHolder viewHolder,Data data, int dataPosition) {
	    // 刷新界面 viewHolder-控件集  data-数据  dataPosition-位置
	    // getView(rId)是viewHolder实现的方法,此方式获取控件无需再强转类型
	    TextView tv = viewHolder.getView(R.id.activity_recycler_item_tv);
            tv.setText(data.name);
    }
};

Adapter - 多类型Item

/**
 * 创建MultiItemTypeAdapter
 * 泛型:数据的类型
 * 参数:1.上下文对象  2.recyclerview实例  3.数据集List
 */
MultiItemTypeAdapter mAdapter = new MultiItemTypeAdapter<Data>(datas);
// 添加一种item类型(有几种类型,添加几个)
mAdapter.addItemViewDelegate(new ItemDelegate<Data>() {
    @Override
    public int getItemViewLayoutId() {
	// 此种类的item需要的布局文件
	return R.layout.activity_recycler_item;
    }
    @Override
    public boolean isItemViewType(Data data, int dataPosition) {
	// 根据数据或位置判断是否用此种类型的item,返回bool类型
	return data.type == 1;
    }
    @Override
    public void convert(ViewHolder viewHolder, Data data, int dataPosition) {
	// 刷新界面 viewHolder-控件集  item-数据  dataPosition-位置
	// getView(rId)是viewHolder实现的方法,此方式获取控件无需再强转类型
        TextView tv = viewHolder.getView(R.id.activity_recycler_item_tv);
        TextView tv_age = viewHolder.getView(R.id.activity_recycler_item_tv_age);
        TextView tv_type = viewHolder.getView(R.id.activity_recycler_item_tv_type);
        tv.setText(data.name);
        tv_age.setText("index:" + data.index);
        tv_type.setText("类型:" + data.type);
    }
});
// 添加第二种item类型(有几种类型,添加几个)
mAdapter.addItemViewDelegate(new ItemDelegate<Data>() {
    @Override
    public int getItemViewLayoutId() {
        return R.layout.activity_recycler_item2;
    }
    @Override
    public boolean isItemViewType(Data data, int dataPosition) {
        return data.type == 2;
    }
    @Override
    public void convert(ViewHolder viewHolder, Data data, int dataPosition) {
        TextView tv = viewHolder.getView(R.id.activity_recycler_item_tv);
        TextView tv_age = viewHolder.getView(R.id.activity_recycler_item_tv_age);
        TextView tv_type = viewHolder.getView(R.id.activity_recycler_item_tv_type);
        tv.setText(data.name);
        tv_age.setText("index:" + data.index);
        tv_type.setText("类型:" + data.type);
    }
});

Adapter - 单类型+侧滑菜单的Item

/**
 * 创建SimpleSwipeMenuAdapter
 * 泛型:数据的类型
 * 参数:1.上下文对象  2.recyclerview实例  3.item布局  4.数据集List  5.侧滑菜单需要的监听器
 */
SimpleSwipeMenuAdapter mAdapter = new SimpleSwipeMenuAdapter<Data>(R.layout.activity_recycler_item, datas, new OnSwipeMenuListener() {
    @Override
    public int[] getLeftMenuLayoutIds(int dataPosition) {
	// item左侧菜单(如不需要,return null即可)
	// 根据位置判断要显示的菜单布局及菜单项
        if (dataPosition == 3) {
	    // 返回一个数组new int[]{菜单布局,菜单项1,菜单项2,菜单项3}
            return new int[]{R.layout.activity_recycler_swipe_menu_right
                    , R.id.menu_1, R.id.menu_2, R.id.menu_3};
        }
        // 返回一个数组new int[]{菜单布局,菜单项1,菜单项2}
        return new int[]{R.layout.activity_recycler_swipe_menu_left
                , R.id.menu_1, R.id.menu_2};
    }
    @Override
    public int[] getRightMenuLayoutIds(int dataPosition) {
        // 右侧菜单同左侧菜单(如不需要,return null即可)
        return new int[]{R.layout.activity_recycler_swipe_menu_right
                , R.id.menu_1, R.id.menu_2, R.id.menu_3};
    }
    @Override
    public boolean onMenuItemClick(int dataPosition, int menuItemLayoutId, int direction) {
	// 菜单项点击回调
	// 函数参数:1.dataPosition列表的位置 2.menuItemLayoutId菜单项Id 3.左/右测菜单
        String dirStr = "左边菜单";
        if (direction == SwipeMenuView.RIGHT_DIRECTION) {
            dirStr = "右边菜单";
        }
        switch (menuItemLayoutId) {
            case R.id.menu_1:
                ToastUtils.show("position:" + dataPosition + dirStr + "的第一个menu");
                break;
            case R.id.menu_2:
                ToastUtils.show("position:" + dataPosition + dirStr + "的第二个menu");
                break;
            case R.id.menu_3:
                ToastUtils.show("position:" + dataPosition + dirStr + "的第三个menu");
                break;
        }
        return true;
    }
}) {
    @Override
    public void convert(ViewHolder viewHolder, Data item, int dataPosition) {
	// 刷新界面 viewHolder-控件集  item-数据  dataPosition-位置
    }
};

Adapter - 多类型+侧滑菜单的Item

/**
 * 创建MultiItemTypeSwipeMenuAdapter
 * 泛型:数据的类型
 * 参数:1.上下文对象  2.recyclerview实例  3.数据集List  4.侧滑菜单需要的监听器
 * 菜单创建和使用方式同上 : Adapter - 单类型+侧滑菜单的Item
 */
MultiItemTypeSwipeMenuAdapter mAdapter = new MultiItemTypeSwipeMenuAdapter(datas, new OnSwipeMenuListener() {
    @Override
    public int[] getLeftMenuLayoutIds(int dataPosition) {
        return new int[0];
    }
    @Override
    public int[] getRightMenuLayoutIds(int dataPosition) {
        return new int[0];
    }
    @Override
    public boolean onMenuItemClick(int dataPosition, int menuItemLayoutId, int direction) {
        return false;
    }
});
// 添加一种item类型(有几种类型,添加几个)
mAdapter.addItemViewDelegate(new ItemDelegate<Data>() {});
// 添加第二种item类型(有几种类型,添加几个)
mAdapter.addItemViewDelegate(new ItemDelegate<Data>() {});

Header、Footer视图

/*
 * 创建HeaderAndFooterWrapper
 * 参数:1.recyclerview实例  2.Adapter实例(链式Adapter,最底层为RecyclerView.Adapter,其次为自定义Wrapper)
 */
HeaderAndFooterWrapper mHeaderAndFooterWrapper = new HeaderAndFooterWrapper(rv, mAdapter);
// HeaderView1
TextView t1 = new TextView(this);
t1.setText("Header 1\n\nHeader 1");
// HeaderView2
TextView t2 = new TextView(this);
t2.setText("Header 2\n\nHeader 2");
// HeaderView添加到列表中
mHeaderAndFooterWrapper.addHeaderView(t1);
mHeaderAndFooterWrapper.addHeaderView(t2);
// FooterView1
TextView t3 = new TextView(this);
t3.setText("Footer 1\n\nFooter 1");
// FooterView2
TextView t4 = new TextView(this);
t4.setText("Footer 2\n\nFooter 2");
// FooterView添加到列表中
mHeaderAndFooterWrapper.addFooterView(t3);
mHeaderAndFooterWrapper.addFooterView(t4);

Empty视图

/*
 * 创建EmptyWrapper
 * 参数:1.recyclerview实例  2.Adapter实例(链式Adapter,最底层为RecyclerView.Adapter,其次为自定义Wrapper) 3.EmptyView 4.Empty视图点击事件监听器
 */
// EmptyView
TextView t6 = new TextView(this);
t6.setText("无数据,点击加载\n\n无数据,点击加载");
EmptyWrapper mEmptyWarpper = new EmptyWrapper(rv, mHeaderAndFooterWrapper, t6, new OnEmptyListener() {
    @Override
    public void onEmptyClick(View itemView) {
	// 点击回调
        loadMore(itemView);
    }
});

上拉加载更多

/*
 * 创建LoadMoreWrapper
 * 参数:1.recyclerview实例  2.Adapter实例(链式Adapter,最底层为RecyclerView.Adapter,其次为自定义Wrapper) 3.LoadMore视图点击事件监听器
 * 支持自定义LoadMore视图
 */
LoadMoreWrapper mLoadMoreWarpper = new LoadMoreWrapper(rv, mHeaderAndFooterWrapper, new OnLoadMoreListener() {
    @Override
    public void onLoadMore(View itemView) {
	// 上拉加载更多回调
        loadMore(itemView);
    }

    @Override
    public void onLoadMoreClick(View itemView) {
	// LoadMore单击回调
        loadMore(itemView);
    }
});
// 提前2条加载下一次数据
mLoadMoreWarpper.setRefreshBefore(2);

事件监听器(单击&长按 & 滑动删除 & 拖拽变换)

/*
 * 需创建RecyclerItemListener统一管理各个监听器
 */
RecyclerItemListener itemListener = new RecyclerItemListener(rv);
// 单击&长按 监听
itemListener.setItemCallback(new ItemCallback() {
    @Override
    public void onClick(RecyclerView.ViewHolder viewHolder, int dataPosition) {
	// 单击回调
        ToastUtils.show("item:" + dataPosition);
    }
    @Override
    public void onLongClick(RecyclerView.ViewHolder viewHolder, int dataPosition) {
	//长按回调
        ToastUtils.show("longClick:" + dataPosition);
    }
});
// 滑动删除 监听
itemListener.setItemSwipeCallback(new ItemSwipeCallback() {
    @Override
    public boolean onDeleteData(RecyclerView.ViewHolder viewHolder, int dataPosition, int viewPosition) {
	// 滑动删除回调,需手动处理数据
        datas.remove(dataPosition);
        // 返回值:true表示自动删掉item视图  false表示要手动处理视图
        return true;
    }
    @Override
    public boolean onDeleteCheck(RecyclerView.ViewHolder viewHolder, int dataPosition) {
	// 返回值:true表示此item项支持侧滑删除功能 false表示不支持侧滑删除
        if (dataPosition < 5) {
            return false;
        }
        return super.onDeleteCheck(viewHolder, dataPosition);
    }
});
// 拖拽变换 监听
 itemListener.setItemDragCallback(new ItemDragCallback() {
    @Override
    public boolean onTransformData(RecyclerView.ViewHolder fromViewHolder, RecyclerView.ViewHolder toViewHolder, int fromDataPosition, int toDataPosition, int fromViewPosition, int toViewPosition) {
	// 变换回调,需手动处理数据(变换数据位置)
        datas.add(toDataPosition, datas.remove(fromDataPosition));
        // 返回值:true表示自动删掉并添加item视图  false表示要手动处理视图
        return true;
    }
    @Override
    public boolean onTransformCheck(RecyclerView.ViewHolder viewHolder, int dataPosition) {
        // 返回值:true表示此item项支持长按拖动功能 false表示不支持长按拖动功能
        // 此回调为起始位回调,非被换位置的item项
        if (dataPosition < 2) {
            return false;
        }
        return super.onTransformCheck(viewHolder, dataPosition);
    }
    @Override
    public boolean onTransformToCheck(RecyclerView.ViewHolder viewHolder, int dataPosition) {
        // 返回值:true表示此item项允许被换位置 false表示不允许
        if (dataPosition < 2) {
            return false;
        }
        return super.onTransformToCheck(viewHolder, dataPosition);
    }
    @Override
    public void onSelectedStart(RecyclerView.ViewHolder viewHolder) {
        // 拖动开始生效时回调
        viewHolder.itemView.setBackgroundColor(Color.LTGRAY);
    }
    @Override
    public void onSelectedEnd(RecyclerView.ViewHolder viewHolder) {
        // 拖动结束时回调
        viewHolder.itemView.setBackgroundResource(0);
    }
});

分组 + 悬浮

/*
 * 创建分组回调监听
 */
GroupListener mGroupListener = new GroupListener() {
    /**
     * 获取GroupItem层级的数量
    */
    @Override
    public int getGroupItemLevelNum() {
        return 4;
    }
    /**
     * 判断GroupItem的视图类型是否大于一种(当Level等级大于1时,此值不在有效)
     */
    @Override
    public boolean isGroupItemTypeMoreOne() {
        return false;
    }
    /**
     * 设置Head是否自动与GroupItemView宽高同步
     */
    @Override
    public boolean isAutoSetGroupHeadViewWidthHeightByGroupItemView() {
        return false;
    }
    /**
     * 判断是否创建GroupItemView
     * @param dataPosition 定位数据的position
     */
    @Override
    public boolean isCreateGroupItemView(int dataPosition) {
        if (datas.get(dataPosition).type == 3) {
            return true;
        }
        return false;
    }
    /**
     * 获取GroupItemView视图
     * @param root         容器
     * @param groupLevel   分组层级(计数从0开始)
     * @param dataPosition 定位数据的position
     */
    @Override
    public View getGroupItemView(ViewGroup root, int groupLevel, int dataPosition) {
        View view = null;
        if (datas.get(dataPosition).type == 3) {
            if (dataPosition % 15 == 0) {
                switch (groupLevel) {
                    case 0:
                        view = LayoutInflater.from(MainActivity.this).inflate(R.layout.activity_recycler_group_item, root, false);
                        break;
                    case 1:
                        view = LayoutInflater.from(MainActivity.this).inflate(R.layout.activity_recycler_group_item2, root, false);
                        break;
                    case 2:
                        view = LayoutInflater.from(MainActivity.this).inflate(R.layout.activity_recycler_group_item3, root, false);
                        break;
                    case 3:
                        view = LayoutInflater.from(MainActivity.this).inflate(R.layout.activity_recycler_group_item4, root, false);
                        break;
                }
            } else {
                switch (groupLevel) {
                    case 1:
                        view = LayoutInflater.from(MainActivity.this).inflate(R.layout.activity_recycler_group_item2, root, false);
                        break;
                    case 2:
                        view = LayoutInflater.from(MainActivity.this).inflate(R.layout.activity_recycler_group_item3, root, false);
                        break;
                    case 3:
                        view = LayoutInflater.from(MainActivity.this).inflate(R.layout.activity_recycler_group_item4, root, false);
                        break;
                }
            }
        }
        return view;
    }
    /**
     * 更新GroupItemView视图
     * @param groupItemView 要更新的groupItemView
     * @param groupLevel    分组层级(计数从0开始)
     * @param dataPosition  定位数据的position
     */
    @Override
    public void changeGroupItemView(View groupItemView, int groupLevel, int dataPosition) {
        TextView tv = (TextView) groupItemView.findViewById(R.id.activity_recycler_group_item_tv_number);
        tv.setText("dataPosition:" + dataPosition + "  groupLevel:" + groupLevel);
    }
    /**
     * 获取GroupHeadView视图
     * * 大多数情况下与GroupItemView相同,可互相调用(保留此回调是为了当出现于GroupHeadView不同时,方便拓展)
     * @param root         容器
     * @param groupLevel   分组层级(计数从0开始)
     * @param dataPosition 定位数据的position
     */
    @Override
    public View getGroupHeadView(ViewGroup root, int groupLevel, int dataPosition) {
        return getGroupItemView(root, groupLevel, dataPosition);
    }
    /**
     * 更新GroupHeadView视图
     * * 大多数情况下与GroupItemView相同,可互相调用(保留此回调是为了当出现于GroupHeadView不同时,方便拓展)
     * @param groupHeadView 要更新的groupHeadView
     * @param groupLevel    分组层级(计数从0开始)
     * @param dataPosition  定位数据的position
     */
    @Override
    public void changeGroupHeadView(View groupHeadView, int groupLevel, int dataPosition) {
        changeGroupItemView(groupHeadView, groupLevel, dataPosition);
    }
};
/**
 * 创建分组Decoration并设置布局,添加到RecyclerView中,并绑定GroupListener
 */
rv.addItemDecoration(new GroupDecoration((GroupHeadLayout) findViewById(R.id.groupLayout), mGroupListener));
/**
 * 如果RecyclerView为GridLayoutManager或StaggeredGridLayoutManager,则必须设置
 * 兼容Group分组功能,网格或瀑布流,必须设置,否则无法支持Group功能
 */
mAdapter.setGroupListener(mGroupListener);

gitHub : https://github.com/AcmenXD 如对您有帮助,欢迎点Star支持,谢谢~

技术博客 : http://blog.csdn.net/wxd_beijing

END