自己实现一款安卓日历

来源:互联网 发布:竞猜单关历史开奖数据 编辑:程序博客网 时间:2024/06/15 21:30

今日科技快讯

近日,苹果公司宣布,自7月18日至24日,凡在有银联云闪付标识的指定商铺,使用ApplePay来支付,即可享受低至5折优惠,以及高达50倍银行信用卡积分奖赏。此次活动,除了数十家线下实体门店外,还有京东、携程等电商参与。不过专家认为:“最好的时机已经过去了,微信和支付宝已经全面占领了中国的移动支付市场。在中国,苹果支付用户的活跃度并不高。”

作者简介

明天就是周末了,在这个高温天气下,大家尽可能地还是少出门,以防中暑。提前祝大家周末愉快!

本篇是 yannecer 的第二篇投稿,分享了一个日历开源项目,效果很不错,希望大家喜欢!

yannecer 的博客地址:

http://blog.csdn.net/y12345654321

简述

最近写了一款日历,包含周日历、月日历以及滑动切换视图,先上效果图:

项目主要用到了自定义View,ViewPager,RecyclerView 和 NestedScrollingParent。

本篇文章主要说一下月日历数据、月视图绘制以及点击日期的实现。

数据

数据部分,网上能找到比较完整的工具类,主要是根据本月和上月的天数以及本月第一天是周几来计算。

首先计算上月日期: 由本月的第一天是周几和上个月的天数,得出上月的日期的显示

再计算本月日期:本月内的数据根据该月的天数跑循环。

再计算下月计算上月日期的显示:下月的天数显示可以看本月最后一天是周几,根据距离一周最后一天的间隔天数,从1开始直接加上就可以了。

这里要分情况了,有的月份跨5个周,有的月份能跨6个周。计算上没有区别,但是显示的时候会有区别,为了简单,统一成6周,共42个元素,一月多余的用下月日期补充。日期计算肯定使用

joda-time

https://github.com/JodaOrg/joda-time

天数、月份、年份计算都非常简单,有一点,这个库每周是周一开始的,周日历要注意一下。

一月的数据:

这里简化了操作,项目中我把每个数据都转化成了 joda-time 中的 DateTime对象,方便后面操作。

数据有了,接着就是绘制这些数据。

MonthView

MonthView 继承于 View,重写 onDraw(canva)方法。首先在构造方法中根据颜色和字体大小初始化画笔:

接着就是在 onDraw(canva)方法 中绘制。

我们先考虑一下我们都需要做哪些事情。需要绘制公历、农历、小圆点、选中的圆环包括后面的点击操作,这些元素确定位置都需要一个矩形(Rect),那么就可以先在这个 View 里面绘制42个矩形。四个点确定一个矩形,可以在纸上画一下大致的图案,大致画个一两行矩形,应该就找到规律了,感觉有点像以前上学时做的找规律的数学题。

6行7列的一个矩形阵

有了这42个矩形,我们做后面的事情就简单了。

绘制文字

绘制文字 canvas.drawText() 会发现,可能会出现文字不在矩形的中心,解决办法参看这篇博客

Android Canvas drawText实现中文垂直居中

http://blog.csdn.net/hursing/article/details/18703599

我们需要在绘制的循环里面要判断这些内容:

1、是不是本月的数据(用颜色区分本月和其他月的数据)

2、是不是今天

3、有没有选中的日期

4、显示不显示农历

其中今天和选中的日期用圆环表示,就需要在当天和选中的日期的矩形中绘制圆环。已今天为例:

里面的一些工具类可参见文末项目地址。

点击事件

点击操作使用了 GestureDetector,这个类里面已经定义好了单级,双击,长按等操作,只需要我们重写相应的方法就可以,不用我们在去定义一个点击操作了。

重写 MonthView 的 onTouchEvent(MotionEvent event)方法,交给 GestureDetector 处理

触摸事件交给 GestureDetector,当发生单击时,循环刚才绘制文本时的矩形,根据用户点击的XY坐标值判断是在哪个矩形内,我们就知道用户点击的是哪个日期了。

