自定義日歷控android,Android 一個日歷控件的實現小記

304c8e70d0bd

先看幾張動態的效果圖吧!

304c8e70d0bd

304c8e70d0bd

304c8e70d0bd

這里主要記錄一下在編寫日歷控件過程中一些主要的點:

一、主要功能

1、支持農歷、節氣、常用節假日

2、日期范圍設置,默認支持的最大日期范圍[1900.1~2049.12]

3、禁用日期范圍設置

4、初始化選中單個或多個日期

5、單選、多選操作

6、跳轉到指定日期

7、替換農歷為指定文字

8、通過自定義屬性定制日期外觀,以及簡單的日期item布局配置

9、......

二、基本結構

我們要實現的日歷控件采用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> chooseDate = new SparseArray<>()

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

public void setChooseDate(int day, boolean flag, int position) {

if (position == -1) {

position = currentPosition;

}

HashSet days = chooseDate.get(position);

if (flag) {

if (days == null) {

days = new HashSet<>();

chooseDate.put(position, days);

}

days.add(day);

positions.add(position);

} 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提供的自定義屬性如下:

屬性名

格式

描述

默認值

choose_type

enum

設置單選(single)、多選(multi)

single

show_lunar

boolean

是否顯示農歷

true

show_last_next

boolean

是否在MonthView顯示上月和下月日期

true

show_holiday

boolean

是否顯示節假日

true

show_term

boolean

是否顯示節氣

true

switch_choose

boolean

單選時切換月份,是否選中上次的日期

true

solar_color

color

陽歷日期的顏色

solar_size

integer

陽歷的日期尺寸

14

lunar_color

color

農歷的日期顏色

lunar_size

integer

農歷的日期尺寸

8

holiday_color

color

節假日、節氣的顏色

choose_color

color

選中的日期顏色

day_bg

reference

選中的日期背景(圖片)

CalendarView相關方法:

方法名

描述

setInitDate(String date)

設置日歷的初始顯示年月

setStartEndDate(String startDate, String endDate)

設置日歷開始、結束年月

setDisableStartEndDate(String startDate, String endDate)

設置日歷的禁用日期范圍(小于startDate、大于endDate禁用)

setSpecifyMap(HashMap map)

將顯示農歷的區域替換成指定文字

setSingleDate(String date)

設置單選時初始選中的日期(不設置則不默認選中)

getSingleDate()

得到單選時選中的日期

setMultiDate(List dates)

設置多選時默認選中的日期集合

getMultiDate()

得到多選時選中的全部日期

toSpecifyDate(int year, int month, int day)

單選時跳轉到指定年月日

setOnCalendarViewAdapter(int layoutId, CalendarViewAdapter adapter)

設置自定義日期item樣式

init()

日期初始化(以上屬性配置完后調用)

setOnPagerChangeListener(OnPagerChangeListener listener)

設置月份切換回調

setOnSingleChooseListener(OnSingleChooseListener listener)

設置單選回調

setOnMultiChooseListener(OnMultiChooseListener listener)

設置多選回調

today()

單選時跳轉到今天

nextMonth()

跳轉到下個月

lastMonth()

跳轉到上個月

nextYear()

跳轉到下一年的當前月

lastYear()

跳轉到上一年的當前月

toStart()

跳轉到日歷的開始年月

toEnd()

跳轉到日歷的結束年月

CalendarUtil.getCurrentDate()

獲得當前日期(今天)

默認的日期布局是陽歷、陰歷垂直排列,節假日會覆蓋在農歷上顯示,這個從上邊的靜態截圖可以看出。如果要使用其它的排列方式,例如水平排列等,就需要提供一個自定的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數組。

十一、WeekView

我們將日期和星期的顯示功能分割開了,所以CalendarView并不負責星期的顯示,

WeekView是星期顯示的自定義View,從周日開始依次是周一到周六,可通過自定義屬性來配置星期的顯示文字,以及文字的顏色、尺寸,這個還是相對簡單,具體可見Github中的使用介紹。

十二、小結

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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/529112.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/529112.shtml
英文地址,請注明出處:http://en.pswp.cn/news/529112.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python先返回再處理_python xpath解析返回對象怎么處理

3 4 5 text 6 7 ... 8 ... 9 ......10 11 12 ...13 ...14 ......15 16 17 18

android文件讀取工具類,Android 下讀取Assets Properties操作封裝工具類

Android 下讀取Assets Properties操作封裝工具類發布時間&#xff1a;2018-06-03作者&#xff1a;laosun閱讀(2081)為了方便使用&#xff0c;首先創建BaseApplication類&#xff0c;如下所示&#xff1a;import android.app.Application;import android.content.Context;/*** C…

python粘性拓展_如何將tkinter小部件置于粘性框架中

在google中使用“如何使tkinter網格擴展”&#xff0c;我遇到了這個問題。 引用布萊恩奧克利的話Rows and columns have "weight" which describes how they grow or shrink to fill extra space >in the master. By default a row or column has a weight of zer…

android 固件 編輯器,RK3288編譯 Android 5.1 固件

1 準備工作編譯 Android 對機器的配置要求較高&#xff1a;64 位 CPU16GB 物理內存交換內存30GB 空閑的磁盤空間用于構建&#xff0c;源碼樹另外占用大約 25GBUbuntu 14.04 操作系統八核i7&#xff0c;編譯完成需要一個半小時安裝 JDK 7:sudo apt-get install openjdk-7-jdkUbu…

python解壓到指定文件夾_在Python中壓縮和解壓文件

Python部落(python.freelycode.com)組織翻譯&#xff0c;禁止轉載&#xff0c;歡迎轉發。 如果你已經使用計算機一段時間&#xff0c;你可能遇到了.zip擴展名的文件。它們是可以保存許多其他文件&#xff0c;文件夾和子文件夾的壓縮內容的特殊文件。這種類型的文件在使用互聯網…

android bar布局,Android學習路線(十)如何將Action Bar疊放在你的布局上

默認狀況下&#xff0c;action bar出如今activity窗口的頂部&#xff0c;略微減小了activity布局的總空間。若是你想隱藏或者顯示action bar&#xff0c;在這堂用戶體驗的課程中&#xff0c;你能夠經過調用htmlFigure 1. Gallerys action bar in overlay mode.android為了不在a…

geant4運行例子_Geant4--一次編譯,運行多個Run,極大提升模擬效率

文|梁佐佐應唐光毅博士/后之約&#xff0c;對于Geant4模擬&#xff0c;我們看是否能解決這么一個問題&#xff1a;我現在想模擬探測器不同角度下的響應&#xff0c;每次模擬需要/run/beamOn 100&#xff0c; 可是我真的不想一遍一遍的去http://DetectorConstruction.cc中修改幾…

python3.7基礎教程_關于本教程 |《Python 官方文檔:入門教程 3.7.0》| Python 技術論壇...

本文檔最新版為 3.8&#xff0c;舊版本可能放棄維護&#xff0c;推薦閱讀最新版&#xff01; Python 入門教程 Python 是一門簡單易學且功能強大的編程語言。它擁有高效的高級數據結構&#xff0c;并能夠用簡單又有效的方式進行面向對象編程。Python 優雅的語法和動態類型&…

android listview countdowntimer,Android-ListView中的CountDownTimer隨機閃爍

我正在使用計時器制作列表視圖&#xff0c;每個計時器都有不同的截止日期&#xff0c;具體取決于數據庫(類似于拍賣)Time now new Time();now.setToNow();now.normalize(true);nowMillis now.toMillis(true);..String endtime a.get(position).get(TAG_ENDTIME);Integer tim…

echart實現3d地圖_3D飛線效果——讓線“飛”起來的秘密

在城市規劃、統計、交通等行業&#xff0c;地圖可視化已成為最直接也最吸引眼球的一種表達方式。例如人群遷徙、人口流動、OD出行、職住分析、客流來源等眾多場景都需要用到飛線效果呈現。2D飛線效果圖隨著可視化技術的進一步發展&#xff0c;傳統的2D飛線效果略顯單調&#xf…

ad域管理與維護_在NAS SMB卷上使用VisualSVN Server維護代碼庫

VisualSVN Server[1] 是 Windows 平臺上流行的 SVN 形式的代碼管理工具。以下我們將介紹把 NAS SMB 卷作為 VisualSVN 代碼庫存儲中心時會遇到的幾個問題以及相應的解決方法。1. 安裝錯誤的解決方法我們以 VisualSVN Server 3.3.1 版本為例&#xff0c;在安裝 VisualSVN Server…

android 開發art,Android應用開發之Android 系統啟動原理(art 虛擬機)

本文將帶你了解Android應用開發之Android 系統啟動原理(art 虛擬機)&#xff0c;希望本文對大家學Android有所幫助。Android 系統啟動原理(art 虛擬機)一、虛擬機的啟動Android 是一個 Linux 的虛擬機&#xff0c;當虛擬機啟動的時候&#xff0c;會執行手機根目錄下的 init.r…

電腦文件夾可以分屏的軟件_電腦上什么便簽軟件可以添加音頻?

提及便簽&#xff0c;很多人都會很自然地想到手機便簽。這是因為隨著智能手機和移動互聯網的發展&#xff0c;現在很多手機上都有了系統自帶的便簽app。其實&#xff0c;除了手機便簽外&#xff0c;還有電腦便簽呢&#xff01;這不&#xff0c;Win7及其以上版本的電腦上還有系統…

jsp form提交到后臺中文亂碼_JSP與servlet之間的數據傳遞

【51】Jsp與Servlet之間的傳值有兩種&#xff0c;一種是Jsp傳值給Sevlet&#xff0c;另一種是Servlet傳值給Jsp&#xff1b;使用request、response對象完成傳值&#xff0c;具體實現如下&#xff1a;Jsp與Servlet之間的傳值有兩種&#xff0c;一種是Jsp傳值給Sevlet&#xff0c…

android jni 中jnienv,android JNI中JNIEnv類型和jobject類型的解釋

JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv *env, jobject obj){cout<}對於這個方法參數中的JNIEnv* env參數的解釋:JNIEnv類型實際上代表了Java環境&#xff0c;通過這個JNIEnv* 指針&#xff0c;就可以對Java端的代碼進行操作。例如&#xff0c;…

