背景

开发过程中,我们经常会遇见使用日历组件,有时候需要我们自己做高度自定义。因此下面我们来讲一下如何使用合适的算法来绘制我们自己的日历组件。

需求分析

要显示一个日历组件,我们需要知道日历的大致外观是怎么样的?

日历图片

从上图中看,

a. 日历大致有表头,星期显示,日期显示以及农历(此处暂不讨论)显示。

b. 日期格子占有的数量是:6 × 7 = 42 个,其中包含有:上月的部分日期,本月的全部日期以及下月的部分日期。明白一点说就是,如果我们想计算某个月的日历,那么我们需要知道本月的一号是星期几,有多少天,上个月在这个月的日历中占有几天以及下个月在这个月的日历中占有几天,那么我们就可以计算出当月的日历数据了。

具体实现

由于年份中存在闰年和平年的概念,并且影响2月的天数,因此我们需要一个判断闰年和平年的方法,如下:

/**

* 判断某一年是否是闰年

* @param year

*/

@JvmStatic

fun isLeapYear(year: Int): Boolean = year % 4 == 0 && year % 100 != 0 || year % 400 == 0

计算每个月的1号是星期几?查看网上资料

关于这个星期几的计算,有多种计算公式。

①. 常用公式

W = [Y-1] + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] + D

Y是年份数,D是这一天在这一年中的累积天数,也就是这一天在这一年中是第几天。

②. 蔡勒(Zeller)公式

w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1

公式中的符号含义如下,w:星期;c:世纪;y:年(两位数);m:月(m大于等于3,小于等于14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月来计 算,比如2003年1月1日要看作2002年的13月1日来计算);d:日;[ ]代表取整,即只要整数部分。相比于通用通用计算公式而言,蔡勒(Zeller)公式大大降低了计算的复杂度。

③. 对蔡勒(Zeller)公式的改进

相比于另外一个通用通用计算公式而言,蔡勒(Zeller)公式大大降低了计算的复杂度。不过,笔者给出的通用计算公式似乎更加简洁(包括运算过程)。现将公式列于其下:

***W=[y/4]+r (y/7)-2r(c/4)+m’+d***

公式中的符号含义如下,r ( )代表取余,即只要余数部分;m’是m的修正数,现给出1至12月的修正数1’至12’如下:(1’,10’)=6;(2’,3’,11’)=2;(4’,7’)=5;5’=0;6’=3;8’=1;(9’,12’)=4(注意:在笔者给出的公式中,y为润年时1’=5;2’=1)。其他符号与蔡勒(Zeller)公式中的含义相同。

④. 基姆拉尔森计算公式

W= (d+2m+3(m+1)/5+y+y/4-y/100+y/400) mod 7

在公式中d表示日期中的日数,m表示月份数,y表示年数。

注意:在公式中有个与其他公式不同的地方:

把一月和二月看成是上一年的十三月和十四月,例:如果是2004-1-10则换算成:2003-13-10来代入公式计算。

此处,我们取第④种公式,因此有如下代码:

/**

* 计算星期几? 取值范围从 1 - 7

*

* @param year 当前年份

* @param month 当前月份

* @param date 当前日期

* @return

*/

@JvmStatic

fun computeWeekNum(year: Int, month: Int, date: Int): Int {

val isMonth1Or2 = month == 1 || month == 2

val month1 = if (isMonth1Or2) month + 12 else month

val year1 = if (isMonth1Or2) year - 1 else year

val weekNum = (date + 2 * month1 + 3 * (month1 + 1) / 5 + year1 - year1 / 100 + year1 / 4 + year1 / 400) % 7

return weekNum + 1

}

在日历计算中,我们还需要知道当月天数,前一个月的天数以及后一个月的天数,所以:

/**

* 计算月份的天数

*

* @param year 当前年份

* @param month 当前月份

* @return

*/

@JvmStatic

fun computeMonthDayCount(year: Int, month: Int): Int {

when (month) {

2 -> return if(isLeapYear(year)) 29 else 28

1, 3, 5, 7, 8, 10, 12 -> return 31

4, 6, 9, 11 -> return 30

}

return -1

}

磨刀不误砍柴工,现在我们的准备工作都已经完成,现在就要开始日历的计算工作了。

/**

* 计算日历中的日期数据,其中涉及到的数字42就是6×7的布局。

* @param year 年份

* @param month 月份

*/

@JvmStatic

fun computeDatesInCalendar(year: Int, month: Int): MutableList {

val dates: MutableList = mutableListOf()

val monthDayCount = computeMonthDayCount(year, month) //计算出当前这个月有多少天

val preMonthDayNum = computeWeekNum(year, month, 1) //先获得该日期下是周几?然后计算出上个月有几天要在日期中显示

val preMonthDayCount = computeMonthDayCount(if (month == 1) year - 1 else year, if (month == 1) 12 else month - 1)

val nextMonthDayNum = 42 - preMonthDayNum - monthDayCount //计算出下一个月要显示几天

IntRange(0, preMonthDayNum - 1).forEach { dates.add(preMonthDayCount - (preMonthDayNum - 1 - it)) } //填充上个月要显示的天数

IntRange(0, monthDayCount - 1).forEach { dates.add(it + 1) } //填充本月要显示的天数

IntRange(0, nextMonthDayNum - 1).forEach { dates.add(it + 1) } //填充下一月要显示的天数

return dates

}

现在我们来测试一下结果,如下:

public class ExampleUnitTest {

@Test

public void addition_isCorrect() throws Exception {

List data = DateUtil.computeDatesInCalendar(2017, 1);

for (int i = 0; i < data.size(); i++) {

System.out.print(data.get(i) + "\t");

if ((i + 1) % 7 == 0) {

System.out.print("\n");

}

}

assertEquals(4, 2 + 2);

}

}

