Android可折叠收缩伸展的Expandable分组RecyclerView:模型抽象和封装(二)

简介: Android可折叠收缩伸展的Expandable分组RecyclerView:模型抽象和封装(二)如今Android这种可收缩伸展的Expandable RecyclerView是如此常用,在附录1的基础上,我重新把模型进行了抽象和封装,设计了一套新的架构和简洁的使用方式支持这种功能。
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 

相关文章
|
2月前
|
Android开发 开发者 索引
Android实战经验之如何使用DiffUtil提升RecyclerView的刷新性能
本文介绍如何使用 `DiffUtil` 实现 `RecyclerView` 数据集的高效更新,避免不必要的全局刷新,尤其适用于处理大量数据场景。通过定义 `DiffUtil.Callback`、计算差异并应用到适配器,可以显著提升性能。同时,文章还列举了常见错误及原因,帮助开发者避免陷阱。
180 9
|
2月前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
67 8
|
3月前
|
开发者 算法 虚拟化
惊爆!Uno Platform 调试与性能分析终极攻略,从工具运用到代码优化,带你攻克开发难题成就完美应用
【8月更文挑战第31天】在 Uno Platform 中,调试可通过 Visual Studio 设置断点和逐步执行代码实现,同时浏览器开发者工具有助于 Web 版本调试。性能分析则利用 Visual Studio 的性能分析器检查 CPU 和内存使用情况,还可通过记录时间戳进行简单分析。优化性能涉及代码逻辑优化、资源管理和用户界面简化,综合利用平台提供的工具和技术,确保应用高效稳定运行。
83 0
|
3月前
|
Android开发 iOS开发
Android项目架构设计问题之将隐式跳转的逻辑进行抽象和封装如何解决
Android项目架构设计问题之将隐式跳转的逻辑进行抽象和封装如何解决
41 0
|
3月前
|
存储 Android开发 开发者
Android项目架构设计问题之定义RecyclerView的ViewHolder如何解决
Android项目架构设计问题之定义RecyclerView的ViewHolder如何解决
43 0
|
3月前
|
数据可视化 Java 数据挖掘
Android项目架构设计问题之设置RecyclerView的LayoutManager如何解决
Android项目架构设计问题之设置RecyclerView的LayoutManager如何解决
34 0
|
3月前
|
编解码 API 开发工具
Android平台轻量级RTSP服务模块二次封装版调用说明
本文介绍了Android平台上轻量级RTSP服务模块的二次封装实践,旨在简化开发流程,让开发者能更专注于业务逻辑。通过`LibPublisherWrapper`类提供的API,可在应用中轻松初始化RTSP服务、配置视频参数(如分辨率、编码类型)、启动与停止RTSP服务及流发布,并获取RTSP会话数量。此外,还展示了如何处理音频和视频数据的采集与推送。最后,文章提供了从启动服务到销毁资源的完整示例,帮助开发者快速集成实时流媒体功能。
|
5月前
|
API Android开发 开发者
`RecyclerView`是Android API 21引入的UI组件,用于替代ListView和GridView
【6月更文挑战第26天】`RecyclerView`是Android API 21引入的UI组件,用于替代ListView和GridView。它提供高效的数据视图复用,优化的布局管理,支持多种布局(如线性、网格),并解耦数据、适配器和视图。RecyclerView的灵活性、性能(如局部刷新和动画支持)和扩展性使其成为现代Android开发的首选,特别是在处理大规模数据集时。
64 2
|
5月前
|
XML Java Android开发
Android RecyclerView用代码动态设置item的selector
Android RecyclerView用代码动态设置item的selector
41 0
|
6天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。