中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何在Android 開發中實現一個日歷控件

發布時間:2020-12-05 15:36:04 來源:億速云 閱讀:168 作者:Leah 欄目:移動開發

這期內容當中小編將會給大家帶來有關如何在Android 開發中實現一個日歷控件,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

一、主要功能

1、支持農歷、節氣、常用節假日
2、日期范圍設置,默認支持的最大日期范圍[1900.1~2049.12]
3、默認選中日期設置
4、單選、多選
5、跳轉到指定日期
6、通過自定義屬性定制日期外觀,以及簡單的日期item布局配置

二、基本結構

我們要實現的日歷控件采用ViewPager作為主框架,CalendarView繼承ViewPager,這樣就天生擁有左右滑動和緩存的功能。目前我們設定日歷左右滑動為月份切換的操作,每一個月份顯示通過自定義ViewGroup實現,也就是我們的MonthView,月份中的日期是通過layout布局解析出的View,根據月份的不同每個MonthView可能包含6 x 7或5 x 7個日期View,由于給ViewPager綁定數據需要通過PagerAdapter,所以繼承PagerAdapter我們擴展了一個CalendarPagerAdapter,來完成MonthView的相關初始化和日期數據的綁定。

三、計算每個MonthView需要填充的日期數據

從上邊的截圖可以看出,每個MonthView的日期數據應該由上個月的后0~6天、當前月的天數和下個月的前0~6天組成。首先計算出當前月有多少天,這個簡單,以及根據年月算出當前月的第一天是星期幾:

public static int getFirstWeekOfMonth(int year, int month) {
    Calendar calendar = Calendar.getInstance();
    calendar.set(year, month, 1);
    return calendar.get(Calendar.DAY_OF_WEEK) - 1;
  }

返回0代表周日,1~6代表周一到周六,以上邊的截圖為例,可以知道2017年5月的第一天是周一:week = getFirstWeekOfMonth(2017, 5-1),按照如下偽碼則可計算出包含的上個月的日期:

for (int i = 0; i < week; i++) {
      ld = 上個月天數 - week + 1 + i;
    }

至于包含的下個月的日期和當前MonthView顯示的行數有關,如果 當前月的天數+week可以被7整除則不需要包含下月日期,否則需要計算包含的下月日期,偽碼如下:

for (int i = 0; i < 7 * 顯示的行數 - 當月天數 - week; i++) {
      nd = i + 1;
    }

這樣需要的日期數據就計算完了,詳細的算法可參考源碼。

四、 計算日歷的總頁數

總頁數應由日歷的起始年月得到,其實就是確定ViewPager的總頁數,這樣好理解點。可按照如下方法計算:

復制代碼 代碼如下:

count = (dateEnd[0] - dateStart[0]) * 12 + dateEnd[1] - dateStart[1] + 1

其中dateStart、dateEnd是包含日歷開始年月和結束年月的數組。這個count也是CalendarPagerAdapter必須的。

五、用position計算日期

PagerAdapter有個instantiateItem()方法:

public Object instantiateItem(ViewGroup container, int position) {
    return instantiateItem((View) container, position);
  }

來創建ViewPager的每一頁,所以日歷每一頁也是在這里創建的,也就是MonthView,這里有個關鍵的點就是根據 positon 參數推算出日歷每一頁對應的年月,然后通過年月計算出當前MonthView需要的日期數據。如何根據position推算出年月呢?

public static int[] positionToDate(int position, int startY, int startM) {
    int year = position / 12 + startY;
    int month = position % 12 + startM;

    if (month > 12) {
      month = month % 12;
      year = year + 1;
    }

    return new int[]{year, month};
  }

其中startY、startM代表日歷的其實年月。有了對應的年月就可以用第二點中的方式計算日期數據,然后填充到MothView中。

六、MothView

前邊已經提到了,MonthView繼承ViewGroup,也就是日歷的每一頁,接收到日期數據后,在MonthView中根據數據構造對應的日期View,然后添加View到MonthView中,最后通過onMeasure、onLayout確定每個View最終大小和位置。到這里運行一個ViewPager的基本條件就滿足了,在上邊提到的instantiateItem()方法中完成MothView的初始化:

public Object instantiateItem(ViewGroup container, int position) {
    MonthView view = new MonthView(container.getContext());
    //根據position計算對應年、月
    int[] date = CalendarUtil.positionToDate(position, dateStart[0], dateStart[1]);
    view.setDateList(CalendarUtil.getMonthDate(date[0], date[1]), SolarUtil.getMonthDays(date[0], date[1]));
    container.addView(view);

    return view;
  }

這里只保留了核心的代碼,當日歷切換月份時,會自動根據position計算出對應月份的日期數據,然后傳給MonthView,最后將MonthView添加到ViewPager中。

七、切換月份選中日期

按照目前的設定,當選擇當前月的某天后,然后切換月份,新的月份中會找到上次選中的日期,并標記為選中狀態,如果找不到則選中新月份的最后一天。其實邏輯很簡單,關鍵是如何在新月份中找到相應的日期并選中。首先記錄上次選中的日期,由于ViewPager默認會緩存兩頁,再加上當前頁共三頁,在CalendarPagerAdapter中根據position保存三頁緩存,當ViewPager切換到某一頁后會執行如下回調:

addOnPageChangeListener(new SimpleOnPageChangeListener() {
      @Override
      public void onPageSelected(int position) {
      }
    });

