Android-仿支付寶的日期選擇頁

描述

參考支付寶所制作的日期選擇頁,效果如下:

效果展示

代碼已經(jīng)上傳至GitHub,點擊查看

知識點

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、基礎(chǔ)架構(gòu)

整個項目的結(jié)構(gòu)是RecyclerView嵌套RecyclerView:

項目結(jié)構(gòu)

紅色為外層RecyclerView,藍(lán)色為內(nèi)部RecyclerView。

外層RecyclerView包含了固定頭部條目與月份RecyclerView。

每個月份RecyclerView對應(yīng)了一個月份的日期展示。

4、數(shù)據(jù)存儲

關(guān)于日期數(shù)據(jù),我們需要考慮到很多情況。

因為日期數(shù)據(jù)可能很大,幾年的數(shù)據(jù)甚至更多,如果傳遞來傳遞去會在內(nèi)存中產(chǎn)生多份數(shù)據(jù)(根據(jù)引用地址不同可以看出)。

為了節(jié)省內(nèi)存,我選擇使用單例模式來實現(xiàn),并且是懶漢式單例模式,因為此功能用戶不一定每次打開App都會使用。

這樣做的好處不僅是節(jié)省了內(nèi)存、減少了來回傳遞數(shù)據(jù)的繁瑣操作,還有就是很方便地讓我們實現(xiàn)了回顯功能,從上面的Gif圖中也可以看出回顯功能。

那么還有一個問題就是,什么時候銷毀數(shù)據(jù)?

我的想法是,在用戶第一次打開日期選擇頁時,進(jìn)行數(shù)據(jù)的初始化,接著用戶反復(fù)在結(jié)果展示頁和日期選擇頁來回切換時,用的都是這一批數(shù)據(jù)。

當(dāng)用戶離開結(jié)果展示頁(提交、或者放棄)時,就要銷毀數(shù)據(jù),清理內(nèi)存,具體創(chuàng)建、銷毀邏輯可以查看代碼。

5、刷新

關(guān)于每個日期的狀態(tài)初始化以及選擇后的刷新,是比較麻煩的。

尤其是跨月、跨年選擇日期!因為我們的每個月是獨立的嵌套RecyclerView,當(dāng)你跨月選擇時,你就需要刷新多條內(nèi)部RecyclerView。

為了解決這種情況,我在代碼中使用了外部Adapter來進(jìn)行條目的刷新,并且不會調(diào)用全局刷新:adapter.notifyDataSetChanged();

選擇使用局部刷新:

adapter.notifyItemRangeChanged(int position, int count);
adapter.notifyItemChanged(int position);

不會去刷新無關(guān)的條目。

還有一點要說的是,嵌套RecyclerView的高度是不固定的:可能有5行、6行,也可能有7行。

關(guān)于嵌套RecyclerView的高度問題,確實已經(jīng)無需計算高度了,可以直接將內(nèi)部RecyclerView的高度設(shè)置為wrap_content

之前需要動態(tài)計算高度,是因為RecyclerView兼容包的Bug,不過已經(jīng)在23.2.0之后修復(fù)了,具體參考這里

不過我在嵌套RecyclerView還是計算了高度,我嘗試了不計算高度,感覺性能遠(yuǎn)不如計算高度,具體就需要看看源碼了。

至此,難點基本已經(jīng)介紹完了。

不過項目當(dāng)中因為上述高度不固定的原因,產(chǎn)生了一個小問題:

在調(diào)用adapter.notifyItemChanged(int position)時,當(dāng)position為0或1時,有時會導(dǎo)致屏幕自動滾動至頂部,目前還沒有找到合適的解決方案,各位看官可以自己試一試,找找解決方案。

源碼在這里。

感謝

其中RecyclerView的頭部固定引用了sticky-headers-recyclerview庫。

Android日期顯示和選擇庫學(xué)到了很多東西。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容