Android-仿支付寶的日期選擇頁
描述
參考支付寶所製作的日期選擇頁,效果如下:
知識點與難點
1、獲取指定月份有多少天
public static int getDayCountOfMonth(int year, int month) {
int[] arr = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int days = 0;
//如果是閏年,二月= 29天
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
arr[1] = 29 ;
}
try {
days = arr[month - 1];
} catch (Exception e) {
e.printStackTrace();
}
return days;
}
2、計算指定日期是周幾
/**
* 計算指定日期是周幾
* 1為週日,2為週一,3為週二,4為週三,5為週四,6為週五,7為週六
*/
public static int getDayOfWeekInMonth(int year, int month, int day) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month - 1 );
calendar.set(Calendar.DATE, day);
return calendar.get(Calendar.DAY_OF_WEEK);
}
3、基礎架構
整個專案的結構是RecyclerView巢狀RecyclerView:
紅色為外層RecyclerView,藍色為內部RecyclerView。
外層RecyclerView包含了固定頭部條目與月份RecyclerView。
每個月份RecyclerView對應了一個月份的日期展示。
4、資料儲存
關於日期資料,我們需要考慮到很多情況。
因為日期資料可能很大,幾年的資料甚至更多,如果傳遞來傳遞去會在記憶體中產生多份資料(根據引用地址不同可以看出)。
為了節省記憶體,我選擇使用單例模式來實現,並且是懶漢式單例模式,因為此功能使用者不一定每次開啟App都會使用。
這樣做的好處不僅是節省了記憶體、減少了來回傳遞資料的繁瑣操作,還有就是很方便地讓我們實現了回顯功能,從上面的Gif圖中也可以看出回顯功能。
那麼還有一個問題就是,什麼時候銷燬資料?
我的想法是,在使用者第一次開啟日期選擇頁時,進行資料的初始化,接著使用者反覆在結果展示頁和日期選擇頁來回切換時,用的都是這一批資料。
當用戶離開結果展示頁(提交、或者放棄)時,就要銷燬資料,清理記憶體,具體建立、銷燬邏輯可以檢視程式碼。
5、重新整理
關於每個日期的狀態初始化以及選擇後的重新整理,是比較麻煩的。
尤其是跨月、跨年選擇日期!因為我們的每個月是獨立的巢狀RecyclerView,當你跨月選擇時,你就需要重新整理多條內部RecyclerView。
為了解決這種情況,我在程式碼中使用了外部Adapter來進行條目的重新整理,並且不會呼叫全域性重新整理:adapter.notifyDataSetChanged();
選擇使用區域性重新整理:
adapter.notifyItemRangeChanged(int position, int count);
adapter.notifyItemChanged(int position);
不會去重新整理無關的條目。
還有一點要說的是,巢狀RecyclerView的高度是不固定的:可能有5行、6行,也可能有7行。
關於巢狀RecyclerView的高度問題,確實已經無需計算高度了,可以直接將內部RecyclerView的高度設定為wrap_content
。
之前需要動態計算高度,是因為RecyclerView相容包的Bug,不過已經在23.2.0之後修復了,具體參考這裡。
不過我在巢狀RecyclerView還是計算了高度,我嘗試了不計算高度,感覺效能遠不如計算高度,具體就需要看看原始碼了。
至此,難點基本已經介紹完了。
不過專案當中因為上述高度不固定的原因,產生了一個小問題:
在呼叫adapter.notifyItemChanged(int position)
時,當position為0或1時,有時會導致螢幕自動滾動至頂部,目前還沒有找到合適的解決方案,各位看官可以自己試一試,找找解決方案。