里面写了一些回调,方便在 ViewPager 中跳转到相应的月份。剩下的操作放到了 ViewPager 中完成,如果不是本月就跳转再设置选中的日期,如果是本月,就直接设置选中的日期:

在 doClickEvent(DateTime dateTime, int currentItem)方法 中,得到当前的 MonthView ,设置选中日期 monthView.setSelectDateTime(dateTime); 

而在 setSelectDateTime(DateTime dateTime) 中就是赋值和重绘页面:

这样在 onDraw(Canvas canvas) 中 mSelectDateTime!=null,就会绘制选中的圆环了。

MonthView 没有重写 onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,是因为这个 MonthView 是放在继承自 ViewPager 的 MonthCalendar 中使用的,只需在布局文件中设置 MonthCalendar 的 layout_width 和 layout_height 即可:

滑动切换

月视图和周视图滑动切换,是自定义的 MWCalendar 继承 LinearLayout,实现了 NestedScrollingParent接口,消费了 RecyclerView 的滑动距离,实现整体上滑,在滑动过程中根据条件判断是否显示周视图 WeekCalendar,用 OverScroller 实现滚动。

关于 NestedScrollingParent 和 RecyclerView 的嵌套滑动,网上已经有比较详细的资料,这里不再复制粘贴,只讲一下实现细节,我主要参考了Hongyang的照片文章

Android NestedScrolling机制完全解析 带你玩转嵌套滑动

http://blog.csdn.net/lmj623565791/article/details/52204039

MWCalendar 是一个继承 LinearLayout,并实现 NestedScrollingParent 的容器,里面包含了 一个月视图 MonthCalendar 和 一个RecyclerView,WeekCalendar 是在 xml 加载完后以参数的方式传进去的,目的是为了使 MWCalendar 和 WeekCalendar 同在一个 RelativeLayout 中方便处理切换.

RecyclerView 向上滑动时,让 NestedScrollingParent 消耗掉 RecyclerView 的上滑距离,当向上滑动到只剩一个行高的时候,NestedScrollingParent 停止滑动,由 RecyclerView 继续滑动。这里主要说 onStartNestedScroll(),onStopNestedScroll(),onNestedPreScroll() 和 onMeasure() 这四个方法。

这个方法在滑动时调用,用来判断月视图和周视图的切换,dy>0 向上滑,且滑动距离 getScrollY()<rowHeigh * 5(月视图共6个行高)时,需要月视图向上滑,滑动距离为 rowHeigh * 5。

这个方法是停止滑动手松开的时候调用,在这个方法中根据状态判断是上滚还是下滚,再根据已滑动的距离 getScrollY() 和总的滑动距离得到需要利用 mScroller 滚动的距离。

computeScroll() 方法,在执行 mScroller.startScroll() 后调用,实时调用并绘制界面,所以可以在这个方法中判断周视图和月视图哪个应该显示。

在 NestedScrollingParent 中如果不重写 onMeasure()方法,会出现上滑的过程中,下面的部分是空白的,原因是 整体View 测量的时候,测量的结果适应屏幕的大小,在上滑的过程中并没有重新测量,上滑的时候 整体View 一起整体上滑,View 的高度还是那么高,向上滑出一部分,下面的就变成空白了。

所以我们需要做的就是在 View 测量的时候,给 RecyclerView 加高,这样上滑的时候也是正常显示,这个加高不是随便加的,必须是停止滑动的时候正好能显示 RecyclerView 的全部数据,这样就可以计算得到,加高的部分是正常的高度减去上面一行的高度,强行把 RecyclerView 加高已补充上滑出去的那部分视图。

完整项目参见:

https://github.com/yannecer/NCalendar

更多

每天学习累了,看些搞笑的段子放松一下吧。关注最具娱乐精神的公众号,每天都有好心情。

如果你有好的技术文章想和大家分享,欢迎向我的公众号投稿,投稿具体细节请在公众号主页点击“投稿”菜单查看。

欢迎长按下图 -> 识别图中二维码或者扫一扫关注我的公众号:

原创粉丝点击