最近項(xiàng)目中用到自定義日歷組件,找來(lái)找去豹爹,最后發(fā)現(xiàn)GitHub的material-calendarview這個(gè)項(xiàng)目最貼近以上需求裆悄,稍作修改就能用了,簡(jiǎn)單記錄下基本知識(shí)點(diǎn)臂聋。
首先是到兩個(gè)基本的Java類
Date類
在JDK1.0中光稼,Date類是唯一的一個(gè)代表時(shí)間的類或南,但是由于Date類不便于實(shí)現(xiàn)國(guó)際化,所以從JDK1.1版本開(kāi)始艾君,推薦使用Calendar類進(jìn)行時(shí)間和日期處理采够。這里簡(jiǎn)單介紹一下Date類的使用。
1冰垄、使用Date類代表當(dāng)前系統(tǒng)時(shí)間
Date d = new Date();
使用Date類的默認(rèn)構(gòu)造方法創(chuàng)建出的對(duì)象就代表當(dāng)前時(shí)間蹬癌,由于Date類覆蓋了toString方法,所以可以直接輸出Date類型的對(duì)象虹茶,顯示的結(jié)果如下:
Sun Mar 08 16:35:58 CST 2017
在該格式中逝薪,Sun代表Sunday(周日),Mar代表March(三月)写烤,08代表8號(hào)翼闽,CST代表China Standard Time(中國(guó)標(biāo)準(zhǔn)時(shí)間,也就是北京時(shí)間(東八區(qū)))洲炊。
2感局、使用Date類代表指定的時(shí)間
Date d1 = new Date(2017-1900,3-1,9);
System.out.println(d1);
使用帶參數(shù)的構(gòu)造方法,可以構(gòu)造指定日期的Date類對(duì)象暂衡,Date類中年份的參數(shù)應(yīng)該是實(shí)際需要代表的年份減去1900询微,實(shí)際需要代表的月份減去1以后的值。例如上面的示例代碼代表就是2019年9月9號(hào)狂巢。
實(shí)際代表具體的年月日時(shí)分秒的日期對(duì)象撑毛,和這個(gè)類似。
3唧领、獲得Date對(duì)象中的信息
Date d2 = new Date();//年份
int year = d2.getYear() + 1900;//月份
int month = d2.getMonth() + 1;//日期
int date = d2.getDate();//小時(shí)
int hour = d2.getHours();//分鐘
int minute = d2.getMinutes();//秒
int second = d2.getSeconds();//星期幾
int day = d2.getDay();
System.out.println("年份:" + year);
System.out.println("月份:" + month);
System.out.println("日期:" + date);
System.out.println("小時(shí):" + hour);
System.out.println("分鐘:" + minute);
System.out.println("秒:" + second);
System.out.println("星期:" + day);
使用Date類中對(duì)應(yīng)的get方法藻雌,可以獲得Date類對(duì)象中相關(guān)的信息,需要注意的是使用getYear獲得是Date對(duì)象中年份減去1900以后的值斩个,所以需要顯示對(duì)應(yīng)的年份則需要在返回值的基礎(chǔ)上加上1900胯杭,月份類似。在Date類中還提供了getDay方法受啥,用于獲得Date對(duì)象代表的時(shí)間是星期幾做个,Date類規(guī)定周日是0,周一是1滚局,周二是2居暖,后續(xù)的依次類推。
4藤肢、Date對(duì)象和相對(duì)時(shí)間之間的互轉(zhuǎn)
Date d3 = new Date(2017-1900,9-18,10);
long time = 1290876532190L;
//將Date類的對(duì)象轉(zhuǎn)換為相對(duì)時(shí)間
long t = d3.getTime();
System.out.println(t);
//將相對(duì)時(shí)間轉(zhuǎn)換為Date類的對(duì)象
Date d4 = new Date(time);
System.out.println(d4);
使用Date對(duì)象中的getTime方法太闺,可以將Date類的對(duì)象轉(zhuǎn)換為相對(duì)時(shí)間,使用Date類的構(gòu)造方法谤草,可以將相對(duì)時(shí)間轉(zhuǎn)換為Date類的對(duì)象跟束。經(jīng)過(guò)轉(zhuǎn)換以后莺奸,既方便了時(shí)間的計(jì)算丑孩,也使時(shí)間顯示比較直觀了冀宴。
Calendar類
從JDK1.1版本開(kāi)始,在處理日期和時(shí)間時(shí)温学,系統(tǒng)推薦使用Calendar類進(jìn)行實(shí)現(xiàn)略贮。在設(shè)計(jì)上,Calendar類的功能要比Date類強(qiáng)大很多仗岖,而且在實(shí)現(xiàn)方式上也比Date類要復(fù)雜一些逃延,下面就介紹一下Calendar類的使用。
Calendar類是一個(gè)抽象類轧拄,在實(shí)際使用時(shí)實(shí)現(xiàn)特定的子類的對(duì)象揽祥,創(chuàng)建對(duì)象的過(guò)程對(duì)程序員來(lái)說(shuō)是透明的,只需要使用getInstance方法創(chuàng)建即可檩电。
1拄丰、使用Calendar類代表當(dāng)前時(shí)間
Calendar c = Calendar.getInstance();
由于Calendar類是抽象類,且Calendar類的構(gòu)造方法是protected的俐末,所以無(wú)法使用Calendar類的構(gòu)造方法來(lái)創(chuàng)建對(duì)象料按,API中提供了getInstance方法用來(lái)創(chuàng)建對(duì)象。
使用該方法獲得的Calendar對(duì)象就代表當(dāng)前的系統(tǒng)時(shí)間卓箫,由于Calendar類toString實(shí)現(xiàn)的沒(méi)有Date類那么直觀载矿,所以直接輸出Calendar類的對(duì)象意義不大。
2烹卒、使用Calendar類代表指定的時(shí)間
Calendar c1 = Calendar.getInstance();
c1.set(2017, 3 - 1, 9);
使用Calendar類代表特定的時(shí)間闷盔,需要首先創(chuàng)建一個(gè)Calendar的對(duì)象,然后再設(shè)定該對(duì)象中的年月日參數(shù)來(lái)完成旅急。
set方法的聲明為:
public final void set(int year,int month,int date)
以上示例代碼設(shè)置的時(shí)間為2017年3月9日逢勾,其參數(shù)的結(jié)構(gòu)和Date類不一樣。Calendar類中年份的數(shù)值直接書(shū)寫(xiě)坠非,月份的值為實(shí)際的月份值減1敏沉,日期的值就是實(shí)際的日期值。
如果只設(shè)定某個(gè)字段炎码,例如日期的值盟迟,則可以使用如下set方法:
public void set(int field,int value)
在該方法中,參數(shù)field代表要設(shè)置的字段的類型潦闲,常見(jiàn)類型如下:
Calendar.YEAR——年份
Calendar.MONTH——月份
Calendar.DATE——日期
Calendar.DAY_OF_MONTH——日期攒菠,和上面的字段完全相同
Calendar.HOUR——12小時(shí)制的小時(shí)數(shù)
Calendar.HOUR_OF_DAY——24小時(shí)制的小時(shí)數(shù)
Calendar.MINUTE——分鐘
Calendar.SECOND——秒
Calendar.DAY_OF_WEEK——星期幾
后續(xù)的參數(shù)value代表,設(shè)置成的值歉闰。例如:
c1.set(Calendar.DATE,10);
該代碼的作用是將c1對(duì)象代表的時(shí)間中日期設(shè)置為10號(hào)辖众,其它所有的數(shù)值會(huì)被重新計(jì)算卓起,例如星期幾以及對(duì)應(yīng)的相對(duì)時(shí)間數(shù)值等。
3凹炸、獲得Calendar類中的信息
Calendar c2 = Calendar.getInstance();//年份
int year = c2.get(Calendar.YEAR);//月份
int month = c2.get(Calendar.MONTH) + 1;//日期
int date = c2.get(Calendar.DATE);//小時(shí)
int hour = c2.get(Calendar.HOUR_OF_DAY);//分鐘
int minute = c2.get(Calendar.MINUTE);//秒
int second = c2.get(Calendar.SECOND);//星期幾
int day = c2.get(Calendar.DAY_OF_WEEK);
System.out.println("年份:" + year);
System.out.println("月份:" + month);
System.out.println("日期:" + date);
System.out.println("小時(shí):" + hour);
System.out.println("分鐘:" + minute);
System.out.println("秒:" + second);
System.out.println("星期:" + day);
使用Calendar類中的get方法可以獲得Calendar對(duì)象中對(duì)應(yīng)的信息戏阅,get方法的聲明如下:
public int get(int field)
其中參數(shù)field代表需要獲得的字段的值,字段說(shuō)明和上面的set方法保持一致啤它。需要說(shuō)明的是奕筐,獲得的月份為實(shí)際的月份值減1,獲得的星期的值和Date類不一樣变骡。在Calendar類中离赫,周日是1,周一是2塌碌,周二是3渊胸,依次類推。
4台妆、其它方法說(shuō)明
其實(shí)Calendar類中還提供了很多其它有用的方法翎猛,下面簡(jiǎn)單的介紹幾個(gè)常見(jiàn)方法的使用。
a频丘、add方法
public abstract void add(int field,int amount)
該方法的作用是在Calendar對(duì)象中的某個(gè)字段上增加或減少一定的數(shù)值办成,增加是amount的值為正,減少時(shí)amount的值為負(fù)搂漠。
例如在計(jì)算一下當(dāng)前時(shí)間100天以后的日期迂卢,代碼如下:
Calendar c3 = Calendar.getInstance();
c3.add(Calendar.DATE, 100);
int year1 = c3.get(Calendar.YEAR);//月份
int month1 = c3.get(Calendar.MONTH) + 1;//日期
int date1 = c3.get(Calendar.DATE);
System.out.println(year1 + "年" + month1 + "月" + date1 + "日");
這里add方法是指在c3對(duì)象的Calendar.DATE,也就是日期字段上增加100桐汤,類內(nèi)部會(huì)重新計(jì)算該日期對(duì)象中其它各字段的值而克,從而獲得100天以后的日期
b、after方法
public boolean after(Object when)
該方法的作用是判斷當(dāng)前日期對(duì)象是否在when對(duì)象的后面怔毛,如果在when對(duì)象的后面則返回true员萍,否則返回false。例如:
Calendar c4 = Calendar.getInstance();
c4.set(2007, 10 - 1, 10);
Calendar c5 = Calendar.getInstance();
c5.set(2017, 10 - 1, 10);
boolean b = c5.after(c4);
System.out.println(b);
另外一個(gè)類似的方法是before拣度,該方法是判斷當(dāng)前日期對(duì)象是否位于另外一個(gè)日期對(duì)象之前碎绎。
c、getTime方法
public final Date getTime()
該方法的作用是將Calendar類型的對(duì)象轉(zhuǎn)換為對(duì)應(yīng)的Date類對(duì)象抗果,兩者代表相同的時(shí)間點(diǎn)筋帖。
類似的方法是setTime,該方法的作用是將Date對(duì)象轉(zhuǎn)換為對(duì)應(yīng)的Calendar對(duì)象冤馏,該方法的聲明如下:
public final void setTime(Date date)
轉(zhuǎn)換的示例代碼如下:
Date d = new Date();
Calendar c6 = Calendar.getInstance();//Calendar類型的對(duì)象轉(zhuǎn)換為Date對(duì)象
Date d1 = c6.getTime();//Date類型的對(duì)象轉(zhuǎn)換為Calendar對(duì)象
Calendar c7 = Calendar.getInstance();
c7.setTime(d);
5日麸、Calendar對(duì)象和相對(duì)時(shí)間之間的互轉(zhuǎn)
Calendar c8 = Calendar.getInstance();
long t = 1252785271098L;
//將Calendar對(duì)象轉(zhuǎn)換為相對(duì)時(shí)間
long t1 = c8.getTimeInMillis();
//將相對(duì)時(shí)間轉(zhuǎn)換為Calendar對(duì)象
Calendar c9 = Calendar.getInstance();
c9.setTimeInMillis(t1);
在轉(zhuǎn)換時(shí),使用Calendar類中的getTimeInMillis方法可以將Calendar對(duì)象轉(zhuǎn)換為相對(duì)時(shí)間逮光。在將相對(duì)時(shí)間轉(zhuǎn)換為Calendar對(duì)象時(shí)代箭,首先創(chuàng)建一個(gè)Calendar對(duì)象墩划,然后再使用Calendar類的setTimeInMillis方法設(shè)置時(shí)間即可。
MaterialCalendarView
項(xiàng)目開(kāi)源地址:
https://github.com/prolificinteractive/material-calendarview
集成清單
添加compile 'com.prolificinteractive:material-calendarview:1.4.0'
添加日歷控件到布局中
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/calendarView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:mcv_showOtherDates="all"
app:mcv_selectionColor="#00F"
/>
功能展示
material-calendarview項(xiàng)目提供9個(gè)Sample來(lái)展示其功能用法
一嗡综、對(duì)照組:OldCalendarViewActivity
采用系統(tǒng)控件CalendarView乙帮,實(shí)現(xiàn)一個(gè)點(diǎn)擊顯示日期的功能,可惜在低版本Android手機(jī)上沒(méi)有material的樣式蛤高,顏值很低蚣旱,不能用碑幅。
二戴陡、基本用法:BasicActivity
綁定控件
@Bind(R.id.calendarView)
MaterialCalendarView widget;
OnDateSelectedListener:用來(lái)監(jiān)聽(tīng)選中日期的變化,
/**
* The callback used to indicate a date has been selected or deselected
*/
public interface OnDateSelectedListener {
/**
* Called when a user clicks on a day.
* There is no logic to prevent multiple calls for the same date and state.
*
* @param widget? the view associated with this listener
* @param date? ? the date that was selected or unselected
* @param selected true if the day is now selected, false otherwise
*/
void onDateSelected(@NonNull MaterialCalendarView widget, @NonNull CalendarDay date, boolean selected);
}
OnMonthChangedListener:用來(lái)接聽(tīng)頁(yè)面滑動(dòng)的變化沟涨,這個(gè)有點(diǎn)名不副實(shí)恤批,因?yàn)樵谥苣J降臅r(shí)候,滑動(dòng)頁(yè)面是按周來(lái)變化的裹赴。
/**
* The callback used to indicate the user changes the displayed month
*/
public interface OnMonthChangedListener {
/**
* Called upon change of the selected day
*
* @param widget the view associated with this listener
* @param date? the month picked, as the first day of the month
*/
void onMonthChanged(MaterialCalendarView widget, CalendarDay date);
}
getSelectedDate():獲取被選中的日期喜庞。
private String getSelectedDatesString() {
CalendarDay date = widget.getSelectedDate();
if (date == null) {
return "No Selection";
}
return FORMATTER.format(date.getDate());
}
三、修飾選中日期:BasicActivityDecorated
設(shè)置日期范圍
widget.setShowOtherDates(MaterialCalendarView.SHOW_ALL);
Calendar instance = Calendar.getInstance();
widget.setSelectedDate(instance.getTime());
Calendar instance1 = Calendar.getInstance();
instance1.set(instance1.get(Calendar.YEAR), Calendar.JANUARY, 1);
Calendar instance2 = Calendar.getInstance();
instance2.set(instance2.get(Calendar.YEAR), Calendar.DECEMBER, 31);
widget.state().edit()
.setMinimumDate(instance1.getTime())
.setMaximumDate(instance2.getTime())
.commit();
增加日期修飾
widget.addDecorators(
new MySelectorDecorator(this),
new HighlightWeekendsDecorator(),
oneDayDecorator
);
案例提供了三種修飾:
對(duì)選中日期增加指定背景圖層
public class MySelectorDecorator implements DayViewDecorator {
private final Drawable drawable;
public MySelectorDecorator(Activity context) {
drawable = context.getResources().getDrawable(R.drawable.my_selector);
}
@Override
public boolean shouldDecorate(CalendarDay day) {
return true;
}
@Override
public void decorate(DayViewFacade view) {
view.setSelectionDrawable(drawable);
}
}
對(duì)周末增加指定背景圖層
/**
* Highlight Saturdays and Sundays with a background
*/
public class HighlightWeekendsDecorator implements DayViewDecorator {
private final Calendar calendar = Calendar.getInstance();
private final Drawable highlightDrawable;
private static final int color = Color.parseColor("#228BC34A");
public HighlightWeekendsDecorator() {
highlightDrawable = new ColorDrawable(color);
}
@Override
public boolean shouldDecorate(CalendarDay day) {
day.copyTo(calendar);
int weekDay = calendar.get(Calendar.DAY_OF_WEEK);
return weekDay == Calendar.SATURDAY || weekDay == Calendar.SUNDAY;
}
@Override
public void decorate(DayViewFacade view) {
view.setBackgroundDrawable(highlightDrawable);
}
}
對(duì)選中日期增加指定修飾
/**
* Decorate a day by making the text big and bold
*/
public class OneDayDecorator implements DayViewDecorator {
private CalendarDay date;
public OneDayDecorator() {
date = CalendarDay.today();
}
@Override
public boolean shouldDecorate(CalendarDay day) {
return date != null && day.equals(date);
}
@Override
public void decorate(DayViewFacade view) {
view.addSpan(new StyleSpan(Typeface.BOLD));
view.addSpan(new RelativeSizeSpan(3.0f));
}
/**
* We're changing the internals, so make sure to call {@linkplain MaterialCalendarView#invalidateDecorators()}
*/
public void setDate(Date date) {
this.date = CalendarDay.from(date);
}
}
對(duì)特定日期增加紅點(diǎn)修飾
/**
* Decorate several days with a dot
*/
public class EventDecorator implements DayViewDecorator {
private int color;
private HashSet dates;
public EventDecorator(int color, Collection dates) {
this.color = color;
this.dates = new HashSet<>(dates);
}
@Override
public boolean shouldDecorate(CalendarDay day) {
return dates.contains(day);
}
@Override
public void decorate(DayViewFacade view) {
view.addSpan(new DotSpan(5, color));
}
}
四棋返、模式切換:SwappableBasicActivityDecorated
切換周模式延都、月模式
@OnClick(R.id.button_weeks)
public void onSetWeekMode() {
widget.state().edit()
.setCalendarDisplayMode(CalendarMode.WEEKS)
.commit();
}
@OnClick(R.id.button_months)
public void onSetMonthMode() {
widget.state().edit()
.setCalendarDisplayMode(CalendarMode.MONTHS)
.commit();
}
五、日期禁用:DisableDaysActivity
核心代碼是:
DayViewFacade view
view.setDaysDisabled(true);
這個(gè)作者非常喜歡用裝飾模式睛竣。所以對(duì)DayViewFacade的修飾可以無(wú)窮的累加晰房。只要集成DayViewDecorator就可以。
/**
* Decorate Day views with drawables and text manipulation
*/
public interface DayViewDecorator {
/**
* Determine if a specific day should be decorated
*
* @param day {@linkplain CalendarDay} to possibly decorate
* @return true if this decorator should be applied to the provided day
*/
boolean shouldDecorate(CalendarDay day);
/**
* Set decoration options onto a facade to be applied to all relevant days
*
* @param view View to decorate
*/
void decorate(DayViewFacade view);
}
六射沟、透過(guò)xml自定義控件:CustomizeXmlActivity
xml中能設(shè)置的屬性很多殊者,常用的如下:
mcv_showOtherDates:日期范圍
mcv_firstDayOfWeek:一周的第一天
mcv_calendarMode:日歷模式
七、透過(guò)java自定義控件:CustomizeCodeActivity
效果和xml是一致的验夯。
widget.setShowOtherDates(MaterialCalendarView.SHOW_ALL);widget.setArrowColor(getResources().getColor(R.color.sample_primary));widget.setLeftArrowMask(getResources().getDrawable(R.drawable.ic_navigation_arrow_back));widget.setRightArrowMask(getResources().getDrawable(R.drawable.ic_navigation_arrow_forward));widget.setSelectionColor(getResources().getColor(R.color.sample_primary));widget.setHeaderTextAppearance(R.style.TextAppearance_AppCompat_Medium);widget.setWeekDayTextAppearance(R.style.TextAppearance_AppCompat_Medium);widget.setDateTextAppearance(R.style.CustomDayTextAppearance);widget.setTitleFormatter(newMonthArrayTitleFormatter(getResources().getTextArray(R.array.custom_months)));widget.setWeekDayFormatter(newArrayWeekDayFormatter(getResources().getTextArray(R.array.custom_weekdays)));widget.setTileSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,36, getResources().getDisplayMetrics()));CalendarDay today = CalendarDay.from(2016,5,2);widget.setCurrentDate(today);widget.setSelectedDate(today);widget.state().edit()? ? ? ? .setFirstDayOfWeek(Calendar.WEDNESDAY)? ? ? ? .setMinimumDate(CalendarDay.from(2016,4,3))? ? ? ? .setMaximumDate(CalendarDay.from(2016,5,12))? ? ? ? .setCalendarDisplayMode(CalendarMode.WEEKS)? ? ? ? .commit();
八猖吴、完整的設(shè)置展示DynamicSettersActivity