最快的Android TreeView出现了!

简介: 最快的Android TreeView出现了!

最快的Android TreeView出现了!

源码地址:https://github.com/niugao/RecyclerListTreeView

  • 基于RecyclerView实现。
  • 存储数据的结构并不是Tree,而是一个ArrayList。与所有已知的网上的实现都不一样,大家似乎都跳不出固定思维。 可以比较一下代码量,此实现比其它的少一半都不止。
  • 核心是一个表示Tree的类,但它的本质是一个List。对RecyclerView没有任何改动,对Adapter只有少量封装, 使用者不会产生任何陌生感。也就是说你对RecyclerView能做的,现在依然能做。
  • 以List的形式表式树,带来很多好处:
    -- 没有了递归。该用递归的地方全部变成了循环(Tree不论有多少层都没有栈溢出)。

-- 其次是有序,插入节点时,可以指定它是它爸爸的第几个儿子。
-- 极其适合在RecyclerView中使用。
-- 跟List无异,无论根节点还是子节点都对应RecyclerView中的一行。
-- 不需对RecyclerView做任何改动。


有诗为证

远看像棵树
近看不是树
似树而非树
是为牛逼树

示例

Adapter:

public class ExampleListTreeAdapter extends
        ListTreeAdapter<ExampleListTreeAdapter.BaseViewHolder> {

    //行上弹出菜单的侦听器
    private PopupMenu.OnMenuItemClickListener itemMenuClickListener;
    //记录弹出菜单是在哪个行上出现的
    private ListTree.TreeNode currentNode;

    //保存子行信息的类
    public static class ContactInfo{
        //头像,用于设置给ImageView。
        private Bitmap bitmap;
        //标题
        private String title;
        //描述
        private String detail;

        public ContactInfo(Bitmap bitmap, String title, String detail) {
            this.bitmap = bitmap;
            this.title = title;
            this.detail = detail;
        }

        public Bitmap getBitmap() {
            return bitmap;
        }

        public String getTitle() {
            return title;
        }

        public String getDetail() {
            return detail;
        }
    }

    //构造方法
    public ExampleListTreeAdapter(ListTree tree,
                                  PopupMenu.OnMenuItemClickListener listener){
        super(tree);
        this.itemMenuClickListener=listener;
    }

    public ListTree.TreeNode getCurrentNode() {
        return currentNode;
    }

    @Override
    protected BaseViewHolder onCreateNodeView(ViewGroup parent, int viewType){
        View view = null;
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        BaseViewHolder vh;
        //创建不同的行View
        if(viewType==R.layout.contacts_group_item){
            //最后一个参数必须传true
            view = inflater.inflate(viewType,parent,true);
            vh=new GroupViewHolder(view);
        }else if(viewType == R.layout.contacts_contact_item){
            view = inflater.inflate(viewType,parent,true);
            vh=new ContactViewHolder(view);
        }else{
            return null;
        }

        return vh;

    }

    @Override
    protected void onBindNodeViewHolder(BaseViewHolder holder, int position) {
        View view = holder.itemView;
        //get node at the position
        ListTree.TreeNode node = tree.getNodeByPlaneIndex(position);

        if(node.getLayoutResId() == R.layout.contacts_group_item){
            //group node
            String title = (String)node.getData();

            GroupViewHolder gvh= (GroupViewHolder) holder;
            gvh.textViewTitle.setText(title);
            gvh.textViewCount.setText("0/"+node.getChildrenCount());
            gvh.aSwitch.setChecked(node.isChecked());

            gvh.aSwitch.setTag(node);
            gvh.textViewMenu.setTag(node);
        }else if(node.getLayoutResId() == R.layout.contacts_contact_item){
            //child node
            ContactInfo info = (ContactInfo) node.getData();

            ContactViewHolder cvh= (ContactViewHolder) holder;
            cvh.imageViewHead.setImageBitmap(info.getBitmap());
            cvh.textViewTitle.setText(info.getTitle());
            cvh.textViewDetail.setText(info.getDetail());
            cvh.aSwitch.setChecked(node.isChecked());

            cvh.aSwitch.setTag(node);
        }
    }

    //组行和联系人行的Holder基类
    class BaseViewHolder extends ListTreeViewHolder{
        public BaseViewHolder(View itemView) {
            super(itemView);
        }
    }

    //将ViewHolder声明为Adapter的内部类,反正外面也用不到
    class GroupViewHolder extends BaseViewHolder {

        TextView textViewTitle;
        TextView textViewCount;
        Switch aSwitch;
        TextView textViewMenu;

        public GroupViewHolder(View itemView) {
            super(itemView);

            textViewTitle = itemView.findViewById(R.id.textViewTitle);
            textViewCount = itemView.findViewById(R.id.textViewCount);
            aSwitch = itemView.findViewById(R.id.switchChecked);
            textViewMenu = itemView.findViewById(R.id.textViewMenu);

            //应响应点击事件而不是CheckedChange事件,因为那样会引起事件的递归触发
            aSwitch.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    ListTree.TreeNode node = (ListTree.TreeNode) view.getTag();
                    node.setChecked(!node.isChecked());
                    int planeIndex = tree.getNodePlaneIndex(node);
                    //改变所有的子孙们的状态
                    int count =tree.setDescendantChecked(planeIndex,node.isChecked());
                    notifyItemRangeChanged(planeIndex,count+1);
                }
            });

            //点了PopMenu控件,弹出PopMenu
            textViewMenu.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    ListTree.TreeNode node = (ListTree.TreeNode) v.getTag();
                    currentNode=node;
                    PopupMenu popup = new PopupMenu(v.getContext(), v);
                    popup.setOnMenuItemClickListener(itemMenuClickListener);
                    MenuInflater inflater = popup.getMenuInflater();
                    inflater.inflate(R.menu.menu_item, popup.getMenu());
                    popup.show();
                }
            });
        }
    }

    class ContactViewHolder extends BaseViewHolder{
        ImageView imageViewHead;
        TextView textViewTitle;
        TextView textViewDetail;
        Switch aSwitch;

        public ContactViewHolder(View itemView) {
            super(itemView);

            imageViewHead = itemView.findViewById(R.id.imageViewHead);
            textViewTitle = itemView.findViewById(R.id.textViewTitle);
            textViewDetail = itemView.findViewById(R.id.textViewDetail);
            aSwitch = itemView.findViewById(R.id.switchChecked);

            //应响应点击事件而不是CheckedChange事件,因为那样会引起事件的递归触发
            aSwitch.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    ListTree.TreeNode node = (ListTree.TreeNode) view.getTag();
                    node.setChecked(!node.isChecked());
                    int planeIndex = tree.getNodePlaneIndex(node);
                    //改变所有的子孙们的状态
                    int count =tree.setDescendantChecked(planeIndex,node.isChecked());
                    notifyItemRangeChanged(planeIndex,count+1);
                }
            });
        }
    }
}

