当前位置:   article > 正文

Android-签到日历_kotlin calendarview 签到记录

kotlin calendarview 签到记录

前言


项目中签到的日历尝试过用GridView或者Recyclerview来实现,用ViewFilper或者ViewPager实现切换动画,功能是实现了,但是第一次启动时,因为GridView或者Recyclerview要创建多个布局,导致界面卡顿,后来想到可以用自定义View的方式来实现,就是可能略微麻烦一些,不过还是尽量实现了一下,于是就有了下面的效果及这篇博客,算是对Calendar的用法总结。


实现过程


日历展示部分用自定义View的方式实现,左右的切换用ViewPager来实现,根据当前ViewPager的位置(Position)来计算当前的年和月,并画出对应的日期,先来看看日历展示部分View的实现过程。日历总共有6行7列的展示界面,那也就是需要画6*7个日期,那么1号的位置就是1号所对应的周几的位置,假如1号是周五,那么1号对应的下标就为5,1号之前的日期为前一个月的日期,当月最大天数以后的日期为后一个月的日期,以下为关键性代码:
  1. ...
  2. //当月信息
  3. int year = 1970 + currentPosition / 12;
  4. int month = currentPosition % 12;
  5. calendar.set(year, month, 1);
  6. int firstDay = calendar.get(Calendar.DAY_OF_WEEK) - 1;
  7. int selectMonthMaxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
  8. //上一个月的最大天数
  9. calendar.add(Calendar.MONTH, -1);
  10. int previousMonthMaxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
  11. ...
首先根据ViewPager的当前位置计算需要展示的年月,并设置当前日期为展示年月的1号,获取了该月1号的下标和该月的最大天数后,获取上一个月最大的天数,用于展示上月数据。ok,接下来就是画日历了,这个部分很简单,关键代码如下:
  1. for (int i = 1; i <= 42; i++) {
  2. int copyI = i - 1;
  3. int x = (copyI % 7) * itemWidth + itemWidth / 2;
  4. int y = (copyI / 7) * itemHeight + itemHeight / 2 + ...;
  5. if (i <= firstDay) {//前一月数据
  6. ...
  7. int day = previousMonthMaxDay - firstDay + i;
  8. canvas.drawText(String.valueOf(day), x, y, paint);
  9. ...
  10. } else if (i > selectMonthMaxDay + firstDay) {//后一月数据
  11. ...
  12. int day = i - firstDay - selectMonthMaxDay;
  13. canvas.drawText(String.valueOf(day), x, y, paint);
  14. ...
  15. } else {//当前月数据
  16. int day = i - firstDay;
  17. ...
  18. canvas.drawText(String.valueOf(day), x, y, paint);
  19. ...
  20. }
  21. }
签到标志的绘制需要提供个日期和签到是否成功的标志,然后画日期的时候判断一下即可:
  1. HashMap<String, Boolean> signRecords = new HashMap<>();
  2. signRecords.put("2017-07-12", true);
  3. signRecords.put("2017-07-23", true);
  4. //画签到标志
  5. date.set(year, month, day);
  6. String dateStr = format.format(date.getTime());
  7. if (signRecords.containsKey(dateStr)) {
  8. ...
  9. if (signRecords.get(dateStr)) {
  10. canvas.drawBitmap(...);
  11. } else {
  12. canvas.drawBitmap(...);
  13. }
  14. }

OK,这样日历的展示部分就完成了。但实际项目中的需求可能是当用户点击选中某个日期的时候,查看当前的签到信息,那么这个点击位置怎么判断呢,其实也很简单,获取到当前点击位置的x,y值,判断所在的位置:
  1. private int getPosition(float x, float y) {
  2. y -= config.weekHeight;
  3. int y1 = (int) (y / itemHeight);
  4. int x1 = (int) (x / itemWidth);
  5. return y1 * 7 + x1;
  6. }
至于农历的实现就是用网上的公历转农历的算法,换算一下即可,但是需要注意的换算的算法比较复杂,如果我们每个日期都用这个算法换算一下的话,肯定时间复杂度不是很理想了,优化如下:
  1. //如果阳历是当在同一年,同一月,day是lastDay的后一天,并且
  2. //阴历lastLunarDay<29的时候,
  3. //此时的阴历直接在前天的基础上加1,否则重新计算
  4. // false为在前一天的基础上已经修改了,直接可以使用lunar实例
  5. if (lastYear == year && lastMonth == month && day - lastDay == 1) {
  6. if (lastLunarDay > 0 && lastLunarDay < 29) {
  7. //上个日期的基础上加1
  8. lunar.lunarDay = lastLunarDay + 1;
  9. ...
  10. }
  11. }
  12. ...
  13. //否则重新使用农历转换算法计算日期