yang模型中rpc_領域驅動模型(DDD)設計講解

一. 什么是領域驅動模型(DDD)&#xff1f;領域驅動模型一種設計思想&#xff0c;我們又稱為DDD設計思想。是一種為了解決傳統設計思想帶來的維護困難&#xff0c;溝通困難和交互困難而產生的一種新的思想。也解決了在部分公司中&#xff0c;一個項目組就是一套服務&#xff0c;…

鴻蒙系統操作界面跟蘋果很像,鴻蒙手機UI界面曝出!圖標擬物化、操作邏輯近似蘋果iOS13...

原標題&#xff1a;鴻蒙手機UI界面曝出&#xff01;圖標擬物化、操作邏輯近似蘋果iOS13?【IT爆料王-原創文章-具備版權效力】就在近日&#xff0c;筆者收到了網友的匿名私信&#xff0c;提供給筆者華為鴻蒙系統的UI界面截圖&#xff0c;以及搭載鴻蒙系統的華為手機的曝光圖片。…

python3中的int類型占64位,有沒有什么辦法來強制Python來使用64位整數的Windows?

I’ve noticed that whenever any integer surpasses 2^31-1 my number heavy code suffers a large slowdown, despite the fact I’m using a 64 bit build of Python on a 64bit version of Windows. This seems to be true on Python 2.7 and Python 3. I’ve read that Wi…

crtsiii型無砟軌道板_無砟軌道裂縫破損怎么修補

隨著高速鐵路、客運專線、城市地鐵的快速發展&#xff0c;無砟軌道軌道板&#xff08;道床板&#xff09;廣泛應用&#xff0c;但施工中和運營期都發現軌道板混凝土存在不同程度的微細裂縫&#xff0c;對無砟軌道造成了一定的病害。高鐵軌道板裂縫是不可避免的。為確保無砟軌道…

c調用python第三方庫_Python使用ctypes模塊調用DLL函數之C語言數組與numpy數組傳遞...

在Python語言中&#xff0c;可以使用ctypes模塊調用其它如C語言編寫的動態鏈接庫DLL文件中的函數&#xff0c;在提高軟件運行效率的同時&#xff0c;也可以充分利用目前市面上各種第三方的DLL庫函數&#xff0c;以擴充Python軟件的功能及應用領域&#xff0c;減少重復編寫代碼、…