flutter入门之实现展示机构树的功能
【原创不易,转载请注明出处:https://blog.csdn.net/email_jade/article/details/86528143】
在日常开发中,我们经常会遇到展示机构树的应用场景,比如说展示某公司的组织架构,或者是展示某一个目录的结构,在flutter中,暂时还没有发现类似的开源库,那么只能自己动手撸一个了,先展示下效果图:
数据结构如下,每个机构分为两类,一类是子机构,另一类是机构成员,都有name属性,这也是日常应用中最常见的形式:
class Organ{List<Organ> subOrgans;List<Member> members;String name;Organ(this.subOrgans,this.members,this.name);
}class Member{String name;Member(this.name);
}
测试数据如下,平时我们遇到的大多数情况是顶级机构只有一个节点, 为了测试顶级下面可以挂多个机构的情况,将数据改造了下:
List<Organ> _buildData(){return [Organ([Organ([Organ([Organ([Organ(null,[Member("五级机构成员1"),Member("五级机构成员2"),Member("五级机构成员3"),Member("五级机构成员4"),],"五级机构")], [Member("四级机构成员1"),Member("四级机构成员2"),Member("四级机构成员3"),Member("四级机构成员4"),],"四级机构"),Organ([Organ(null,[Member("六级机构成员1"),Member("六级机构成员2"),Member("六级机构成员3"),Member("六级机构成员4"),],"六级机构")], [Member("七级机构成员1"),Member("七级机构成员2"),Member("七级机构成员3"),Member("七级机构成员4"),],"七级机构")], [Member("三级机构成员1"),Member("三级机构成员2"),Member("三级机构成员3"),Member("三级机构成员4"),], "三级机构")], [Member("二级机构成员1"),Member("二级机构成员2"),Member("二级机构成员3"),], "二级机构")], [Member("一级机构成员1"),Member("一级机构成员2"),Member("一级机构成员3"),Member("一级机构成员4"),Member("一级机构成员5"),], "一级机构"),Organ(null, [Member("八级机构成员1"),Member("八级机构成员2"),Member("八级机构成员3"),Member("八级机构成员4"),Member("八级机构成员5"),], "八级机构")];}
}
先说下思路,机构树列表,了解设计模式的同学应该很清楚,这个场景跟设计模式的Composite模式特别相似,参考下Composite模式的思想,保持容器与内容的一致性,因此,我们可以将机构与成员看成同一种数据,区别可能是,机构下面可能有子机构和成员,但是成员下面不可能有其他数据,是叶节点。
为了绘制的方便,我们先对原始数据进行处理,将所有的数据封装成Node节点,Node节点包含是否展开的标记expand,深度depth,类型type,唯一节点号nodeId,父节点号fatherId,还有原始数据,如下:
class Node<T>{static int typeOrgan = 10000;static int typeMember = 10001;bool expand;int depth;int type;int nodeId;int fatherId;T object;Node(this.expand,this.depth,this.type,this.nodeId,this.fatherId,this.object,);}
数据的处理如下:
///保存所有数据的ListList<Node> list = new List();///第一个节点的indexint nodeId = 1;///如果解析的数据是一个list列表,采用这个方法void _parseOrgans(List<Organ> organs){for(Organ organ in organs){_parseOrgan(organ);}}///递归解析原始数据,将organ递归,记录其深度,nodeID和fatherID,将根节点的fatherID置为-1,///保存原始数据为泛型Tvoid _parseOrgan(Organ organ, {int depth = 0, int fatherId = -1}) {int currentId = nodeId;list.add(Node(false, depth, Node.typeOrgan, nodeId++, fatherId, organ));List<Node<Member>> members = new List();if (organ.members != null) {for (Member member in organ.members) {members.add(Node(false, depth + 1, Node.typeMember, nodeId++, currentId, member));}}list.addAll(members);if (organ.subOrgans != null) {for (Organ organ in organ.subOrgans) {_parseOrgan(organ, depth: depth + 1, fatherId: currentId);}}}
对于树的展示,我们采用的是ListView,为了区分不同的层级,可以根据depth为每个Item增加缩进,对于树状的列表来说,最重要的是树的展开与收起,先来说树的展开,我们点击了一个机构,肯定是想展示该机构下的所有子机构和成员,那么,我们可以遍历Node列表,发现了fatherId==该机构的nodeId,那么代表这些是需要展示的数据,将其保存,插入到改机构的后面,至于为啥要插入,是因为如果直接加到展示数据的末尾,那么子成员也展示到了整个ListView的末尾,极度没有体验。然后再来说说树的收起,收起的时候,要收起所有直接挂在该机构下(子树)以及所有简介挂在该机构下(儿子的子树。。)的数据,依旧采用递归,先对已有的树进行递归,将所有与该树相关的子孙树删掉即可,本处采用的是先标记再将非标记的树替换为现有的数据的方法。
树的展示如下:
///扩展机构树:id代表被点击的机构id/// 做法是遍历整个list列表,将直接挂在该机构下面的节点增加到一个临时列表中,///然后将临时列表插入到被点击的机构下面void _expand(int id) {//保存到临时列表List<Node> tmp = new List();for (Node node in list) {if (node.fatherId == id) {tmp.add(node);}}//找到插入点int index = -1;int length = expand.length;for(int i=0; i<length; i++){if(id == expand[i].nodeId){index = i+1;break;}}//插入expand.insertAll(index, tmp);}///收起机构树:id代表被点击的机构id/// 做法是遍历整个expand列表,将直接和间接挂在该机构下面的节点标记,///将这些被标记节点删除即可,此处用到的是将没有被标记的节点加入到新的列表中void _collect(int id){//清楚之前的标记mark.clear();//标记_mark(id);//重新对expand赋值List<Node> tmp = new List();for(Node node in expand){if(mark.indexOf(node.nodeId) < 0){tmp.add(node);}else{node.expand = false;}}expand.clear();expand.addAll(tmp);}///标记,在收起机构树的时候用到void _mark(int id) {for (Node node in expand) {if (id == node.fatherId) {if (node.type == Node.typeOrgan) {_mark(node.nodeId);}mark.add(node.nodeId);}}}///增加根void _addRoot() {for (Node node in list) {if (node.fatherId == -1) {expand.add(node);}}}///构建元素List<Widget> _buildNode(List<Node> nodes) {List<Widget> widgets = List();if (nodes != null && nodes.length > 0) {for (Node node in nodes) {widgets.add(GestureDetector(child: ImageText(node.type == Node.typeOrgan? node.expand ? "images/expand.png" : "images/collect.png": "images/member.png",node.type == Node.typeOrgan ? (node.object as Organ).name : (node.object as Member).name,padding: node.depth * 20.0,),onTap: (){if(node.type == Node.typeOrgan){if(node.expand){ //之前是扩展状态,收起列表node.expand = false;_collect(node.nodeId);}else{ //之前是收起状态,扩展列表node.expand = true;_expand(node.nodeId);}setState(() {});}},));}}return widgets;}
做完了收起和展开,那么一个树差不多就完成了,至于树的展示顺序,因为对于不同的应用场景来说,展示的顺序也有所不同,因此,本代码暂未涉及。当然,博客里面的一些代码可能不全,完整的代码见:
GitHub地址:https://github.com/jadennn/flutter_tree
(20190214更新,增加搜索功能)
相关代码见:
https://github.com/jadennn/flutter_tree/commit/f7fa077f0aee92cb7bfb0db7cc996f7603f29c6c
flutter很好,路还很长,让我们一起奋斗前行!
flutter入门之实现展示机构树的功能相关推荐
- Flutter 入门指北(Part 9)之弹窗和提示(SnackBar、BottomSheet、Dialog)
该文已授权公众号 「码个蛋」,转载请指明出处 前面的小节把常用的一些部件都介绍了,这节介绍下 Flutter 中的一些操作提示.Flutter 中的操作提示主要有这么几种 SnackBar.Botto ...
- 从零开始的Flutter入门实战(二)
目录 前言 一.Column布局 1.创建一个Column 2.添加Container 3.运行验证 二.Row布局 1.将Column改成Row 三.Column布局和Row布局的混合使用 1.Si ...
- Flutter入门——创建第一个Flutter项目
Flutter入门--创建第一个Flutter项目 一.创建项目 第一个项目使用Android Studio创建,步骤如下: 先打开Android Studio,会有一个创建新的Flutter应用的选 ...
- Flutter 入门经典
Flutter是Google公司推出的新一代前端框架,最初目标只是为了满足移动端跨平台的应用开发, 开发人员可使用 Flutter 在 iOS 和 Android 上快速构建高质量的原生用户界面.但如 ...
- Flutter入门系列(二)---Flutter的原理及美团的实践
转载自:美团技术团队 导读 Flutter是Google开发的一套全新的跨平台.开源UI框架,支持iOS.Android系统开发,并且是未来新操作系统Fuchsia的默认开发套件.自从2017年5月发 ...
- Flutter入门之——HelloWorld
Flutter入门之--HelloWorld 安装Flutter开发环境 首先要准备好Flutter的开发环境和开发所使用的IDE,这里我们使用AndroidStudio作为开发工具. Flutter ...
- flutter 入门示例_AnyChart入门— 10个实用示例
flutter 入门示例 If your website is data-intensive, then you will need to make that data easy to visuali ...
- Flutter入门进阶之旅(二)Hello Flutter
开题 好像几乎我们学习或者掌握任何一门编程语言都是Hello word开始的,本篇博文做为Flutter入门进阶的第一篇分享,我们也从最简单的Hello world开始,至于Flutter开发环境的配 ...
- 机构树的数据库设计与查询构想-使用递归算法
需求 设计机构树,查询实验室所有机构 以某一节点为根节点,查询局部机构树 实现 思路: 自身嵌套查询 代码递归查询 数据库设计 CREATE TABLE `sys_dept` (`id` bigint ...
最新文章
- ORB_SLAM2代码阅读(1)——系统入口
- Linux下使用service启动jar包.md
- 支持的网卡列表_Windows 10的5G网卡折腾笔记(含采购链接)
- 在 Unity 中基于 Oculus DK1 的开发
- Kubernetes二进制部署——证书的制作和ETCD的部署
- JAVA软件图片浏览下载_java模拟浏览器下载图片
- Ue4.20 安卓开发配置及Android Studio 调试ue安卓工程
- 单例模式不能被继承_Spring的单例实现原理
- 为什么大公司一定要使用 DevOps?
- xshell6和xftp6的安装
- 电脑照片,怎么把电脑照片传到iphone手机 将电脑照片传到iphone方法【图文】
- word如何插入和删除脚注,尾注
- 微信网页授权及使用微信jssdk
- 创建API Signing Key
- -XX:CMSInitiatingOccupancyFraction
- 从零开始入门推荐算法工程师
- 用户运营:如何用B端运营思维做用户增长?
- ArcGIS教程:创建饼图
- 常见的SNS盈利模式(商业模式)
- WordPress安装教程(2022)|详细