自己当初编写CalendarSelector
库主要是为了解决日期的选择
问题,比如说档期,一个人的档期大部分是一段连续的时间。
后来随着功能的完善,发现还可以很好的满足一些其它的需求,比如选择某几天,或者纯粹的显示。为了满足这些需求,自己进行了几个版本的迭代,在迭代中也解决了几个自己觉得比较棘手的问题,下面会分析自己的实现思路,具体的实现过程和进行的一些优化。
MonthView的绘制
View实现方式
MonthView是对月天数的组合显示,使得以月为整体来显示。自己最初的做法是MonthView为一个原始的View,通过canvas来绘制每一天,根据这个思路实现了一个版本,但最后被自己放弃。
因为如果想要实现一些动画的效果或者想自定义天的显示太麻烦了,只能通过canvas来绘制,坐标的计算太繁琐,也很容易出现误差~
ViewGroup实现方式
View实现方式被放弃之后,自己就在思考如何让绘制和增加一些动画能易于实现,并且方便第三方使用。自己最后选择了ViewGroup的方式,月为ViewGroup,而月的每一天都为
一个单独的View,通过组合的方式来实现一个月的显示,一个是自己不用管理天的绘制,而由于天的显示被抽象成View,添加动画,自定义自然会很方便。
1 |
|
1 | private void createDayViews() { |
针对RecyclerView的优化
由于每个月的天数存在不相等的情况,所以当把MonthView嵌入到RecyclerView中时会存在一些小问题。RecyclerView回收机制的存在,有可能存在回收使用的MonthView的height跟当前需要显示的月份要求的高度不匹配,这个时候就需要requestLayout,重新measure、layout和draw。
但是呢当两者height相等时并不需要走这个流程,因为这个流程还是相当耗时的,为了针对这个进行优化,做了一些判断。
1 | // when use in the recyclerview, each item's height may be different, we should requestLayout again |
neededRelayout在计算月的天数时来进行判断,如果行相同,那么就不需要requestLayout()。
1 | if(drawMonthDay) { |
在使用RecyclerView时减少一些对象的创建,对性能的改进还是明显的,可以减少gc的频率,降低内存抖动,有效的减少掉帧的情况出现。
DayViewInflater抽象的实现
当初自己构思DayViewInflater实现时,不得不惊讶于代码有结构的组织带来的效果真是大啊,通过对DayViewInflater抽象的实现,自己之前一直纠结的灵活自定义天的显示和选中、未选择中状态切换动画,变得是那么的简单,一切皆迎刃而解。
DayViewInflater
1 | public abstract class DayViewInflater { |
DayViewHolder
1 | public abstract class DayViewHolder { |
通过DayViewInflater和DayViewHolder的组合,让天的UI自定义非常的方便,而状态切换的动画也更加的方便实现。
AnimDayViewInflater.java
1 | public class AnimDayViewInflater extends DayViewInflater{ |
SingleMonthSelector和CalendarSelector分析
其实CalendarSelector库的核心功能由这两个类来实现的,自己的初衷也是为了实现select的功能,下面简要的介绍下实现原理。
SingleMonthSelector
首先对select的功能做了区分,定义了两种模式,分别是选择一段连续的天
和多个不连续的天
。
1 | public enum Mode{ |
针对这两种模式分别有不同的实现,其实思路是一样的,只不过实现过程中的具体逻辑有一些微小的区别。
INTERVAL
1 | protected void intervalSelect(MonthView monthView, FullDay day) { |
SEGMENT
1 | private void segmentSelect(MonthView monthView, FullDay ssDay) { |
CalendarSelector
CalendarSelector的实现相对于SingleMonthSelector的实现要复杂一些,因为要跨MonthView来选择,但是实现的思路跟SingleMonthSelector是一样的,只不过是多了一些判断。
CalendarSelector也有两种模式,这个跟SingleMonthSelector是一样的。
INTERVAL模式跟SingleMonthSelector一样的实现
1 | protected void intervalSelect(MonthView monthView, FullDay day) { |
SEGMENT模式稍微复杂一些,主要是一些状态的判断,还有MonthView的刷新
1 | private void segmentSelect(ViewGroup container, MonthView monthView, FullDay ssDay, int position) { |
从上面的代码看的出来,在日期的选择过程中把一些拦截的功能交给了使用者,这样方便实现各种特殊的功能,灵活性相对来说比较高。
1 | selector = new CalendarSelector(data, CalendarSelector.Mode.SEGMENT); |
总结
上面谈到的几点基本上包含了CalendarSelector库最主要的功能点了,如果还有什么疑问的话,非常欢迎在GITHUB上提issue:)