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

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
ScrollView滑动—仿微博主页标题栏渐变悬浮及Fragment实现多个内容页面切换
作为一名热爱学习的Android开发工程si,刷微博的时候居然还想着技术呢,觉得自己也是够够了........哈哈哈 image.png 进入今天的正题,微博主页大家肯定是看过的,先看一下微博的效果。 (小提示:该Demo是采用kotlin语言编写的,需要配置Kotlin开发环境哦!) wb.gif 微博的效果大家都看到了,先看看这标题栏悬停的效果。实现方式很多种,我的思路很简单:顶部有一个默认隐藏的标题栏在上面,然后通过计算ScrollView向上滑动的距离,动态控制头部标题导航栏的显示隐藏。 简单分析一下页面布局的结构: image.png 页面布局代码: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/parent_view" android:layout_width="match_parent" android:layout_height="matc...
- 下一篇
Android Crash的跟踪方法,使用腾讯Bugly来捕捉一些疑难杂症,让我们APP稳定上线
Android Crash的跟踪方法,使用腾讯Bugly来捕捉一些疑难杂症,让我们APP稳定上线 我们在开发中常常会注意到一些Crash,这正是很头疼的,而且Crash会带来很多意想不到的状态,很恶心,所以今天,我们来着重研究一下Crash,同时,我们也将使用第三方SDK,也就是腾讯的Bugly来扑捉这些Crash,让他无所遁形。 一.糟糕的Android Crash 作为开发人员,很容易就能碰到Crash,但是作为用户,我相信也有很大部分人碰到过Crash,这些原因,也正是Android的平台差异化和适配所导致的,而且,你要是你安装一个软件就Crash,我想你会立马把他卸载掉。同时心理嘲笑了一下开发人员,有些用户还有可能会去商店给你差评之类的,这就不多说了,反正会直接影响到你APP的口碑和市场竞争能力,你要是一些用户很多的APP的话,你还得考虑用户留存和收入的问题了 而在以前,我们是怎么处理Crash的呢?一般有三个补救措施吧,感觉,当然,还有其他,待补充: 1.等待用户反馈,这样很被动 2.了解出错原因,让用户提供日志和使用场景,不靠谱,攒人品吧 3.提示用户反馈,这样对用户体验有...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS6,CentOS7官方镜像安装Oracle11G