欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android 一個(gè)日歷控件的實(shí)現(xiàn)代碼

 更新時(shí)間:2017年05月19日 15:32:11   作者:Othershe  
本篇文章主要介紹了Android 一個(gè)日歷控件的實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

先看幾張動(dòng)態(tài)的效果圖吧!



項(xiàng)目地址:https://github.com/Othershe/CalendarView

這里主要記錄一下在編寫(xiě)日歷控件過(guò)程中一些主要的點(diǎn):

一、主要功能

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

二、基本結(jié)構(gòu)

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

三、計(jì)算每個(gè)MonthView需要填充的日期數(shù)據(jù)

從上邊的截圖可以看出,每個(gè)MonthView的日期數(shù)據(jù)應(yīng)該由上個(gè)月的后0~6天、當(dāng)前月的天數(shù)和下個(gè)月的前0~6天組成。首先計(jì)算出當(dāng)前月有多少天,這個(gè)簡(jiǎn)單,以及根據(jù)年月算出當(dāng)前月的第一天是星期幾:

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),按照如下偽碼則可計(jì)算出包含的上個(gè)月的日期:

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

至于包含的下個(gè)月的日期和當(dāng)前MonthView顯示的行數(shù)有關(guān),如果 當(dāng)前月的天數(shù)+week可以被7整除則不需要包含下月日期,否則需要計(jì)算包含的下月日期,偽碼如下:

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

這樣需要的日期數(shù)據(jù)就計(jì)算完了,詳細(xì)的算法可參考源碼。

四、 計(jì)算日歷的總頁(yè)數(shù)

總頁(yè)數(shù)應(yīng)由日歷的起始年月得到,其實(shí)就是確定ViewPager的總頁(yè)數(shù),這樣好理解點(diǎn)??砂凑杖缦路椒ㄓ?jì)算:

復(fù)制代碼 代碼如下:

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

其中dateStart、dateEnd是包含日歷開(kāi)始年月和結(jié)束年月的數(shù)組。這個(gè)count也是CalendarPagerAdapter必須的。

五、用position計(jì)算日期

PagerAdapter有個(gè)instantiateItem()方法:

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

來(lái)創(chuàng)建ViewPager的每一頁(yè),所以日歷每一頁(yè)也是在這里創(chuàng)建的,也就是MonthView,這里有個(gè)關(guān)鍵的點(diǎn)就是根據(jù) positon 參數(shù)推算出日歷每一頁(yè)對(duì)應(yīng)的年月,然后通過(guò)年月計(jì)算出當(dāng)前MonthView需要的日期數(shù)據(jù)。如何根據(jù)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代表日歷的其實(shí)年月。有了對(duì)應(yīng)的年月就可以用第二點(diǎn)中的方式計(jì)算日期數(shù)據(jù),然后填充到MothView中。

六、MothView

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

public Object instantiateItem(ViewGroup container, int position) {
    MonthView view = new MonthView(container.getContext());
    //根據(jù)position計(jì)算對(duì)應(yīng)年、月
    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;
  }

這里只保留了核心的代碼,當(dāng)日歷切換月份時(shí),會(huì)自動(dòng)根據(jù)position計(jì)算出對(duì)應(yīng)月份的日期數(shù)據(jù),然后傳給MonthView,最后將MonthView添加到ViewPager中。

七、切換月份選中日期

按照目前的設(shè)定,當(dāng)選擇當(dāng)前月的某天后,然后切換月份,新的月份中會(huì)找到上次選中的日期,并標(biāo)記為選中狀態(tài),如果找不到則選中新月份的最后一天。其實(shí)邏輯很簡(jiǎn)單,關(guān)鍵是如何在新月份中找到相應(yīng)的日期并選中。首先記錄上次選中的日期,由于ViewPager默認(rèn)會(huì)緩存兩頁(yè),再加上當(dāng)前頁(yè)共三頁(yè),在CalendarPagerAdapter中根據(jù)position保存三頁(yè)緩存,當(dāng)ViewPager切換到某一頁(yè)后會(huì)執(zhí)行如下回調(diào):

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

在onPageSelected(int position)方法中通過(guò)position從緩存中拿到對(duì)應(yīng)的MonthView,也是是切換到的頁(yè),這樣就能在MonthView中根據(jù)記錄的日期找到對(duì)應(yīng)的子日期View,然后更改為選中狀態(tài)。

