android 二级列表拖动排序_Android RecyclerView 二级列表实现

本文介绍了如何在Android中使用RecyclerView实现二级列表,包括数据格式、RecyclerView的工作原理、ViewHolder的创建以及如何处理二级列表的展开和收起。通过自定义Adapter,实现了二级列表的拖动排序功能。
摘要由CSDN通过智能技术生成

[TOC]

Android RecyclerView 二级列表实现

2017.5.16 添加demo

git

简述

在开发 Android APP 的时候,难免会需要实现二级列表的情况,而在自己的项目中使用的列表是 android.support.v7.widget 包里面的 RecyclerView,好处是可以根据情况实现不同样式的列表,可扩展程度高。而坏处是什么都要自己实现。所以在想要用 RecyclerView 实现的二级列表的时候,却发现没有类似 ListView 的 ExpandableListView,只能自己去实现。

实现基础

在使用 RecyclerView 的时候,与 ListView 类似,需要创建一个 Adapter 去告诉 RecyclerView 如何工作RecyclerView 的 Adapter 的时候,一般需要重载以下几个方法:

onCreateViewHolder() 为每个项目创建 ViewHolder

onBindViewHolder() 处理每个 item

getItemViewType() 在 onCreateViewHolder 前调用,返回 item 类型

getItemCount() 获取 item 总数

加载 RecyclerView 的过程如下图:

flow.PNG

ps简书的MD不支持流程图?

除此之外,还需要创建一个 ViewHolder 用于寻找自定义 item的各个控件。

实现思路

根据上述,在实现二级列表的时候,我们在 onCreateViewHolder() 和 onBindViewHolder() 中,判断是加载一级项目(GroupItem)还是二级子项目(SubItem)。

实现过程

二级列表数据格式

一般来说,一个 GroupItem 下面有一个,或多个 SubItem,一对多。

在这里,用一个 DataTree 来封装这种数据格式,代码如下:

public class DataTree {

private K groupItem;

private List subItems;

public DataTree(K groupItem, List subItems) {

this.groupItem = groupItem;

this.subItems = subItems;

}

public K getGroupItem() {

return groupItem;

}

public List getSubItems() {

return subItems;

}

}

RecyclerView Item状态

ItemStatus 用封装列表每一项的状态,包括:

viewType item的类型,group item 还是 subitem

groupItemIndex 一级索引位置

subItemIndex 如果该 item 是一个二级子项目,则保存子项目索引

private static class ItemStatus {

public static final int VIEW_TYPE_GROUPITEM = 0;

public static final int VIEW_TYPE_SUBITEM = 1;

private int viewType;

private int groupItemIndex = 0;

private int subItemIndex = -1;

public ItemStatus() {

}

public int getViewType() {

return viewType;

}

public void setViewType(int viewType) {

this.viewType = viewType;

}

public int getGroupItemIndex() {

return groupItemIndex;

}

public void setGroupItemIndex(int groupItemIndex) {

this.groupItemIndex = groupItemIndex;

}

public int getSubItemIndex() {

return subItemIndex;

}

public void setSubItemIndex(int subItemIndex) {

this.subItemIndex = subItemIndex;

}

}

ViewHolder

这里的 groupItem 和 subItem 分别用不同的布局文件,所以我把 ViewHolder 分开写,如下:

public static class GroupItemViewHolder extends RecyclerView.ViewHolder {

...

public GroupItemViewHolder(View itemView) {

super(itemView);

...

}

}

public static class SubItemViewHolder extends RecyclerView.ViewHolder {

...

public SubItemViewHolder(View itemView) {

super(itemView);

...

}

}

其它属性和方法

context

list dataTrees 用于显示的数据

list groupItemStatus 保存 groupItem 状态,开还是关

//向外暴露设置显示数据的方法

public void setDataTrees(List> dt) {

this.dataTrees = dt;

initGroupItemStatus(groupItemStatus);

notifyDataSetChanged();

}

//设置初始值,所有 groupItem 默认为关闭状态

private void initGroupItemStatus(List l) {

for (int i = 0; i < dataTrees.size(); i++) {

l.add(false);

}

}

getItemStatusByPosition() 方法实现

顾名思义,用于根据 position 来计算判断该 item 的状态,返回一个 ItemStatus。

position

代码如下:

