摘要:但是如果用戶先確定了日,再去選擇月的話,之后月份改變,日數(shù)就會重新從開始。
盡管Android給我們提供了時(shí)間選擇控件DatePicker和TimePicker(它們的使用方法可以參考我的這篇文章Android之日期時(shí)間選擇控件DatePicker和TimePicker),但無奈我的項(xiàng)目主色調(diào)是土豪金和高級黑,原生的控件用在里面顯得格格不入,特別是為了兼容低版本的系統(tǒng)之后顯示的是2.x年代的風(fēng)格,不但是簡陋,簡直是丑陋了。要解決這種問題,就只有走自定義控件這條道。但我目前還是不太熟悉自定義控件的寫法,所以只好發(fā)揮拿來主義了。查看了一通之后,發(fā)現(xiàn)liuwan1992這位博主寫的非常漂亮,我在他的基礎(chǔ)上做了一些改動(dòng),使得整個(gè)控件更符合我的項(xiàng)目。
這是他的文章鏈接:Android 好看的自定義滾動(dòng)式日期選擇控件 ,關(guān)于控件的使用大家直接閱讀他的文章即可。在此,感謝他的付出,本人只是巨人肩膀上的小白而已。
1、創(chuàng)建工程你可以下載博主的源碼,用Android Studio打開之后就直接動(dòng)手修改,也可以像我這樣新建一個(gè)工程,然后將需要用到的代碼和文件從源碼復(fù)制過來即可。
2、修改對話框外觀由于我需要用到我自己的顏色,所以在colors.xml中做了一些改動(dòng):
#3F51B5 #303F9F #FF4081 #F0F0F0 //年、月、日等單位的字體顏色#FFFFFF #B0B0B0 //未被選中的數(shù)字顏色#666666 #57C5E8 #b0000000 //作為背景的高級黑#ffda53 //作為字體的土豪金
其中color_bg和color_gold分別是背景高級黑和字體土豪金。
資源文件準(zhǔn)備好之后,就可以到對話框的布局custom_date_picker.xml中修改背景和字體顏色了。滾輪中選中和未選中的字體顏色則需要到DatePickerView中修改:
private void init() { timer = new Timer(); mDataList = new ArrayList<>(); //第一個(gè)paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Style.FILL); mPaint.setTextAlign(Align.CENTER); //被選中的數(shù)字顏色 mPaint.setColor(ContextCompat.getColor(context, R.color.color_gold)); //第二個(gè)paint nPaint = new Paint(Paint.ANTI_ALIAS_FLAG); nPaint.setStyle(Style.FILL); nPaint.setTextAlign(Align.CENTER); //未選中的數(shù)字顏色 nPaint.setColor(ContextCompat.getColor(context, R.color.color_text_unselected)); }
修改完的效果如下,項(xiàng)目需求如此,如果覺得辣眼睛的請多多諒解。
3、給控件增加標(biāo)題雖然選擇的都是時(shí)間,但是如果我們在控件的標(biāo)題中告訴用戶當(dāng)前要選擇的是什么時(shí)間的話就更好一點(diǎn)。比如用戶要確定一個(gè)時(shí)間段的話,我們就可以在彈出的控件上顯示要選擇的是“起始時(shí)間”還是“結(jié)束時(shí)間”。因此,我們可以在CustomDatePicker的構(gòu)造函數(shù)中再增加一個(gè)參數(shù):
public CustomDatePicker(Context context,String title, ResultHandler resultHandler, String startDate, String endDate) { }
這樣在創(chuàng)建控件時(shí)我們就可以把控件的標(biāo)題傳進(jìn)去了。
4、點(diǎn)擊控件以外的區(qū)域讓控件消失這個(gè)修改比較簡單,我們的時(shí)間選擇控件其實(shí)就是一個(gè)Dialog,只要將setCancelable方法設(shè)置為true就可以了。
5、修改選擇順序這個(gè)控件的時(shí)間選擇順序默認(rèn)是“年-月-日-時(shí)-分”,也就是選好了年才能選擇月,選好了月才能選擇日,從左到右。但是如果用戶先確定了日,再去選擇月的話,之后月份改變,日數(shù)就會重新從1開始。用戶體驗(yàn)無疑是十分糟糕的,所以我們要把用戶的每一步選擇都保存下來,使控件可以從右到左選擇時(shí)間。毫無疑問,這就需要到CustomDatePicker中改動(dòng)了,因此,這一步的修改是最復(fù)雜也是最關(guān)鍵的。
5.1 保存用戶選好的時(shí)間數(shù)據(jù)打開CustomDatePicker文件,創(chuàng)建以下幾個(gè)String變量:
private String currentMon, currentDay, currentHour, currentMin; //當(dāng)前選中的月、日、時(shí)、分
年份位于最左端,“權(quán)限”是最高的,不會受其它數(shù)據(jù)的影響,可以不必保存。仔細(xì)想想,我們需要在兩種情況下保存選中的日期數(shù)據(jù):
用戶滑動(dòng)滾輪之后,保存選擇的數(shù)據(jù);
用戶沒有滑動(dòng)滾輪,但是點(diǎn)擊了對話框上的“確認(rèn)”按鈕
源碼中,對于每個(gè)滾輪的滾動(dòng)都寫了監(jiān)聽方法,我們可以在每個(gè)監(jiān)聽方法中賦值:
private void addListener() { year_pv.setOnSelectListener(new DatePickerView.onSelectListener() { @Override public void onSelect(String text) { selectedCalender.set(Calendar.YEAR, Integer.parseInt(text)); monthChange(); } }); month_pv.setOnSelectListener(new DatePickerView.onSelectListener() { @Override public void onSelect(String text) { selectedCalender.set(Calendar.DAY_OF_MONTH, 1); selectedCalender.set(Calendar.MONTH, Integer.parseInt(text) - 1); currentMon = text; //保存選擇的月份 dayChange(); } }); day_pv.setOnSelectListener(new DatePickerView.onSelectListener() { @Override public void onSelect(String text) { selectedCalender.set(Calendar.DAY_OF_MONTH, Integer.parseInt(text)); currentDay = text;//保存選擇的日期 hourChange(); } }); hour_pv.setOnSelectListener(new DatePickerView.onSelectListener() { @Override public void onSelect(String text) { selectedCalender.set(Calendar.HOUR_OF_DAY, Integer.parseInt(text)); currentHour = text; //保存選擇的小時(shí) minuteChange(); } }); minute_pv.setOnSelectListener(new DatePickerView.onSelectListener() { @Override public void onSelect(String text) { selectedCalender.set(Calendar.MINUTE, Integer.parseInt(text)); currentMin = text; //保存選擇的分鐘 } }); }
如果用戶沒有滑動(dòng)滾輪,那么滾動(dòng)監(jiān)聽事件就不會觸發(fā),這時(shí)就要到設(shè)置默認(rèn)選中時(shí)間的方法setSelectedTime方法中去賦值了,比如分鐘的賦值:
currentMin = timeStr[1]; //保存選擇的分鐘
其它的時(shí)間數(shù)據(jù)同理,代碼太長,這里就不貼了,大家直接看Demo吧。
5.2 時(shí)間數(shù)據(jù)之間的聯(lián)動(dòng)邏輯保存好數(shù)據(jù)之后還沒有完,要知道,各個(gè)時(shí)間數(shù)據(jù)之間并不是可以任意選擇的,它們之間是相互關(guān)聯(lián),相互制約的,特別是當(dāng)你規(guī)定了最大時(shí)間不超過當(dāng)前時(shí)間時(shí)。比如假設(shè)現(xiàn)在是2017年05月24日15:30,當(dāng)我滑到2017年5月23日16:00,此時(shí)再將日改回24日時(shí),時(shí)間顯然是不能超過15:30的。這時(shí)就要做一些判斷了。
以分鐘的數(shù)值為例,如果當(dāng)前的分鐘數(shù)值小于60,而且之前選擇的分鐘數(shù)值比當(dāng)前的分鐘數(shù)值大,那么就要將分鐘數(shù)值修改為當(dāng)前的分鐘時(shí)間,否則繼續(xù)沿用之前的數(shù)值。
閱讀源碼可以看到,修改分鐘數(shù)值的方法為minuteChange,將復(fù)位處理的代碼注釋之后,然后加上我們的邏輯判斷。
// selectedCalender.set(Calendar.MINUTE, Integer.parseInt(minute.get(0))); // minute_pv.setSelected(0); if (minute.size() < 60 && minute.size() < Integer.valueOf(currentMin)) { minute_pv.setSelected(minute.size() - 1); selectedCalender.set(Calendar.MINUTE, minute.size()); //改變當(dāng)前選擇的分鐘 currentMin = formatTimeUnit(minute.size()); } else { minute_pv.setSelected(currentMin); selectedCalender.set(Calendar.MINUTE, Integer.parseInt(currentMin)); }
小時(shí)的判斷跟分鐘的差不多,這里就貼代碼了,但是月份的就有點(diǎn)特殊了,因?yàn)闊o論哪一天都是24小時(shí),無論哪一小時(shí)都有60分鐘,而每個(gè)月的天數(shù)卻不盡相同,比如二月份就只有28或者29天。我們需要再創(chuàng)建一個(gè)int值變量lastMonthDays來記錄上一個(gè)被選中的月份的天數(shù)。當(dāng)前選中的月份天數(shù)比之前選中的月份天數(shù)少,而且之前選中的日的數(shù)值比當(dāng)前選中的月份天數(shù)還有大時(shí),那么日的數(shù)值就必須改為這個(gè)月的最后一天了。舉個(gè)例子,比如將2017年3月31日中月份改為2月份,由于二月份只有28天,那么日的數(shù)值就不能停留在31了,而是跳轉(zhuǎn)到28。
在dayChange方法中的代碼修改如下:
// selectedCalender.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day.get(0))); // day_pv.setSelected(0); if (day.size() < lastMonthDays && Integer.valueOf(currentDay) > day.size()) { day_pv.setSelected(day.size() - 1); currentDay = formatTimeUnit(day.size()); } else { day_pv.setSelected(currentDay); } selectedCalender.set(Calendar.DAY_OF_MONTH, Integer.parseInt(currentDay)); //重新賦值 lastMonthDays = day.size();
最后,當(dāng)然也別忘了到setSelectedTime中給lastMonthDays設(shè)置默認(rèn)值。
lastMonthDays = day.size();6、將某一列的數(shù)值設(shè)為多帶帶循環(huán)滾動(dòng)
盡管源碼中已經(jīng)有setIsLoop方法可以設(shè)置為循環(huán)滾動(dòng),但是這個(gè)是將所有列都同時(shí)設(shè)置為循環(huán)滾動(dòng)的,如果我們想多帶帶將某一列設(shè)為循環(huán)滾動(dòng)的話可以再增加幾個(gè)方法:
public void setYearIsLoop(boolean isLoop) { if (canAccess) { this.year_pv.setIsLoop(isLoop); } } public void setMonIsLoop(boolean isLoop) { if (canAccess) { this.month_pv.setIsLoop(isLoop); } } public void setDayIsLoop(boolean isLoop) { if (canAccess) { this.day_pv.setIsLoop(isLoop); } } public void setHourIsLoop(boolean isLoop) { if (canAccess) { this.hour_pv.setIsLoop(isLoop); } } public void setMinIsLoop(boolean isLoop) { if (canAccess) { this.minute_pv.setIsLoop(isLoop); } }7、去除對話框與屏幕之間的間距
這個(gè)問題我在Demo中沒有發(fā)現(xiàn),整合到工程中才遇到。彈出對話框時(shí),左右和底部有一定的間距始終消除不了,但是在初始化對話框的方法initDialog方法中加了下面的代碼就可以了:
window.setBackgroundDrawableResource(R.color.color_bg);
顏色的值可以是透明的,但個(gè)人覺得最后還是設(shè)置一個(gè)背景色。這個(gè)問題,目前我也沒有找到原因……
8、總結(jié)經(jīng)過上面一番改動(dòng),我可以愉快地整合到我自己的項(xiàng)目中了,個(gè)人覺得這個(gè)控件挺美觀實(shí)用的,歡迎大家支持原博主。如果大家有什么好的改動(dòng),也歡迎給我留言。下面附上源碼:
TimePickerDemo
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/67123.html
閱讀 654·2021-09-24 09:48
閱讀 2499·2021-08-26 14:14
閱讀 524·2019-08-30 13:08
閱讀 1450·2019-08-29 15:22
閱讀 3084·2019-08-29 11:06
閱讀 1011·2019-08-26 18:26
閱讀 1062·2019-08-26 13:53
閱讀 2538·2019-08-26 12:21