结果输出:

25 26 27 28 29 30 31

1 2 3 4 5 6 7

8 9 10 11 12 13 14

15 16 17 18 19 20 21

22 23 24 25 26 27 28

29 30 31 1 2 3 4

结果我们已经算出来了,现在造UI轮子还难吗?下面是一个Git地址,存了使用该算法的一个demo,只是其中涉及到的公式选用的蔡勒(Zeller)公式。

Git项目地址(项目使用java代码编写): http://git.oschina.net/yugecse/CalendarWidget

项目总结:

工作中我们避免不了跟一定的数学公式打交道,尤其是一些UI定制,特殊计算等。因此熟悉一些公式的使用是很有必要的。还有就是注意观察我们需要实现的东西的细节,选择合适的方法进行分步骤解决。

android日历分析,kotlin - Android开发之日历篇(1)相关推荐

  1. Android KTX与Kotlin Android Extensions

    Android KTX Android KTX是Google官方推荐的一套便利的Android API扩展函数库.因还处于beta阶段,相关API并不丰富,但既然是出自JakeWharton大神之手, ...

  2. Android KTX 和 Kotlin android extension对比

    参考: KTX 和 Kotlin android extension 都到底是个啥? Android KTX KTX 是被称为Android之光的 JakeWharton 写的 Android KTX ...

  3. Android架构分析之Android消息处理机制(一)

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本号:4.4.2 在这个系列文章中我们将来分析Android消息处理机制. 本文介绍了一个使用Han ...

  4. kotlin android获取按钮,Kotlin Android按钮

    Android Button是用于在单击时执行事件的按钮.它是android.widget.Button类下的一个UI组件.要了解有关Android Button的更多信息, 请参阅Android B ...

  5. Android木马分析实验,Android木马简介与分析

    本文介绍基于Android的手机恶意软件,是一个基础性的介绍,给新入门的人提供一个分析和工具指引.要分析的木马是一个2013年的syssecApp.apk,这个木马的分析能对Android恶意软件有个 ...

  6. android crash分析工具,Android Crash之Native Crash分析

    前言 上一篇给大家介绍了Android Crash中的Java Crash分析,我们可以知道Java Crash一般会弹出提示框告诉我们程序崩溃了,通常使用Crash工具都能够捕获到:本篇博客来谈谈如 ...

  7. 转 android anr 分析示例,Android ANR 分析与解决

    今天解决了一个ANR异常问题,心情愉悦,原来ANR分析解决很简单(可能问题本来比较简单吧~~暂做参考) 三个步骤搞定: 步骤一:重现ANR 异常 对着界面一顿操作...直至弹出anr提示 步骤二:拉取 ...

  8. android逆向分析so,Android逆向——so反编译分析由浅入深(回帖奖励)

    如果可以,请自己编写so文件库,然后进行反编译自行学习.这样的进步是最快的. 这篇分析仅此一份,心血付出. 0x00 前言 说明 1.之前学习Android逆向的时候跑的太快,很多东西没有咀嚼直接吞咽 ...

  9. android fbe分析,(原创)Android FBE加密源码分析(二)

    上一篇最后讲到,dispatchCommand是通过调用runCommand来执行具体的CMD操作,这一篇会接着说明.在进行说明前,需要先了解FBE的一些内容,为什么需要这些内容呢?因为在接下来的分析 ...

  10. android 行车记录仪分析,基于Android的智能行车记录仪的设计与实现.doc

    基于Android的智能行车记录仪的设计与实现 摘要:移动互联网的出现,极大的方便了人们的衣食住行,同时人们当下对行车安全非常看重,因此设计一款智能行车记录仪APP便可以解决行车安全中的一些需求,如有 ...

最新文章

  1. nginx实现web负载均衡
  2. mysql concat 引号,在MySQL concat里面使用多个单引号,三引号的问题
  3. java面试精典问答
  4. 【PAT乙级】1080 MOOC期终成绩 (25 分)
  5. GDCM:把DICOM文件存在vector<char>里面的测试程序
  6. 如何在 Web Forms 中引入依赖注入机制
  7. 苦逼的程序员怎么发展
  8. MATLAB中的微积分运算(数值符号)
  9. wordpress模板-单栏整洁的个人博客Siren主题模板
  10. 华为手机卡在升级界面_你的华为手机变卡了,1分钟调整这3个功能,让手机再战3年...
  11. android 如何实现连接蓝牙打印机来实现打印功能
  12. Java面试题总结系列 Servlet
  13. 【正点原子STM32连载】第三十七章 触摸屏实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1
  14. “汇乐缘”:甜蜜的阴谋?【zt】
  15. 网传前端大神司徒正美突发病逝,再度思考健康与金钱
  16. 做word计算机海报图片,使用word制作宣传海报
  17. yarn add 添加依赖的各种类型(指定版本安装、git中安装、tgz包安装、文件夹安装)
  18. 排序算法——梳排序 Comb sort
  19. 风起亚洲公共云与VPS比较
  20. 法线向量扰动、副法线

热门文章

  1. [单片机框架][bsp层][cx32l003][bsp_system_clock] clock配置和使用
  2. jstl.jar和standar.jar包下载
  3. layui数据表格实现内容筛选的全选和反选功能
  4. Python爬虫实战+数据分析+数据可视化(汽车之家)
  5. 等额本息计算 java 代码
  6. python把英语句子成分字母_英语句子成分分析报告(最完整版)
  7. Deepin系统配置开机自启动
  8. Android系统启动源码分析
  9. 如果你热爱编码,就应该少写代码
  10. Python爬虫-微信定时消息发送