使用Adapter

public class MainActivity extends AppCompatActivity
        implements PopupMenu.OnMenuItemClickListener {

    //保存数据的集合
    private ListTree tree=new ListTree();
    //从ListTreeAdapter派生的Adapter
    ExampleListTreeAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        //使用Android原生的RecyclerView即可
        RecyclerView listView = findViewById(R.id.listview);

        //创建后台数据:一棵树
        //创建组们,是root node,所有parent为null
        ListTree.TreeNode groupNode1=tree.addNode(null,"特别关心", R.layout.contacts_group_item);
        ListTree.TreeNode groupNode2=tree.addNode(null,"我的好友", R.layout.contacts_group_item);
        ListTree.TreeNode groupNode3=tree.addNode(null,"朋友", R.layout.contacts_group_item);
        ListTree.TreeNode groupNode4=tree.addNode(null,"家人", R.layout.contacts_group_item);
        ListTree.TreeNode groupNode5=tree.addNode(null,"同学", R.layout.contacts_group_item);

        //第二层
        ExampleListTreeAdapter.ContactInfo contact;
        Bitmap bitmap= BitmapFactory.decodeResource(getResources(), R.drawable.contacts_normal);
        contact = new ExampleListTreeAdapter.ContactInfo(bitmap,"王二","[在线]我是王二");
        ListTree.TreeNode contactNode1=tree.addNode(groupNode2,contact,R.layout.contacts_contact_item);
        ListTree.TreeNode contactNode2=tree.addNode(groupNode5,contact,R.layout.contacts_contact_item);
        //再添加一个
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.contacts_normal);
        contact=new ExampleListTreeAdapter.ContactInfo(bitmap,"王三","[离线]我没有状态");
        tree.addNode(groupNode2,contact,R.layout.contacts_contact_item);
        tree.addNode(groupNode5,contact,R.layout.contacts_contact_item);

        //第三层
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.contacts_normal);
        contact=new ExampleListTreeAdapter.ContactInfo(bitmap,"东邪","[离线]出来还价");
        ListTree.TreeNode n=tree.addNode(contactNode1,contact,R.layout.contacts_contact_item);
        n.setShowExpandIcon(false);
        //再添加一个
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.contacts_normal);
        contact=new ExampleListTreeAdapter.ContactInfo(bitmap,"李圆圆","[离线]昨天出门没出去");
        n=tree.addNode(contactNode1,contact,R.layout.contacts_contact_item);
        n.setShowExpandIcon(false);

        adapter=new ExampleListTreeAdapter(tree,this);
        listView.setLayoutManager(new LinearLayoutManager(this));
        listView.setAdapter(adapter);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }else if(id == R.id.action_del_selected){
            //删除选中的Nodes,删一个Node时会将其子孙一起删掉
            tree.removeCheckedNodes();
            adapter.notifyDataSetChanged();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public boolean onMenuItemClick(MenuItem item){
        switch (item.getItemId()) {
            case R.id.action_add_item:
                //向当前行增加一个儿子
                ListTree.TreeNode node = adapter.getCurrentNode();
                Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.contacts_normal);
                ExampleListTreeAdapter.ContactInfo contact=new ExampleListTreeAdapter.ContactInfo(
                        bitmap,"New contact","[离线]我没有状态");
                ListTree.TreeNode childNode = tree.addNode(node,contact,R.layout.contacts_contact_item);
                adapter.notifyTreeItemInserted(node,childNode);
                return true;
            default:
                return false;
        }
    }
}

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

