日历是一个常用控件,一般而言我们会通过两种途径来得到,其一是Android原生控件CalendarView,其二是在网上找一些第三方的日历控件。但这两种都有同样的不足,也即不好自定义,比如添给某段日期加个标记,或加一个中国农历,等等。最佳的,是自己写一个,可以完全自定义,无论以后项目怎么扩展,自己心里都有底。
分析一下,日期本身是”年月日“,但我们要写日历,最重要的是“周”,只要我们能够做到如下定位能力便可以轻易写出日历:1、通过年月日来标示某一天,通过加N或减N来得到之前或之后的第N天;2、根据某一天得到该周。这就够了。
在网上搜索,对Java7以下自带的time+date普遍评价不高,有一个叫org.joda.time的库受到推荐,并且恰恰具有前面两项定位能力。
我们只需要两步来实现日历:1、根据某天得到包含该天的周;2、根据某天得到包含该天的月。
第一步,根据某天得到该周:
public CalendarWeek getWeekOfDay(LocalDate localDate){
LocalDate monday = localDate.withDayOfWeek(DateTimeConstants.
MONDAY);
LocalDate tuesday = localDate.withDayOfWeek(DateTimeConstants.
TUESDAY);
LocalDate wednesday = localDate.withDayOfWeek(DateTimeConstants.
WEDNESDAY);
LocalDate thursday = localDate.withDayOfWeek(DateTimeConstants.
THURSDAY);
LocalDate friday = localDate.withDayOfWeek(DateTimeConstants.
FRIDAY);
LocalDate saturday = localDate.withDayOfWeek(DateTimeConstants.
SATURDAY);
LocalDate
sunday = localDate.withDayOfWeek(DateTimeConstants.
SUNDAY);
CalendarWeek calendarWeek =
new CalendarWeek(//生成周数据
monday,
tuesday,
wednesday,
thursday,
friday,
saturday,
sunday
);
calendarWeek.firstDayOfCurrentMonth = localDate.withDayOfMonth(1);//这个周归属哪个月
return calendarWeek;
}
里面最紧要的是一点firstDayOfCurrentMonth,因为我们要做的是月日历,所以需要一个定位器,就是每个月的第一天。
第二步,根据某天得到某个月:
public CalendarMonth getMonthOfDay(LocalDate localDate){
CalendarMonth calendarMonth =
new CalendarMonth();
calendarMonth.
firstDayOfCurrentMonth = localDate.withDayOfMonth(
1);
for(int i = 0; i<6; i++) {//每个月最多6周,最少4周
CalendarWeek w = getWeekOfDay(calendarMonth.firstDayOfCurrentMonth.plusDays(7 * i));
if(i <
4) {
calendarMonth.
calendarWeeks.addLast(w);
}
else if(w.
calendarDayList.getFirst().
day.getMonthOfYear() == calendarMonth.
firstDayOfCurrentMonth.getMonthOfYear()){
/**
* 从第5周开始检查
* 如果w的第一天的月份等于本月第一天的月份,那么这一周也算本月的周
*/
calendarMonth.
calendarWeeks.addLast(w);
}
else{
break;
}
}
return calendarMonth;
}
步骤是,首先得到这一天的月的第一天,然后利用前面第一步得到的方法获取本月第一天的周,然后本月第一天加7得到第二周的某天,据此获取第二周,类似加7直到第4周。
每个月至少有4周,可能有5周或6周,我们只要判断那一周的第一天是不是本月即可,是则添加到本月。
至此,我们已经得到月日历了。
假定我们要开始翻页到上一个月或下一个月怎么办呢?非常简单,我们用本月的第一天减一就得到上个月的某一天了,利用第二个步骤的方法重新获取那一天所属的月即可。同样,我们用本月第一天加32就得到下一月的某一天了,再次利用方法2即可。
由于某一周可能属于两个月,在显示周的时候我们要留意比对该天是否属于当前月。这里的当前月和前面的月的第一天相当于是同一个月定位器。
实际结果就是这样,在理清思路后,写一个日历控件比想象中要简单。
----------------------2016年10月4日修正:本文只是写了一个数据层面的日历类,而非view层面的控件。后者只需简单展示日历类。我写的数据+View的日历控件源码和图示在这里: https://github.com/maxyou/CalendarPicker