有没有不计算,代码少的方式来实现这个拖拽排序呢?
文末有福利!!!
拖拽排序这块,其实最复杂的部分就是排序动画的处理,在 reOrderables 中,可以说绝大部份代码都是为了定位排序位置,最后通过调整Size,用AnimationController辅助处理,这种方式来实现一个折叠动画;
说实话,效果是有了,但是这代码确实让人看的容易晕;
在 Android 中,其实更多的排序动画是类似这种平移动画的:
所以这次我也计划使用这种动画实现(其实就是懒);
当然,该有的虚影之类的也是应该有的东西;
要想实现上面的平移动画,第一步就是知道各个Item自己本身在GridView中的位置坐标;
关于获取位置的实现方式,大部分方案都是通过给各个Item加上GlobalKey,然后获取renderObject来测量;
不过这次玩点花的,我想尽量少些代码的同时,减少GlobalKey的数量;
众所周知,sliver在layout方法结束后,会调用 didFinishLayout 方法,来通知layout结束,并公布当前缓存的第一个和最后一个Item的index;
既然这样,我在layout方法结束的时候,获取一下各个Item所在的起始位置不就行了?
这个InheritedWidget的作用,就是保存两个列表,分别存放Item的实际位置和重排序位置:
至于这两个List干啥用的,后面再揭秘;
PS : 其实用List存放位置信息是不合适的,因为List中的第一个Item,并不一定是GridView中index = 0 的那个Item,所以最后处理的时候要做一个index的转换,比较麻烦;
我这里偷懒,先用List存了
继承GridView,重写buildChildLayout , 给SliverGrid设置一个GlobalKey:
这个 Key 的作用,就是获取 SliverGrid 的 Element;而通过 Element 就可以拿到 各个child 对应的Element;
不过这里稍微不同的是,由于所说方法名是didFinishLayout,但是实际上,这里是performLayout方法的最后一步而已,所以performLayout方法实际上是没有完成的;
因此parent默认是不允许访问child的size的,如果调用renderOject.localToGlobal就会报错;
既然这样,可以用另一种方式获取child的位置信息,比如说,ParentData:
PS :其实Key这玩意也可以不用设置,在前面对Sliver的分析中已经知道了,调用 SliverChildDelegate 的 didFinishLayout方法的就是 SliverGrid 的 Element ,说白了,如果勤奋点,把 sliver那块重写下,就可以把Element传过来了;
同时因为 Element 可以视为 BuildContext , context 也能拿到,进而也能用来获取InheritedWidget,共享数据也很方便,当然这里是放到build方法中获取InheitedWidget来偷懒;
所以我感觉最合适的方法是重写sliver以及这个SliverChildDelegate……
在这里对build的Item进行一定的处理,首先最外层包一层AnimatedContainer:
AnimatedContainer 可以说是一个全自动动画播放器,没有AnimationController和Tween,仅仅一个setState就可以方便的实现切换动画;
而前面存放的两个List中的内容,就在这里用上了:
只需要把两个Item的Offset一减,setState之后就能实现位置平移动画;
PS:这个方案来自一位德国小哥的项目
不过我感觉他也写的挺麻烦的,没有结合DragTarget,而是仅仅通过Draggable返回的手势信息来做的碰撞检测,看的有点眼花……
PPS : 我知道他为什么不用DragTarget了……GridView本身的hitTest,是根据主轴交叉轴信息修改了点击坐标位置的,但是AnimatedContainer 中加入Transform后,这个坐标再次经过修改后,就根本命中不了任何一个Item…………所以,当Item通过Transform改变位置后,Draggable和DragTarget 是不会响应任何手势的……
至于这两个List中的内容是如何改变的,就是下面这部分了:
这里就来到了熟悉的Draggable+DragTarget部分:
在上图中,最后构造出的Item是一个Stack,其中原本 Item 应有的 child 传递给了buildDraggable方法构造Draggable;
另一部分就是DragTarget ,关键部分就在这里,其所做的事其实就是检测到碰撞后,根据自己和碰撞Item的index,重排序一下:
最后通过回调,触发外面的setState,启动动画即可:
两个文件共计不到300行:
虽说不到300行实现了目标效果,不过bug还是有的,比如说再次滑动切换DragTarget的时候,位置信息就算错了,估计某个状态没存上,onDragEnd之后通知保存数据这块也没做处理
但是300行确实能实现核心逻辑,剩下的东西都是些边边角角的部分,多也多不到哪去……吧,大概吧~~~~~
回正题:国际惯例,看下效果:
后来看了下,这块还真没法用Draggable+DragTarget 的方案,还真只能靠Draggale返回的手势回调来做计算…………
原因就是AnimatedContainer 这块中的PPS补充部分……
不计算这个目标流产了……
通过计算的方式估计要加个200行代码……
或者说有什么方案能让GridView的Item移动,但DragTarget不移动呢………… 比如说用Stack往Grid上面覆盖一层,用来放DragTarget呢 …………
部分……
不计算这个目标流产了……
通过计算的方式估计要加个200行代码……
或者说有什么方案能让GridView的Item移动,但DragTarget不移动呢………… 比如说用Stack往Grid上面覆盖一层,用来放DragTarget呢 …………
按照国际惯例,给大家分享一套十分好用的Android进阶资料:《全网最全Android开发笔记》。
整个笔记一共8大模块、729个知识点,3382页,66万字,可以说覆盖了当下Android开发最前沿的技术点,和阿里、腾讯、字节等等大厂面试看重的技术。
好啦,这份资料就给大家介绍到这了,有需要详细文档的小伙伴,可以微信扫下方二维码回复JJ免费领取哈~
因为所包含的内容足够多,所以,这份笔记不仅仅可以用来当学习资料,还可以当工具书用。
如果你需要了解某个知识点,不管是Shift+F 搜索,还是按目录进行检索,都能用最快的速度找到你要的内容。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照整个知识体系编排的。
1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……
1、热修复设计
2、插件化框架设计
3、组件化框架设计
4、图片加载框架
5、网络访问框架设计
6、RXJava响应式编程框架设计
……
1、设计思想与代码质量优化
2、程序性能优化
3、开发效率优化
……
1、高级UI晋升
2、Android内核组件
3、大型项目必备IPC
4、数据持久与序列化
5、Framework内核解析
……
1、NDK开发之C/C++入门
2、JNI模块开发
3、Linux编程
4、底层图片处理
5、音视频开发
6、机器学习
……
1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter Dart语言系统入门
……
1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战
……
1、准备开始
2、基础
3、类和对象
4、函数和lambda表达式
5、其他
……
好啦,这份资料就给大家介绍到这了,有需要详细文档的小伙伴,可以微信扫下方二维码回复JJ免费领取哈~