1. 程式人生 > >還不瞭解Calendar?實現自定義Android日曆,看這篇就夠了

還不瞭解Calendar?實現自定義Android日曆,看這篇就夠了

Calendar

背景介紹

日曆對我的生活而言是一個容易被忽視,而又十分重要的東西。在Android中,我們也常常需要操作日曆去實現一些需求。比如根據日期獲取對應資料,或者承載了一些需求的自定義日曆。為了方便對日期的操作,誕生了Calendar 類。這大大簡化了我們的計算。
事實上,我們只需要知道如何操作Calendar就行了。本篇我們將一起來了解下Calendar,並且實現一個自定義日曆。

方便的Calendar類

Calendar是幹什麼的?

The Calendar class is an abstract class that provides methods for converting between a specific instant in time and a set of calendar fields such as YEAR, MONTH, DAY_OF_MONTH, HOUR, and so on, and for manipulating the calendar fields, such as getting the date of the next week.

上面是Android文件中對Calendar是什麼的簡單介紹。大概的意思就是說,Calendar是一個抽象類。它提供了一些用於一個具體時間和Calendar的欄位(比如,YEAR、MONTH、DAY_OF_MONTH、HOUR等)互相轉換的方法,以及對Calendar的欄位的操作方法(比如,獲取下一週的日期)。
綜上所述,Calendar就是一個操作日曆的工具類。

Calendar的使用

獲得一個Calendar例項

Calendar calendar = Calendar.getInstance(); //這個方法獲取到的是預設的Calendar例項。
//一般使用預設的就好,它會根據app執行的時區、語言環境自動建立相應的Calendar例項。
Calendar calendar = Calendar.getInstance(Locale.CHINA); //根據Locale來獲取相應的Calendar例項。 Calendar calendar = Calendar.getInstance(TimeZone.getDefaultRef(), Locale.CHINA); //根據TimeZone和Locale來獲取相應的Calendar例項。 //這些方法最終呼叫的都是createCalendar() private static Calendar createCalendar(TimeZone zone, Locale aLocale) { return
new GregorianCalendar(zone, aLocale); //Android中使用了GregorianCalendar。 //注意這裡每次都是new一個新的Calendar例項。 }

挑幾個方法特別說明下

  • set(f, value)

    1. 呼叫該方法可以把Calendar的某個欄位f的值設定為指定值value。
    2. 這裡需要注意:DAY_OF_MONTH欄位是從1開始,如果設定0,表示上個月的最後一天;MONTH欄位是從0開始,如果設定12,表示下一年的第一個月。
    3. set()方法設定後,僅僅是改變了Calendar的指定欄位的值,但是Calendar表示的日期並沒有重新計算。Calendar將會在下次呼叫get()、getTime()、getTimeInMillis()、add()或roll()方法時,真實的重新計算日期。
  • add(f, delta)
    效果等同於呼叫了set(f, get(f) + delta)l。

  • roll(f, delta)
    效果和add()有些類似。但是它的作用範圍限制在f欄位上,不會影響到其它欄位。舉個栗子。

Calendar calendar = Calendar.getInstance();
calendar.set(2016,1,1);
calendar.roll(Calendar.DAY_OF_MONTH, 32);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(dateFormat.format(calendar.getTime()));

輸出:

2016-01-01

看出來沒?它只在DAY_OF_MONTH這個欄位裡迴圈,而不會導致其它欄位發生變化。

  • getFirstDayOfWeek()
    這個方法用於獲取一個周的第一天是周幾。一般一個周的第一天是星期天,但法國的是星期一。

  • clone()
    這個方法是由Clonable介面提供的。可以克隆一個Calendar例項。這很有用,可以避免混亂的操作Calendar。

其它方法的使用請點選連結看

Calendar的方法使用大全

幾個特殊的規則

  • GregorianCalendar的時間原點是1970-01-01 00:00:00.000。所以通過getTimeInMillis()獲得的時間戳就是時間原點的偏移量。
  • 日期大小規則
    Thus, 23:59 on Dec 31, 1999 < 00:00 on Jan 1, 2000 < 00:01 on Jan 1, 2000.
    12:00 am (midnight) < 12:01 am, and 12:00 pm (noon) < 12:01 pm.

擼個自定義日曆

上個效果圖,比較簡陋,各位看官見諒啊。主要是為了說明下Calendar的使用。
日曆
主要使用了ViewPager + Fragment + RecyclerView完成。但這不是重點。重點是Calendar!所以我就放核心的程式碼。其它部分和大家平時使用的ViewPager + Fragment + RecyclerView也都差不多。且看~