八、多選

一個(gè)理想的多選功能應(yīng)該是在當(dāng)前月份選中多個(gè)日期后,切換到其它月份,之后回到有選中日期的月份依然能夠標(biāo)記出選中的日期,因?yàn)閂iewPager有默認(rèn)的三頁(yè)緩存,所以在當(dāng)前月份切換到上月或下月不會(huì)有什么問(wèn)題,但如果切換到前幾個(gè)月或后幾個(gè)月,再回到有選中日期的月份,由于之前緩存的頁(yè)面已經(jīng)被銷毀重建,所以選中的月份也就看不到了。我們的日期點(diǎn)擊事件在MonthView中,當(dāng)每次點(diǎn)擊選中時(shí)我們需要記錄對(duì)應(yīng)年月選中的日期,取消選中時(shí)要從記錄中刪除對(duì)應(yīng)日期,怎么保存呢?在CalendarView類中我們定義一個(gè)SparseArray

復(fù)制代碼 代碼如下:

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

其中的HashSet就是指定年月選中的日期,按照我們的規(guī)則設(shè)定不同年月轉(zhuǎn)換得到的position是唯一對(duì)應(yīng)的,所以我們用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);
    }
  }

之后就是在月份切換過(guò)程中,根據(jù)保存的日期數(shù)據(jù)刷新對(duì)應(yīng)的MonthView,實(shí)現(xiàn)選中狀態(tài)的恢復(fù),這個(gè)和第六點(diǎn)類似。

九、跳轉(zhuǎn)到指定日期

要跳轉(zhuǎn)到指定日期,首先要根據(jù)日期的年月計(jì)算出目標(biāo)MonthView在日歷中的position:

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

ViewPager有一個(gè)setCurrentItem(int item, boolean smoothScroll)方法,這樣就能跳轉(zhuǎn)到position對(duì)應(yīng)的MonthView,然后結(jié)合第六點(diǎn)的方法選中對(duì)應(yīng)的日期View。這樣跳轉(zhuǎn)到日歷設(shè)定日期范圍內(nèi)的任意一天都是沒(méi)問(wèn)題的。

十、自定義日歷樣式

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

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

