本篇學(xué)習(xí)的內(nèi)容
第一,recyclerview的多類型item的使用;
第二,calendar的基本使用與日期計算;
第三,DialogFragment的基本使用。
需求
最近公司項目需要一個選擇時間區(qū)間的控件,效果跟去哪兒網(wǎng)選擇住宿時間區(qū)間非常像,先來看看最終放入我項目中的效果圖(壓縮后圖片比較不清晰,請見諒,最終在移動端顯示效果比這個更佳)如下圖:



實現(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

底色是兩半邊,因為選中時開始與結(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