private ItemStatus getItemStatusByPosition(int position) {

ItemStatus itemStatus = new ItemStatus();

int count = 0; //计算groupItemIndex = i 时,position最大值

int i = 0;

//轮询 groupItem 的开关状态

for (i = 0; i < groupItemStatus.size(); i++ ) {

//pos刚好等于计数时,item为groupItem

if (count == position) {

itemStatus.setViewType(ItemStatus.VIEW_TYPE_GROUPITEM);

itemStatus.setGroupItemIndex(i);

break;

//pos大于计数时,item为groupItem(i - 1)中的某个subItem

} else if (count > position) {

itemStatus.setViewType(ItemStatus.VIEW_TYPE_SUBITEM);

itemStatus.setGroupItemIndex(i - 1);

itemStatus.setSubItemIndex(position - ( count - dataTrees.get(i - 1).getSubItems().size() ) );

break;

}

//无论groupItem状态是开或者关,它在列表中都会存在,所有count++

count++;

//当轮询到的groupItem的状态为“开”的话,count需要加上该groupItem下面的子项目数目

if (groupItemStatus.get(i)) {

count += dataTrees.get(i).getSubItems().size();

}

}

//简单地处理当轮询到最后一项groupItem的时候

if (i >= groupItemStatus.size()) {

itemStatus.setGroupItemIndex(i - 1);

itemStatus.setViewType(ItemStatus.VIEW_TYPE_SUBITEM);

itemStatus.setSubItemIndex(position - ( count - dataTrees.get(i - 1).getSubItems().size() ) );

}

return itemStatus;

}

getItemCount()方法实现

该方法在显示列表的时候会执行多次,如果返回的项目计数不正确的话程序会出现错误奔溃,代码如下:

@Override

public int getItemCount() {

Logger.i("1");

int itemCount = 0;

if (groupItemStatus.size() == 0) {

return 0;

}

for (int i = 0; i < dataTrees.size(); i++) {

if (groupItemStatus.get(i)) {

itemCount += dataTrees.get(i).getSubItems().size() + 1;

} else {

itemCount++;

}

}

return itemCount;

}

其它方法实现

getItemViewType()

该方法会在 onCreateViewHolder() 前执行,并返回 int viewType,代码如下:

@Override

public int getItemViewType(int position) {

return getItemStatusByPosition(position).getViewType();

}

onCreateViewHolder()

根据不同的由 getItemViewType() 返回的 viewType 选择不同的项目布局,代码如下:

@Override

public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

View v;

RecyclerView.ViewHolder viewHolder = null;

if (viewType == ItemStatus.VIEW_TYPE_GROUPITEM) {

v = LayoutInflater.from(parent.getContext()).inflate(R.layout

.item_artist_detail_album, parent, false);

viewHolder = new GroupItemViewHolder(v);

} else if (viewType == ItemStatus.VIEW_TYPE_SUBITEM) {

v = LayoutInflater.from(parent.getContext()).inflate(R.layout

.item_artist_detail_track, parent, false);

viewHolder = new SubItemViewHolder(v);

}

return viewHolder;

}

onBindViewHolder()

根据不同的 viewType 绑定不同的 ViewHolder ,代码如下:

@Override

public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

final ItemStatus itemStatus = getItemStatusByPosition(position);

final DataTree dt = dataTrees.get(itemStatus.getGroupItemIndex());

if ( itemStatus.getViewType() == ItemStatus.VIEW_TYPE_GROUPITEM ) {

final GroupItemViewHolder groupItemVh = (GroupItemViewHolder) holder;

. . . //加载groupItem,处理groupItem控件

groupItemVh.itemView.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

int groupItemIndex = itemStatus.getGroupItemIndex();

if ( !groupItemStatus.get(groupItemIndex) ) {

. . . //groupItem由“关闭”状态到“打开”状态

groupItemStatus.set(groupItemIndex, true);

notifyItemRangeInserted(groupItemVh.getAdapterPosition() + 1, dt.getSubItems().size());

} else {

. . . //groupItem由“打开”状态到“关闭”状态

groupItemStatus.set(groupItemIndex, false);

notifyItemRangeRemoved(groupItemVh.getAdapterPosition() + 1, dt.getSubItems().size());

}

}

});

} else if (itemStatus.getViewType() == ItemStatus.VIEW_TYPE_SUBITEM) {

SubItemViewHolder subItemVh = (SubItemViewHolder) holder;

. . . //加载subItem,处理subItem控件

subItemVh.itemView.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

. . . //点击subItem处理

}

});

}

}

总结

二级列表对 RecyclerView 小小地扩展,在实现的过程中,有点麻烦的地方是对特定的 item 进行识别,即 getItemViewType() 的实现,在这里,我简单地用遍历轮询的方法去判断,应该会有更简单更节省资源的方法。实现二级列表之后,理论上可以实现多级列表,可以试试。

在用这个方法实现之前,有尝试过有 View 提供的方法—— View.setTag() ,但是由于 RecyclerView 的加载机制,当列表被划出界面时,会被销毁,而重新划进来显示时,RecyclerView 会重新创建新的 item,而不是之前的那个,所以,之前的 Tag 会不见,最后没能实现出来,感兴趣的同学可以试试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值