基本可以滿足日常的需求,默認(rèn)的日期布局是陽(yáng)歷、陰歷垂直排列,節(jié)假日會(huì)覆蓋在農(nóng)歷上顯示,這個(gè)從上邊的靜態(tài)截圖可以看出。如果要使用其它的排列方式,例如水平排列等,就需要提供一個(gè)自定的layout(但目前只支持兩個(gè)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綁定一個(gè)接口,傳入lauoyt,然后返回一個(gè)代表陽(yáng)歷和農(nóng)歷的TextView數(shù)組。

十一、WeekView

我們將日期和星期的顯示功能分割開(kāi)了,所以CalendarView并不負(fù)責(zé)星期的顯示,WeekView是星期顯示的自定義View,從周日開(kāi)始依次是周一到周六,可通過(guò)自定義屬性來(lái)配置星期的顯示文字,以及文字的顏色、尺寸,這個(gè)還是相對(duì)簡(jiǎn)單,具體可見(jiàn)Github中的使用介紹。

十二、小結(jié)

這里我們只介紹了日歷的基本實(shí)現(xiàn)原理,和一些關(guān)鍵的點(diǎn),其實(shí)這種實(shí)現(xiàn)方式相對(duì)還是比較簡(jiǎn)單的,容易理解,當(dāng)然難免有不足的地方,后邊根據(jù)需要再逐步完善和擴(kuò)展吧。盡管Github上有許多現(xiàn)成的Calendar,但自己動(dòng)手實(shí)現(xiàn)一個(gè)還是收獲滿滿,一個(gè)看起來(lái)簡(jiǎn)單的東西,只有親自嘗試了才能體會(huì)到其中的滋味,最后希望對(duì)大家有所幫助吧!

相關(guān)文章

  • 簡(jiǎn)單實(shí)現(xiàn)Android驗(yàn)證碼

    簡(jiǎn)單實(shí)現(xiàn)Android驗(yàn)證碼

    在登錄或者注冊(cè)的時(shí)候要求輸入驗(yàn)證碼,這篇文章主要為大家詳細(xì)介紹了如何簡(jiǎn)單實(shí)現(xiàn)Android驗(yàn)證碼的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • Android使用Rotate3dAnimation實(shí)現(xiàn)3D旋轉(zhuǎn)動(dòng)畫(huà)效果的實(shí)例代碼

    Android使用Rotate3dAnimation實(shí)現(xiàn)3D旋轉(zhuǎn)動(dòng)畫(huà)效果的實(shí)例代碼

    利用Android的ApiDemos的Rotate3dAnimation實(shí)現(xiàn)了個(gè)圖片3D旋轉(zhuǎn)的動(dòng)畫(huà),圍繞Y軸進(jìn)行旋轉(zhuǎn),還可以實(shí)現(xiàn)Z軸的縮放。點(diǎn)擊開(kāi)始按鈕開(kāi)始旋轉(zhuǎn),點(diǎn)擊結(jié)束按鈕停止旋轉(zhuǎn)。
    2018-05-05
  • 實(shí)例詳解Android快速開(kāi)發(fā)工具類總結(jié)

    實(shí)例詳解Android快速開(kāi)發(fā)工具類總結(jié)

    這篇文章主要介紹了實(shí)例詳解Android快速開(kāi)發(fā)工具類總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2016-01-01
  • 探秘Android手勢(shì)事件機(jī)制與優(yōu)化技巧

    探秘Android手勢(shì)事件機(jī)制與優(yōu)化技巧

    在Android開(kāi)發(fā)中,手勢(shì)操作被廣泛應(yīng)用于各種應(yīng)用場(chǎng)景,如滑動(dòng)、雙擊等。本文將介紹Android手勢(shì)事件傳遞的原理,包括手勢(shì)事件的類型、分發(fā)機(jī)制和處理流程等內(nèi)容,并提供一些優(yōu)化用戶體驗(yàn)的技巧,需要的朋友可以參考下
    2023-06-06
  • Android實(shí)現(xiàn)圖片的裁剪(不調(diào)用系統(tǒng)功能)

    Android實(shí)現(xiàn)圖片的裁剪(不調(diào)用系統(tǒng)功能)

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)圖片的裁剪,不調(diào)用系統(tǒng)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • Android利用Gson解析嵌套多層的Json的簡(jiǎn)單方法

    Android利用Gson解析嵌套多層的Json的簡(jiǎn)單方法

    下面小編就為大家?guī)?lái)一篇Android利用Gson解析嵌套多層的Json的簡(jiǎn)單方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-08-08
  • Android使用recyclerview打造真正的下拉刷新上拉加載效果

    Android使用recyclerview打造真正的下拉刷新上拉加載效果

    這篇文章先介紹如何使用這個(gè)recyclerview,WZMRecyclerview 是一個(gè)集成了 下拉刷新、上拉加載、滑到底部自動(dòng)加載、添加刪除頭尾部 四個(gè)主要功能的recyclerview,需要的朋友可以參考下
    2016-11-11
  • Android Handler的詳細(xì)介紹

    Android Handler的詳細(xì)介紹

    Handler當(dāng)應(yīng)用程序啟動(dòng)時(shí),Android首先會(huì)開(kāi)啟一個(gè)主線程 (也就是UI線程) , 主線程為管理界面中的UI控件,進(jìn)行事件分發(fā)
    2013-09-09
  • RxJava入門指南及其在Android開(kāi)發(fā)中的使用示例

    RxJava入門指南及其在Android開(kāi)發(fā)中的使用示例

    RxJava是JVM的一個(gè)擴(kuò)展庫(kù),它能夠幫助Java更加方便地實(shí)現(xiàn)基于事件的編程,這對(duì)安卓來(lái)說(shuō)十分有用,接下來(lái)就一起來(lái)看一下RxJava入門指南及其在Android開(kāi)發(fā)中的使用示例:
    2016-06-06
  • Android自定義view實(shí)現(xiàn)滑動(dòng)解鎖效果

    Android自定義view實(shí)現(xiàn)滑動(dòng)解鎖效果

    這篇文章主要為大家詳細(xì)介紹了Android自定義view實(shí)現(xiàn)滑動(dòng)解鎖效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-05-05

最新評(píng)論