作者 | 炎之铠

地址 | http://www.jianshu.com/p/9b05b22c474f

声明 | 本文是 炎之铠 原创,已获授权发布,未经原作者允许请勿转载

前言

之前把我项目用到的类似于 PathView 的菜单 YMenuView 抽离出来,分享了实现思路,但是只是实现了在右下角的几种菜单弹出收回的效果,限制太大,适用性不强。这次重温了一下设计模式之后把 YMenuView 回炉再造,重构代码,打造出一个可以让用户发挥自己自由想象,实现自己想法需求的灵活 YMenuView2.0,还加了3个自定义 YMenu 的例子,先来看看看效果:

(前面几个是之前 YMenuView1.x的效果,后面是新加的)

介绍

YMenuView 是包含着一个 MenuButton 和若干个 OptionButton 的RelativeLayout,具体实现原理前一篇已经有介绍了,所以这篇文章主要说明 YMenuView2.0 的改变(使用方法的话可以直接看 Github。YMenuView2.0 重构代码之后,最大的改变就是把 YMenuView 的大部分逻辑抽离出来,留下4个主要的抽象方法,可以让用户自定义,这4个分别是决定:MenuButton的位置、OptionButton的位置、OptionButton的显示动画和OptionButton的消失动画。

重构过程

抽象类YMenu


YMenuView2.0的主角不再是YMenuView类,现在的它只是一个小弟(子类),真正的大哥现在叫YMenu(父类)

最近正在学习Animation的源码,得知Animation是一个抽象类,然后其他具体的动画时通过继承它,然后重写applyTransformation()方法来实现具体的动画需求,这是模板方法模式的应用(其实Activity也这样),所以YMenuView也参考这种模式,把一些次要的逻辑抽象出来,放都在抽象父类YMenu里面,开放出4个抽象方法给用户可以通过继承来自定义YMenu:

YMenu有4个具体实现类(效果都在上面的gif图里展现过了):

抽象方法的实现


这4个方法抽象出来之后,由子类实现功能,下面介绍如何实现(以下的4个方法介绍的代码是在 YMenuView 里面的):

1.setMenuPosition()方法

此方法的实现决定了 MenuButton 的位置,MenuButton 就是上面效果图那个齿轮,点击之后会有选项出现消失的动作。在上一篇YMenuView源码分析里面就已经写到,它的位置是用Relative的 LayoutParams来设置的(和上一篇有点不同就是因为是从抽象父类获取属性,所以要用一些get方法,而且我还重命名了一些属性,具体可以在 Github的使用介绍属性表查看):

但是这样使用 LayoutParams 的话理解起来很不直观(其实熟悉RelativeLayout 的还好),如果让用户自定义的话会有点麻烦,所以我把这个过程参考建造者模式封装成了一个类 MenuPositionBuilder,通过它来实现这个位置的设置过程。

这样的话感觉就清晰一些了,当然有人如果熟悉了 RelativeLayout 参数的话,可能会觉得比原来复杂了,也可以用上面的写法,这两者是等价的。至于 MenuPositionBuilder 的实现过程,运用的建造者模式,比较简单,由于篇幅原因这里就不写了,有兴趣的话可以直接到项目 Github地址上查看源码。

2.setOptionPosition()方法

此方法的实现决定了那些选项OptionButton的位置,这里是YMenuView的实现:

和 MenuButton 一样,这个方法也有一个专属的OptionPositionBuilder,下面使用它来设置:

这里 YMenuView 的 OptionButton 位置具体的实现逻辑上一篇介绍YMenuView1.x 的时候已经说过了,这里就不详细说了。

3.createOptionShowAnimation() 方法和createOptionDisappearAnimation() 方法

这两个方法用来决定OptionButton的出现和消失的动画,原本在YMenuView1.x版本是在OptionButton内部实现,但是为了我们可以通过继承YMenu来实现一个自定义的效果,所以就通过一个回调设计把这两个方法的具体实现放到YMenu里抽象出来了。

所以,这个方法就是给出一个索引为index的OptionButton的参数,给设置动画作为参考值,然后把设置好的动画返回。

这里的逻辑其实和之前的没有变化,也是要注意 Animation 里面的坐标原点是View的左上角就行了,这里只列出 ShowAnimation 的实现,至于 DisappearAnimation 的话就是和这个相反而已,篇幅原因就不列出来了。

4.抽象方法的执行顺序

这4个抽象方法是有执行顺序的,所以后面的方法能用到前面设置好的对象参数:

这些顺序的具体细节就是:

  • 先初始化MenuButton,让MenuButton经历Measure和Layout过程之后,再初始化OptionButton,这样OptionButton就能以MenuButton的位置信息做为参考。

  • 跟着每一个OptionButton经历Measure和Layout过程之后,它的ShowAnimation和DisappearAnimation又能以它的宽高信息进行初始化。

  • 这样根据不同时期放出了不同抽象方法,让用户可以通过实现这4个方法来实现自定义的YMenu。

Circle8YMenu的实现

这是一个 OptionButton 围绕着 MenuButton 的布局,Option 最大数量为8个,MenuButton 的位置位于 ViewGroup 正中间,如果想改变的话可以继承 Circle8YMenu,单单重写 setMenuPosition() 方法就可以了。

然后就是动画的实现:

动画都是选项从MenuButton里面冒出来和滚回去,亮点就是根据OptionButton 的 index设置了不同的延时,让整个效果看起来炫酷一点。

重写了这4个方法之后,就能够创造出这个Circle8YMenu了,如果想在这基础上改东西,直接重写这4个方法中要改变的方法就行了,有点方便。

TreeYMenu的实现

这是一个 OptionButton 分布成分叉树的布局,Option 最大数量为9个,MenuButton 的位置位于 ViewGroup 中下方。

SquareYMenu的实现

这是一个 OptionButton 和 MenuButton 组成正方形的布局,Option最大数量为8个,MenuButton 为位置依靠右下。这里就不贴出源码了,直接给出乘积数组,因为其他代码思路和前面的差不多,想看具体代码的可以看项目 Github 地址。

Ban的补充

YMenuView1.x就有有Ban功能,能把一些位置设为不放置OptionButton,那么YMenuView2.0也支持这个,例如:

 //对Circle8YMenumYMenu.setBanArray(0,2,4,6);

//对TreeYMenu
mYMenu.setBanArray(2,8,7);

//对SquareYMenu
mYMenu.setBanArray(3,4,7,5,6);

但是动画延时还是会算上不被填充的位置,这暂时无法避免,所以想要更好的体验效果的话就继承YMenu重写方法吧。

总结

这次重构过程主要使用了模板方法模式把一些次要的逻辑封装起来,主要的类结构从一个 YMenuView 扩展为一个父类 YMenu 和4个子类。在这个过程中也学到了不少东西,感觉自己在这方面还是有很多不足,后面会陆续更新改进的。但是最后也把自己的想法实现了。在此记录并分享,如有意见和建议,敬请提出。如果喜欢 YMenuView 的话,也可以上Github 点个Star _

https://github.com/totond/YMenuView

与之相关

1 Android 上一个类似 PathMenu 效果的自定义 View 源码分析

2 「译文」资深程序员应该有的样子

微信号:code-xiaosheng

公众号

「code小生」


回炉再造,灵活的 YMenuView 2.0 诞生相关推荐

  1. 回炉再造,灵活的YMenuView2.0诞生

    出处: 炎之铠邮箱:yanzhikai_yjk@qq.com 博客地址:http://blog.csdn.net/totond 本文原创,转载请注明本出处! 本项目GitHub地址:https://g ...

  2. 【历史上的今天】12 月 18 日:Perl 1.0 诞生;音频制作软件 FL Studio 问世;微软发布 Windows MCE 2003

    整理 | 王启隆 透过「历史上的今天」,从过去看未来,从现在亦可以改变未来. 今天是 2021 年 12 月 18 日,在 1890 年的今天,无线电发明家埃德温·阿姆斯特朗(Edwin Armstr ...

  3. 《J2SE 回炉再造16》-------溺水狗

    第十章 线程 1. 提纲 2. 线程的基本概念 进程是一个静态的概念,严格意义上讲并不能执行,我们所说的进程执行指的是进程里的主线程(main()方法)开始执行了 3. 线程的创建和启动 只要可以使用 ...

  4. 回炉再造Css Layout

    大家好,我叫张文轩,这是我的第5篇分享 初衷 有次看了篇文章,文章有句话对我印象深刻,这句话是废掉一个人的最好方式,就是让他忙的停不下来.意思就是在前进的过程中,如果只知道不停的奔跑,而不间歇性的停下 ...

  5. C++之回炉再造笔记--问题记录1

    目录 1--vs2017包含目录后,引入头文件失败 2--调试TensorRT程序,报"重写虚函数的限制性异常规范比基类虚成员函数少"的错误 3--编译Tensorrt测试程序时, ...

  6. 回炉再造-多线程的创建

    三种方式创建: 1. 通过继承Thread类来创建并启动多线程的方式 2. 通过实现Runnable接口来创建并启动线程的方式 3. 通过实现Callable接口来创建并启动线程的方式 4. 总结Ja ...

  7. 《J2SE 回炉再造18》-------溺水狗

    第十二章 GUI编程 1. 提纲 2. AWT包 3. Component和Container 4. Frame类 代码1: import java.awt.*;public class TestFr ...

  8. 《J2SE 回炉再造17》-------溺水狗

    第十一章 网络编程 1. 提纲 值得注意的是网络编程不等同于网站编程 2. 网络基础概念 3. 网络通信协议及接口 4. 数据分层的思想 5. 数据封装和数据拆封 6. IP协议 IPV4协议中用4个 ...

  9. 《J2SE 回炉再造15》-------溺水狗

    第九章 输入/输出流.字节/字符流.节点/处理流 1. 提纲 2. 概述 注1:输入/输出流:都是站在程序的角度来说的,而不是文件的角度.参考理解 注2:输入流:将其他资源传送到内存(程序):输出流: ...

最新文章

  1. redis和memcached的区别(总结)
  2. 十七、二叉树的建立与基本操作
  3. 关于vue.js的部分总结
  4. 打开数据库_打开这份指南,数据库运维也能优雅、简单!
  5. java UDP 使用示例
  6. 基于visual Studio2013解决C语言竞赛题之1067间隔排序
  7. java零碎要点009---java实现服务器心跳机制,TCP握手
  8. 第2章[2.8] Ext JS的控制器类型及使用
  9. Java编译带包文件
  10. H5:100款html5微信小游戏最新最新源码
  11. Web安全深度剖析-笔记
  12. win-pe 重置win10密码“SAM 文件只读属性,修改操作无法保存”的解决办法
  13. 抖音音频提取php,php抖音背景音乐解析下载API接口
  14. 计算机导师问读研计划和后续计划,考研面试,问“研究生时期的规划”怎么回答急...
  15. 统计检验的基本原理(异常值检验)
  16. 题解 UVA1567 【A simple stone game】
  17. POI入力自定义格式数据
  18. Postgis源码编译
  19. VPS是干嘛用的?有哪些知名牌子?与云服务器有什么区别?
  20. 跨平台应用开发进阶(二十九) :uni-app 实现Android原生APP-云打包集成神策详细教程

热门文章

  1. 发现一个好玩的东西:Markdown 使用 Emoji 表情
  2. 单片机•嵌入式单片机控制技术•CPLD/FPGA技术自动化控制技术教学
  3. TextView的autoLink属性
  4. TinyMCE富文本编辑器自动链接插件 AutoLink配置
  5. 两个半月的业余时间用Flutter做了个app-技术篇
  6. 共识协议(2)共识算法分类
  7. 10个程序员需要收藏的良心网站,你绝对没有用过
  8. FIFO加包文,切割包文
  9. 使用蜻蜓映射实现外网访问内网金蝶软件服务
  10. [B2R]Bob1.0.1