protected void initData() {
    dateList.clear();

    initialDate = (Date) getArguments().getSerializable(KEY_DATE);  //獲取傳過來的Date,用於確定初始的calendar
    Calendar calendar = Calendar.getInstance(Locale.CHINA); //獲取China區Calendar例項,實際是GregorianCalendar的一個例項
    calendar.setTime(initialDate); //初始化日期
    int maxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);  //獲得當前日期所在月份有多少天(或者說day的最大值),用於後面的計算

    Calendar calendarClone = (Calendar) calendar.clone(); //克隆一個Calendar再進行操作,避免造成混亂
    calendarClone.set(Calendar.DAY_OF_MONTH, 1);  //將日期調到當前月份的第一天
    int startDayOfWeek = calendarClone.get(Calendar.DAY_OF_WEEK); //獲得當前日期所在月份的第一天是星期幾
    calendarClone.set(Calendar.DAY_OF_MONTH, maxDay); //將日期調到當前月份的最後一天
    int endDayOfWeek = calendarClone.get(Calendar.DAY_OF_WEEK); //獲得當前日期所在月份的最後一天是星期幾

    /**
     * 計算上一個月在本月日曆頁出現的那幾天.
     * 比如,startDayOfWeek = 3,表示當月第一天是星期二,所以日曆向前會空出2天的位置,那麼讓上月的最後兩天顯示在星期日和星期一的位置上.
     */
    int startEmptyCount = startDayOfWeek - 1; //上月在本月日曆頁因該出現的天數。
    Calendar preCalendar = (Calendar) calendar.clone();  //克隆一份再操作
    preCalendar.set(Calendar.DAY_OF_MONTH, 1); //將日期調到當月第一天
    preCalendar.add(Calendar.DAY_OF_MONTH, -startEmptyCount); //向前推移startEmptyCount天
    for (int i = 0; i < startEmptyCount; i++) {
      DateInfo dateInfo = new DateInfo(); //使用DateInfo來儲存所需的相關資訊
      dateInfo.setDate(preCalendar.getTime());
      dateInfo.setType(DateInfo.PRE_MONTH); //標記日期資訊的型別為上個月
      dateList.add(dateInfo); //將日期新增到陣列中
      preCalendar.add(Calendar.DAY_OF_MONTH, 1); //向後推移一天
    }

    /**
     * 計算當月的每一天日期
     */
    calendar.set(Calendar.DAY_OF_MONTH, 1); //由於是獲取當月日期資訊,所以直接操作當月Calendar即可。將日期調為當月第一天
    for (int i = 0; i < maxDay; i++) {
      DateInfo dateInfo = new DateInfo();
      dateInfo.setDate(calendar.getTime());
      dateInfo.setType(DateInfo.CURRENT_MONTH);  //標記日期資訊的型別為當月
      dateList.add(dateInfo);
      calendar.add(Calendar.DAY_OF_MONTH, 1); //向後推移一天
    }

    /**
     * 計算下月在本月日曆頁出現的那幾天。
     * 比如,endDayOfWeek = 6,表示當月第二天是星期五,所以日曆向後會空出1天的位置,那麼讓下月的第一天顯示在星期六的位置上。
     */
    int endEmptyCount = 7 - endDayOfWeek; //下月在本月日曆頁上因該出現的天數
    Calendar afterCalendar = (Calendar) calendar.clone(); //同樣,克隆一份在操作
    for (int i = 0; i < endEmptyCount; i++) {
      DateInfo dateInfo = new DateInfo();
      dateInfo.setDate(afterCalendar.getTime());
      dateInfo.setType(DateInfo.AFTER_MONTH); //將DateInfo型別標記為下個月
      dateList.add(dateInfo);
      afterCalendar.add(Calendar.DAY_OF_MONTH, 1); //向後推移一天
    }
  }

  //DateInfo有必要放一下。不過這僅代表我的思路。
  private static class DateInfo {
    private static final int PRE_MONTH = 1;
    private static final int CURRENT_MONTH = PRE_MONTH + 1;
    private static final int AFTER_MONTH = CURRENT_MONTH + 1;
    private static final int WEEK_TITLE = AFTER_MONTH + 1;

    private Date date;
    private int type;
    private String weekTitle;

    public Date getDate() {
      return date;
    }

    public void setDate(Date date) {
      this.date = date;
    }

    public int getType() {
      return type;
    }

    public void setType(int type) {
      this.type = type;
    }

    public String getWeekTitle() {
      return weekTitle;
    }

    public void setWeekTitle(String weekTitle) {
      this.weekTitle = weekTitle;
    }
  }

後面只需要把dateList 中的資料放到RecyclerView中就可以。

總結

到此想必大家也對Calendar有了個基本點的瞭解。平時用的比較多的就是get(),getTime(),set(),add(),roll()等方法,我在上面提了幾個特殊點的。其它的可以到上面提到那個連結裡熟悉,寫的比較詳細的。這波【農夫山泉】我就不搬了。

參考連結

  1. Android文件
  2. How Soft Works-Calendar