在onPageSelected(int position)方法中通過position從緩存中拿到對應的MonthView,也是是切換到的頁,這樣就能在MonthView中根據記錄的日期找到對應的子日期View,然后更改為選中狀態。

八、多選

一個理想的多選功能應該是在當前月份選中多個日期后,切換到其它月份,之后回到有選中日期的月份依然能夠標記出選中的日期,因為ViewPager有默認的三頁緩存,所以在當前月份切換到上月或下月不會有什么問題,但如果切換到前幾個月或后幾個月,再回到有選中日期的月份,由于之前緩存的頁面已經被銷毀重建,所以選中的月份也就看不到了。我們的日期點擊事件在MonthView中,當每次點擊選中時我們需要記錄對應年月選中的日期,取消選中時要從記錄中刪除對應日期,怎么保存呢?在CalendarView類中我們定義一個SparseArray

復制代碼 代碼如下:

SparseArray<HashSet<Integer>> chooseDate = new SparseArray<>()

其中的HashSet就是指定年月選中的日期,按照我們的規則設定不同年月轉換得到的position是唯一對應的,所以我們用position作為SparseArray的key,最后在CalendarView中接收選中或取消選中的操作:

public void setLastChooseDate(int day, boolean flag) {
    HashSet<Integer> days = chooseDate.get(currentPosition);
    if (flag) {
      if (days == null) {
        days = new HashSet<>();
        chooseDate.put(currentPosition, days);
      }
      days.add(day);
    } else {
      days.remove(day);
    }
  }

之后就是在月份切換過程中,根據保存的日期數據刷新對應的MonthView,實現選中狀態的恢復,這個和第六點類似。

九、跳轉到指定日期

要跳轉到指定日期,首先要根據日期的年月計算出目標MonthView在日歷中的position:

public static int dateToPosition(int year, int month, int startY, int startM) {
    return (year - startY) * 12 + month - startM;
  }

ViewPager有一個setCurrentItem(int item, boolean smoothScroll)方法,這樣就能跳轉到position對應的MonthView,然后結合第六點的方法選中對應的日期View。這樣跳轉到日歷設定日期范圍內的任意一天都是沒問題的。

十、自定義日歷樣式

目前CalendarView提供的自定義屬性如下:

<declare-styleable name="CalendarView">
    <!--是否多選-->
    <attr name="multi_choose" format="boolean" />
    <!--是否顯示農歷-->
    <attr name="show_lunar" format="boolean" />
    <!--是否顯示上月和下月-->
    <attr name="show_last_next" format="boolean" />
    <!--是否顯示節假日-->
    <attr name="show_holiday" format="boolean" />
    <!--是否顯示節氣-->
    <attr name="show_term" format="boolean" />
    <!--開始日期(1990.1)-->
    <attr name="date_start" format="string" />
    <!--結束日期(2020.12)-->
    <attr name="date_end" format="string" />
    <!--默認展示、選中的日期(2016.10.1)-->
    <attr name="date_init" format="string" />
    <!--是否禁用默認選中日期前的所有日期-->
    <attr name="disable_before" format="boolean" />
    <!--陽歷的日期顏色-->
    <attr name="color_solar" format="color" />
    <!--陽歷的日期尺寸-->
    <attr name="size_solar" format="integer" />
    <!--農歷的日期顏色-->
    <attr name="color_lunar" format="color" />
    <!--農歷的日期尺寸-->
    <attr name="size_lunar" format="integer" />
    <!--節日文字顏色-->
    <attr name="color_holiday" format="color" />
    <!--選中的日期文字顏色-->
    <attr name="color_choose" format="color" />
    <!--選中的日期背景(圖片)-->
    <attr name="day_bg" format="reference" />
    <!--單選時切換月份,是否選中上次的日期-->
    <attr name="switch_choose" format="boolean" />
  </declare-styleable>

基本可以滿足日常的需求,默認的日期布局是陽歷、陰歷垂直排列,節假日會覆蓋在農歷上顯示,這個從上邊的靜態截圖可以看出。如果要使用其它的排列方式,例如水平排列等,就需要提供一個自定的layout(但目前只支持兩個TextView顯示)。例如:

calendarView.setOnCalendarViewAdapter(R.layout.item_layout, new CalendarViewAdapter() {
      @Override
      public TextView[] convertView(View view, DateBean date) {
        TextView solarDay = (TextView) view.findViewById(R.id.solar_day);
        TextView lunarDay = (TextView) view.findViewById(R.id.lunar_day);
        return new TextView[]{solarDay, lunarDay};
      }
    });

給CalendarView綁定一個接口,傳入lauoyt,然后返回一個代表陽歷和農歷的TextView數組。

上述就是小編為大家分享的如何在Android 開發中實現一個日歷控件了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

揭东县| 腾冲县| 安化县| 繁峙县| 浏阳市| 沁阳市| 普兰店市| 崇文区| 遵义县| 将乐县| 沙河市| 贵州省| 石首市| 务川| 隆子县| 长岭县| 碌曲县| 松阳县| 京山县| 双柏县| 南郑县| 三江| 宁德市| 桐城市| 武冈市| 南昌市| 南雄市| 衡阳市| 潜山县| 天津市| 南投县| 乃东县| 来安县| 昌吉市| 田东县| 海阳市| 太原市| 清镇市| 定州市| 高要市| 湟中县|