目录
相关文章
|
2月前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
2月前
|
缓存 前端开发 Android开发
安卓开发中的自定义视图:从零到英雄
【10月更文挑战第42天】 在安卓的世界里,自定义视图是一块画布,让开发者能够绘制出独一无二的界面体验。本文将带你走进自定义视图的大门,通过深入浅出的方式,让你从零基础到能够独立设计并实现复杂的自定义组件。我们将探索自定义视图的核心概念、实现步骤,以及如何优化你的视图以提高性能和兼容性。准备好了吗?让我们开始这段创造性的旅程吧!
30 1
|
2月前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
29天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
53 19
|
2月前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
29天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
58 14
|
1月前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
|
30天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
在数字时代,掌握安卓应用开发技能是进入IT行业的关键。本文将引导读者从零基础开始,逐步深入安卓开发的世界,通过实际案例和代码示例,展示如何构建自己的第一个安卓应用。我们将探讨基本概念、开发工具设置、用户界面设计、数据处理以及发布应用的全过程。无论你是编程新手还是有一定基础的开发者,这篇文章都将为你提供宝贵的知识和技能,帮助你在安卓开发的道路上迈出坚实的步伐。
34 5
|
29天前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
|
30天前
|
XML 搜索推荐 前端开发
安卓开发中的自定义视图:打造个性化UI组件
在安卓应用开发中,自定义视图是一种强大的工具,它允许开发者创造独一无二的用户界面元素,从而提升应用的外观和用户体验。本文将通过一个简单的自定义视图示例,引导你了解如何在安卓项目中实现自定义组件,并探讨其背后的技术原理。我们将从基础的View类讲起,逐步深入到绘图、事件处理以及性能优化等方面。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。