写一个Android日历控件

日历是一个常用控件,一般而言我们会通过两种途径来得到,其一是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(* 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

你可能感兴趣的:(Android)