这样实际的效果就是,启动和切换都更加流畅,哈哈,至此我们的签到日历的日历展示部分就算完成了,至于日历的切换就是根据当前位置计算日历日期,这里需要优化的地方是,ViewPager的切换如果每次都创建一个自定义View的话,很不好,我们可以把ViewPager中销毁的View,在下一次创建时复用,如此将空间复杂度降到最低,关键代码如下:
  1. ...
  2. @Override
  3. public Object instantiateItem(ViewGroup container, int position) {
  4. ZWCalendar calendarView = getContent(position);
  5. container.addView(calendarView);
  6. return calendarView;
  7. }
  8. @Override
  9. public void destroyItem(ViewGroup container, int position, Object object) {
  10. destroyViews.add((ZWCalendar) object);
  11. container.removeView((View) object);
  12. }
  13. ...
  14. private ZWCalendar getContent(int position) {
  15. ZWCalendar calendarView;
  16. if (destroyViews.size() != 0) {
  17. calendarView = destroyViews.valueAt(0);
  18. destroyViews.remove(calendarView);
  19. } else {
  20. calendarView = new ZWCalendar(getContext());
  21. ...
  22. viewSet.add(calendarView);
  23. }
  24. ...
  25. return calendarView;
  26. }
OK啦!

扩展


既然这种形式的签到日历都实现了,那么顺便删减修改下代码实现另外一种形式的签到日历,里面的逻辑和算法和上面的View大同小异,只不过没有日期的选择。先看看效果:

具体使用方法看代码吧,很简单。

使用


为了方便使用,这里定义了一些属性,如下:
  1. <declare-styleable name="ZWCalendarView">
  2. <attr name="weekHeight" />//周几的标题高度
  3. <attr name="weekTextSize" />
  4. <attr name="weekBackgroundColor" />
  5. <attr name="weekTextColor" />
  6. <attr name="calendarTextSize" />//日历的字体大小
  7. <attr name="calendarTextColor" />
  8. <attr name="isShowOtherMonth" format="boolean" />//是否显示上月和下月的日期
  9. <attr name="otherMonthTextColor" format="color" />
  10. <attr name="isShowLunar" />//是否显示农历
  11. <attr name="lunarTextColor" />//农历字体的颜色,大小等
  12. <attr name="lunarTextSize" />
  13. <attr name="todayTextColor" />
  14. <attr name="selectColor" format="color" />//当前选中的圆形颜色
  15. <attr name="selectTextColor" format="color" />//选中的字体颜色
  16. <attr name="signIconSuccessId" format="integer" />//签到成功的标志
  17. <attr name="signIconErrorId" format="integer" />
  18. <attr name="signIconSize" format="dimension" />//签到标志的大小
  19. <attr name="signTextColor" />//签到字体的颜色
  20. <attr name="limitFutureMonth" />//是否显示未来年月的日历
  21. </declare-styleable>
代码中的使用:
  1. calendarView.setSelectListener(new ZWCalendarView.SelectListener() {
  2. @Override
  3. public void change(int year, int month) {
  4. //当前切换的监听
  5. }
  6. @Override
  7. public void select(int year, int month, int day, int week) {
  8. //当前选中的监听
  9. }
  10. });
  11. //代码选中一个日期
  12. calendarView.selectDate(2017, 9, 3);
  13. //显示上个月
  14. calendarView.showPreviousMonth();
  15. //显示下个月
  16. calendarView.showNextMonth();
  17. //返回今天
  18. calendarView.backToday();
另外那个扩展的签到日历的使用和这个稍稍不同,具体去看代码吧。

额,签到日历虽然实现了,但是还没有用到项目中(你问我为什么不用? 生气,你是程序员你应该懂得),虽然经过了测试,但是可能还是会有一些潜在bug,有问题我再改吧。

代码:https://github.com/HzwSunshine/SignCalendarProgect







声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/534814
推荐阅读
相关标签
  

闽ICP备14008679号