仿去哪兒選擇日期區(qū)間控件從0到1的實現(xiàn)

本篇學(xué)習(xí)的內(nèi)容
第一,recyclerview的多類型item的使用;
第二,calendar的基本使用與日期計算;
第三,DialogFragment的基本使用。

需求

最近公司項目需要一個選擇時間區(qū)間的控件,效果跟去哪兒網(wǎng)選擇住宿時間區(qū)間非常像,先來看看最終放入我項目中的效果圖(壓縮后圖片比較不清晰,請見諒,最終在移動端顯示效果比這個更佳)如下圖:


選擇開始后
選擇開始與結(jié)束后
開始與結(jié)束為同一天

實現(xiàn)思路

第一步,日歷主體實現(xiàn)

首先,利用recycerview的多item布局實現(xiàn)日歷主體部分,其中,有兩種item類型;
第一種,月份;
這個簡單,不做過多說明
第二種,日期。
有GridLayoutManager實現(xiàn),設(shè)置SpanCount為7,注意空白數(shù)據(jù)的填充和每個item的樣式類型。

既然數(shù)據(jù)itme有兩種類型,那么數(shù)據(jù)源也是會有兩種類型的,要顯示的數(shù)據(jù)體為了方便咱們可以用一個object類型,就可以匹配itme不同的數(shù)據(jù)類型了

第二步,就是利用calendar計算出這個recyclerview的數(shù)據(jù)源

1.先看今天所在月份的第一天為星期幾;
2.填充空白日期;
3.然后循環(huán)12次,也就是12個月份;
4.每次月份循環(huán)時,看看當(dāng)前月的天數(shù),然后循環(huán)天數(shù),添加數(shù)據(jù);
5.每次天數(shù)循環(huán)后,利用calendar增加1;(最后,注意添加節(jié)日)

另外一點

item中的日期布局,我想講一下,每個item


日期item

底色是兩半邊,因為選中時開始與結(jié)束的itme只需要顯示半邊顏色。

這個布局畫一條中心線,然后從中間分開,利用在左在右的布局形式分別設(shè)置兩個view。節(jié)日在選中情況下,會變成開始字樣,這時注意變色。

實現(xiàn)過程

利用DialogFragment實現(xiàn)從下往上彈出框效果

在DialogFragment的onCreateView方法中初始化view布局,并且設(shè)置相應(yīng)的參數(shù)動畫,代碼注釋已經(jīng)很清晰。

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    // 去掉默認title
    this.getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
    // 外部點擊是否可以收起該dialog
    getDialog().setCanceledOnTouchOutside(false);

    Window window = this.getDialog().getWindow();
    if (window != null) {
        //去掉dialog默認的padding
        window.getDecorView().setPadding(0, 0, 0, 0);
        WindowManager.LayoutParams lp = window.getAttributes();
        lp.width = WindowManager.LayoutParams.MATCH_PARENT;
        // 設(shè)置高度為屏幕高度的四分之三
        lp.height = UIUtils.getScreenHeight(getActivity()) * 3 / 4;
        //設(shè)置dialog的位置在底部
        lp.gravity = Gravity.BOTTOM;
        //設(shè)置dialog的動畫
        lp.windowAnimations = R.style.AnimBottom;
        window.setAttributes(lp);
        window.setBackgroundDrawable(new ColorDrawable());
    }

    View view = inflater.inflate(R.layout.dialog_choose_date, container, false);

    initView(view);
    initRecyclerView();
    initListener();
    initData();

    return view;
}
初始化recyclerview
final GridLayoutManager layoutManager = new GridLayoutManager(getActivity(), 7);
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
    @Override
    public int getSpanSize(int position) {
        // 這個item占有幾個位置
        return (mChooseRecyclerAdapter.getItemViewType(position)
                == ChooseDateRecyclerAdapter.TYPE_YEAR_MONTH ? layoutManager.getSpanCount() : 1);
    }
});

mDateRecyclerView.setAdapter(mChooseRecyclerAdapter);
mDateRecyclerView.setLayoutManager(layoutManager);
mDateRecyclerView.setItemAnimator(new DefaultItemAnimator());

如上所示,GridLayoutManager可以設(shè)置一行劃分為幾個區(qū)域來顯示幾個item,然后在setSpanSizeLookup方法中設(shè)置當(dāng)前item類型可以占據(jù)幾個區(qū)域以此來調(diào)節(jié)item的顯示寬度。

如果一行中顯示區(qū)域不足以顯示該item的長度,則會另起一行。所以這個控件用起來是相當(dāng)靈活而且爽歪歪。

初始化適配器數(shù)據(jù)

這里其實是整個實現(xiàn)過程中的一個難點,咱們按照上面的思路來實現(xiàn)數(shù)據(jù)的初始化。

設(shè)置當(dāng)當(dāng)前月份的第一天。

Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.DAY_OF_MONTH, 1);
// 添加月份數(shù)據(jù)
mData.add(new ChooseDateBean(1, String.valueOf(calendar.get(Calendar.MONTH) + 1) + "月"));

然后查看第一天是星期幾,設(shè)置相應(yīng)的空白數(shù)據(jù)。

int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
for (int j = 1; j < dayOfWeek; j++) {
    DateOfDayBean bean = new DateOfDayBean();
    bean.setDayOfMonth("");
    mData.add(new ChooseDateBean(2, bean));
}

獲取當(dāng)前月份的天數(shù)

private int getDayCountByYearAndMonth(int year, int month) {
    int days = 30;
    switch (month) {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            days = 31;
            break;
        case 4:
        case 6:
        case 9:
        case 11:
            days = 30;
            break;
        case 2:
            if (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)) {
                days = 29;
            } else {
                days = 28;
            }
            break;
    }
    return days;
}

循環(huán)遍歷當(dāng)前月份天數(shù),添加相應(yīng)的數(shù)據(jù)(因為代碼最終會全部上傳,所以這里只寫重要部分來講解)

DateOfDayBean bean = new DateOfDayBean();
bean.setDayOfMonth(String.valueOf(calendar.get(Calendar.DAY_OF_MONTH)));
bean.setCalendar(calendar);

這里我遇到了一個坑,就是計算今天的時候,用了calendar.compareTo(Calendar.getInstance()));這個方法,這個方法其實比較算了時分秒,完全相同的時間戳才會相等,后面該用判斷年月日相同就認為是同一天了。

點擊邏輯部分

這個部分也是難點之一,點擊分為三個部分來進行判斷顯示:

選了開始,沒選結(jié)束
選了開始與結(jié)束
什么都沒有選

其中選了開始與結(jié)束,還有一種樣式就是開始與結(jié)束為同一天,這種需要區(qū)分對待。詳細見代碼。

完整的項目地址如下:
github項目地址,歡迎star

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容