Android可折叠收缩伸展的Expandable分组RecyclerView:模型抽象和封装(二)
如今Android这种可收缩伸展的Expandable RecyclerView是如此常用,在附录1的基础上,我重新把模型进行了抽象和封装,设计了一套新的架构和简洁的使用方式支持这种功能。重点就是ExpandableItemAdapter类,可直接把RecyclerView的Adapter直接继承ExpandableItemAdapter,然后在建立数据模型时候,把具有“组”意义的数据添加子Item时候,调用addSubItem,这样该RecyclerView就具有伸缩和展开的功能。
每一个Item内部存储了一个isExpand值,isExpand值决定在ExpandableItemAdapter初始后每一组是否伸展和收缩。isExpand为true时候,展开,为false时候,收起。
以下是全部代码。
Item.java:
package app.zhangphil.exp;
import java.util.ArrayList;
import java.util.UUID;
public abstract class Item {
/**
* 上层用户可以把此id作为Item的标识。
*/
public int id;
public int position;
public boolean isExpand = true;
/**
* 当添加子Item时候,需要把当前的uniqueId作为子Item的parentUniqueId记录。
*
* 注意:虽然是public属性,但上层用户不应该使用uniqueId和parentUniqueId寻找和比对Item。
* uniqueId和parentUniqueId仅用来处理底层的数据逻辑。
*/
public String parentUniqueId = null;
public final String uniqueId = UUID.randomUUID().toString();
private ArrayList<Item> items = new ArrayList<>();
public abstract int getType();
/**
* 添加该Item的子Item
*
* @param item
*/
public void addSubItem(Item item) {
item.parentUniqueId = uniqueId;
items.add(item);
}
public void clearSubItems(){
items.clear();
}
public ArrayList<Item> getSubItems() {
return items;
}
}
该Item类是RecyclerView装载的条目的数据抽象模型,开发者的数据对象要继承自该Item,并重写返回类型。addSubItem将(递归)添加二级(子)Item。ItemVH.java:
package app.zhangphil.exp;
import android.support.v7.widget.RecyclerView;
import android.view.View;
public abstract class ItemVH extends RecyclerView.ViewHolder {
public ItemVH(View itemView) {
super(itemView);
}
public abstract int getType();
}
ItemVH继承自RecyclerView.ViewHolder,本质是一个ViewHolder。ItemAdapter.java:
package app.zhangphil.exp;
import android.support.v7.widget.RecyclerView;
import java.util.ArrayList;
public abstract class ItemAdapter extends RecyclerView.Adapter<ItemVH> {
private ArrayList<Item> mItems;
public ItemAdapter() {
mItems = new ArrayList<>();
}
@Override
public int getItemCount() {
return mItems.size();
}
@Override
public int getItemViewType(int position) {
return mItems.get(position).getType();
}
public Item getItem(int pos) {
return mItems.get(pos);
}
public void addItem(Item item) {
mItems.add(item);
}
public void addAll(ArrayList<Item> lists) {
mItems.addAll(lists);
}
public ArrayList<Item> getAllItem() {
return mItems;
}
/**
* 清空所有Item。
*/
public void clear() {
mItems.clear();
}
/**
* 寻找具有特定type和id的Item。
*
* @param type
* @param id
* @return
*/
public Item getItem(int type, int id) {
Item mItem = null;
for (int i = 0; i < mItems.size(); i++) {
Item item = mItems.get(i);
if (item.getType() == type && item.id == id) {
mItem = item;
}
}
return mItem;
}
}
ItemAdapter继承自RecyclerView.Adapter,做了简单的初始化和抽象。最重要的是ExpandableItemAdapter,ExpandableItemAdapter实现了全部的展开和折叠Item的功能。ExpandableItemAdapter是可以直接拿来作为RecyclerView的Adapter使用,开发者可以直接继承ExpandableItemAdapter以实现RecyclerView分组的折叠和展开功能。
ExpandableItemAdapter.java:
package app.zhangphil.exp;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.ViewGroup;
import java.util.Iterator;
public abstract class ExpandableItemAdapter extends ItemAdapter {
@NonNull
@Override
public abstract ItemVH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
@Override
public abstract void onBindViewHolder(@NonNull ItemVH holder, int position);
/**
* 展开Item
*
* @param item
*/
public void expand(Item item) {
int pos = -1;
for (int i = 0; i < getItemCount(); i++) {
Item it = getItem(i);
if (TextUtils.equals(it.uniqueId, item.uniqueId)) {
pos = i;
}
}
getAllItem().set(pos, item);
Iterator<Item> iterator = getAllItem().iterator();
while (iterator.hasNext()) {
Item it = iterator.next();
if (TextUtils.equals(it.parentUniqueId, item.uniqueId)) {
iterator.remove();
}
}
if (item.getSubItems().size() > 0) {
getAllItem().addAll(pos + 1, item.getSubItems());
}
item.isExpand = true;
if (mExpandableToggleListener != null) {
mExpandableToggleListener.onExpand(item);
}
}
/**
* 收起Item
*
* @param item
*/
public void collapse(Item item) {
Iterator<Item> iterator = getAllItem().iterator();
while (iterator.hasNext()) {
Item it = iterator.next();
if (TextUtils.equals(it.parentUniqueId, item.uniqueId)) {
iterator.remove();
}
}
item.isExpand = false;
if (mExpandableToggleListener != null) {
mExpandableToggleListener.onCollapse(item);
}
}
/**
* 更新Item
*
* @param item
*/
public void setItem(Item item) {
int pos = -1;
for (int i = 0; i < getItemCount(); i++) {
Item it = getItem(i);
if (TextUtils.equals(it.uniqueId, item.uniqueId)) {
pos = i;
}
}
getAllItem().set(pos, item);
Iterator<Item> iterator = getAllItem().iterator();
while (iterator.hasNext()) {
Item it = iterator.next();
if (TextUtils.equals(it.parentUniqueId, item.uniqueId)) {
iterator.remove();
}
}
if (item.isExpand) {
if (item.getSubItems().size() > 0) {
getAllItem().addAll(pos + 1, item.getSubItems());
}
} else {
}
}
/**
* 添加一个Item,该Item可以是一个普通的Item,也可以是一个包含子Item的item。
*
* @param item
*/
@Override
public void addItem(Item item) {
super.addItem(item);
if (item.isExpand) {
for (int i = 0; i < item.getSubItems().size(); i++) {
super.addItem(item.getSubItems().get(i));
}
} else {
}
}
public interface ExpandableToggleListener {
public void onExpand(Item item);
public void onCollapse(Item item);
}
private ExpandableToggleListener mExpandableToggleListener = null;
public void setExpandableToggleListener(ExpandableToggleListener listener) {
this.mExpandableToggleListener = listener;
}
public void toggle(Item item) {
item.isExpand = !item.isExpand;
if (item.isExpand) {
expand(item);
} else {
collapse(item);
}
}
}
现给出一个使用例子:
package app.zhangphil.exp;
import android.graphics.Color;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private final int TYPE_GROUP = 0xfa01;
private final int TYPE_CHILD = 0xfa02;
private RecyclerViewAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = findViewById(R.id.recycler_view);
LinearLayoutManager layoutManage = new LinearLayoutManager(this);
layoutManage.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManage);
mAdapter = new RecyclerViewAdapter();
recyclerView.setAdapter(mAdapter);
String[] groupNames = {"A", "B", "C", "D", "E", "F", "G"};
for (int i = 0; i < groupNames.length; i++) {
Group group = new Group();
group.id = i;
if (i == 0)
group.isExpand = true;
else {
group.isExpand = false;
}
group.title = groupNames[i];
int count = (int) (Math.random() * 10) % 5 + 1;
for (int j = 0; j < count; j++) {
Child child = new Child();
child.position = j;
child.group = group;
group.addSubItem(child);
}
mAdapter.addItem(group);
}
mAdapter.setExpandableToggleListener(new ExpandableItemAdapter.ExpandableToggleListener() {
@Override
public void onExpand(Item item) {
Toast.makeText(getApplicationContext(), ((Group) item).title + " 展开", Toast.LENGTH_SHORT).show();
}
@Override
public void onCollapse(Item item) {
Toast.makeText(getApplicationContext(), ((Group) item).title + " 收起", Toast.LENGTH_SHORT).show();
}
});
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//更新第0组
Group mGroup = (Group) mAdapter.getItem(0);
mGroup.clearSubItems();
mGroup.title = "zhangphil";
Child child = new Child();
child.group = mGroup;
child.position = 2018;
mGroup.addSubItem(child);
mAdapter.setItem(mGroup);
mAdapter.notifyDataSetChanged();
}
}, 3000);
}
public class RecyclerViewAdapter extends ExpandableItemAdapter {
@Override
public ItemVH onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
ItemVH itemVH = null;
switch (viewType) {
case TYPE_GROUP:
view = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false);
itemVH = new GroupVH(view);
break;
case TYPE_CHILD:
view = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_2, parent, false);
itemVH = new ChildVH(view);
break;
}
return itemVH;
}
@Override
public void onBindViewHolder(ItemVH holder, int position) {
Item item = getItem(position);
switch (getItemViewType(position)) {
case TYPE_GROUP:
final Group g = (Group) item;
GroupVH groupVH = (GroupVH) holder;
groupVH.text.setText(g.title);
groupVH.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
toggle(g);
notifyDataSetChanged();
}
});
break;
case TYPE_CHILD:
Child c = (Child) item;
ChildVH childVH = (ChildVH) holder;
childVH.text1.setText(c.group.title);
childVH.text2.setText(c.position + "");
break;
}
}
}
private class Group extends Item {
public String title;
@Override
public int getType() {
return TYPE_GROUP;
}
}
private class Child extends Item {
public Group group;
@Override
public int getType() {
return TYPE_CHILD;
}
}
private class GroupVH extends ItemVH {
public TextView text;
public GroupVH(View itemView) {
super(itemView);
text = itemView.findViewById(android.R.id.text1);
text.setBackgroundColor(Color.RED);
}
@Override
public int getType() {
return TYPE_GROUP;
}
}
private class ChildVH extends ItemVH {
public TextView text1;
public TextView text2;
public ChildVH(View itemView) {
super(itemView);
text1 = itemView.findViewById(android.R.id.text1);
text2 = itemView.findViewById(android.R.id.text2);
text1.setTextColor(Color.LTGRAY);
text2.setTextColor(Color.BLUE);
}
@Override
public int getType() {
return TYPE_CHILD;
}
}
}
初始后,由于第0个Group的isExpand为true,其他Group均为fasle,所以展开第0组,折叠其他组:
过了三秒,更新了第0组全部数据。
伸展或折叠其他组:
项目代码和库已经上传到github上:https://github.com/zhangphil/recyclerview
附录:
1,《Android可收缩伸展的Expandable分组RecyclerView》链接:https://blog.csdn.net/zhangphil/article/details/79814332