github地址:https://github.com/huanghaibin-dev/CalendarView
推薦一篇講解CalendarView特別好的博客:https://blog.csdn.net/huanghaibin_dev/article/details/79040147
我的這篇文章重點不是講解CalendarView怎么使用的,是說我在使用過程中,由于自己腦抽,本來很容易的事情,被自己搞復(fù)雜了。尤其是下文第3點,到現(xiàn)在都懷疑當(dāng)時的自己怎么看資料的。怎么修改“時間選擇范圍”背景色、結(jié)束日期小于開始日期依舊繪制及處理邏輯、根據(jù)項目需求實時顯示當(dāng)前年月……,以達到UI效果
一:基本屬性的使用:
1.日歷顯示模式有三種(由CalendarLayout控制,直接在布局里面設(shè)置,eg:app:calendar_show_mode="both_month_week_view ",):
both_month_week_view //月模式和周模式(默認模式,可切換月周模式)
only_week_view //周模式
only_month_view //月模式
2.設(shè)置日歷間距calendarView.setCalendarItemHeight
3.想要日歷月周模式切換時,下面(指日歷下面的布局)的內(nèi)容跟著上下走動。這個梗,一開始接觸這個日歷時,研究了好久,一直沒有做出來,主要是:
1)沒有設(shè)置calendar_show_mode屬性為both_month_week_view;
2)一直誤認為com.haibin.calendarviewproject.group.GroupRecyclerView才是真正實現(xiàn),下面跟隨日歷上下收縮的關(guān)鍵。其實項目的要求是,日歷下面跟隨著一個Webview,這個Webview跟隨日歷收縮而進行上下。到了某天,突然間理解了
以WebView為例,重點是:
(1)在CalendarLayout設(shè)置app:calendar_content_view_id="@+id/recyclerView"這個屬性,recyclerView就是WebView的id;
(2)WebView必須在CalendarView后面:
<com.haibin.calendarview.CalendarLayout? ? android:id="@+id/calendarLayout"? ? android:layout_width="match_parent"? ? android:layout_height="match_parent"? ? android:orientation="vertical"? ? app:calendar_show_mode="both_month_week_view "? ? app:default_status="shrink"? ? app:calendar_content_view_id="@+id/recyclerView">? ? <com.haibin.calendarview.CalendarView? ? ? ? android:id="@+id/calendarView"? ? ? ? …………? ? ? />
<!--會跟隨日歷月周模式切換,而進行上下走動(也許描述不太好,就是一直跟在日歷后面,無論是周還是月模式),這個WebView可以替換成任何控件-->
? ? <WebView
? ? ? ? android:id="@+id/recyclerView"
? ? ? ? …………
? ? ? />
</com.haibin.calendarview.CalendarLayout>
4.回到今天:CalendarView.scrollToCurrent()
5.修改選擇范圍背景色,如下圖所示:
CustomRangeMonthView.java(CustomRangeWeekView.java同理)代碼改成如下:
? private int mRadius;
? ? private int middleColor = CalendarColors.middleColor;//中間范圍背景色
? ? private int selectedColor = CalendarColors.selectedColor;//選擇的背景色
? ? private int middleTxtColor = CalendarColors.middleTxtColor;//中間范圍字體顏色
? ? private int selectedTxtColor = CalendarColors.selectedTxtColor;//選擇字體顏色
? ? public CustomRangeMonthView(Context context) {
? ? ? ? super(context);
? ? }
? ? @Override
? ? protected void onPreviewHook() {
? ? ? ? mRadius = Math.min(mItemWidth, mItemHeight) / 5 * 2;
? ? ? ? mSchemePaint.setStyle(Paint.Style.STROKE);
? ? }
? ? /**
? ? * 繪制選中的日期
? ? *
? ? * @param canvas? ? ? ? canvas
? ? * @param calendar? ? ? 日歷日歷calendar
? ? * @param x? ? ? ? ? ? ? 日歷Card x起點坐標(biāo)
? ? * @param y? ? ? ? ? ? ? 日歷Card y起點坐標(biāo)
? ? * @param hasScheme? ? ? hasScheme 非標(biāo)記的日期
? ? * @param isSelectedPre? 上一個日期是否選中
? ? * @param isSelectedNext 下一個日期是否選中
? ? * @return 是否繼續(xù)繪制onDrawScheme,true or false
? ? */
? ? @Override
? ? protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? boolean isSelectedPre, boolean isSelectedNext) {
? ? ? ? int cx = x + mItemWidth / 2;
? ? ? ? int cy = y + mItemHeight / 2;
? ? ? ? if (isSelectedPre) {
? ? ? ? ? ? if (isSelectedNext) {// 中間日期
? ? ? ? ? ? ? ? mSelectedPaint.setColor(middleColor);
? ? ? ? ? ? ? ? canvas.drawRect(x, cy - mRadius, x + mItemWidth, cy + mRadius, mSelectedPaint);
? ? ? ? ? ? } else {//最后一個,the last 最后一個日期
? ? ? ? ? ? ? ? mSelectedPaint.setColor(middleColor);
? ? ? ? ? ? ? ? canvas.drawRect(x, cy - mRadius, cx, cy + mRadius, mSelectedPaint);
? ? ? ? ? ? ? ? mSelectedPaint.setColor(selectedColor);
? ? ? ? ? ? ? ? canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
? ? ? ? ? ? }
? ? ? ? } else {
? ? ? ? ? ? if(isSelectedNext){
? ? ? ? ? ? ? ? mSelectedPaint.setColor(middleColor);
? ? ? ? ? ? ? ? canvas.drawRect(cx, cy - mRadius, x + mItemWidth, cy + mRadius, mSelectedPaint);
? ? ? ? ? ? }
? ? ? ? ? ? mSelectedPaint.setColor(selectedColor);
? ? ? ? ? ? canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
? ? ? ? }
? ? ? ? return false;
? ? }
? ? @Override
? ? protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y, boolean isSelected) {
? ? ? ? int cx = x + mItemWidth / 2;
? ? ? ? int cy = y + mItemHeight / 2;
? ? ? ? canvas.drawCircle(cx, cy, mRadius, mSchemePaint);
? ? }
? ? @Override
? ? protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
? ? ? ? float baselineY = mTextBaseLine + y;
? ? ? ? int cx = x + mItemWidth / 2;
? ? ? ? boolean isInRange = isInRange(calendar);
? ? ? ? boolean isEnable = !onCalendarIntercept(calendar);
? ? ? ? if (isSelected) {
? ? ? ? ? ? boolean isSelectedPre = isSelectPreCalendar(calendar);
? ? ? ? ? ? boolean isSelectedNext = isSelectNextCalendar(calendar);
? ? ? ? ? ? if (isSelectedPre) {
? ? ? ? ? ? ? ? if (isSelectedNext) {// 中間日期
? ? ? ? ? ? ? ? ? ? mSelectTextPaint.setColor(middleTxtColor);
? ? ? ? ? ? ? ? } else {//最后一個,the last 最后一個日期
? ? ? ? ? ? ? ? ? ? mSelectTextPaint.setColor(selectedTxtColor);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? if(isSelectedNext){
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? mSelectTextPaint.setColor(selectedTxtColor);
? ? ? ? ? ? }
? ? ? ? ? ? canvas.drawText(String.valueOf(calendar.getDay()),
? ? ? ? ? ? ? ? ? ? cx,
? ? ? ? ? ? ? ? ? ? baselineY,
? ? ? ? ? ? ? ? ? ? mSelectTextPaint);
? ? ? ? } else if (hasScheme) {
? ? ? ? ? ? canvas.drawText(String.valueOf(calendar.getDay()),
? ? ? ? ? ? ? ? ? ? cx,
? ? ? ? ? ? ? ? ? ? baselineY,
? ? ? ? ? ? ? ? ? ? calendar.isCurrentDay() ? mCurDayTextPaint :
? ? ? ? ? ? ? ? ? ? ? ? ? ? calendar.isCurrentMonth() && isInRange && isEnable? mSchemeTextPaint : mOtherMonthTextPaint);
? ? ? ? } else {
? ? ? ? ? ? canvas.drawText(String.valueOf(calendar.getDay()), cx, baselineY,
? ? ? ? ? ? ? ? ? ? calendar.isCurrentDay() ? mCurDayTextPaint :
? ? ? ? ? ? ? ? ? ? ? ? ? ? calendar.isCurrentMonth() && isInRange && isEnable? mCurMonthTextPaint : mOtherMonthTextPaint);
? ? ? ? }
? ? }
CalendarColors.java代碼如下(可自定義顏色):
public static final int middleColor = Color.rgb(255, 221, 217);
? ? public static final int selectedColor = Color.rgb(255, 83, 67);
? ? public static final int middleTxtColor = Color.rgb(64, 64, 64);
? ? public static final int selectedTxtColor = Color.rgb(255, 255, 255);
6.日期選擇范圍,結(jié)束日期小于開始日期時,依舊繪制(修改源碼三步驟)
在github地址:https://github.com/huanghaibin-dev/CalendarView,下載好項目,運行起來,接下來按照步驟修改
(1)CalendarUtil.java的differ(Calendar calendar1, Calendar calendar2)方法修改成如下:
/**
? ? * 運算 大日期減小日期
? ? * test Pass
? ? *
? ? * @param calendar1 calendar1 第二次點擊的日期
? ? * @param calendar2 calendar2 第一次點擊的日期
? ? * @return 大日期減小日期
? ? */
? ? static int differ(Calendar calendar1, Calendar calendar2) {
? ? ? ? if (calendar1 == null) {
? ? ? ? ? ? return Integer.MIN_VALUE;
? ? ? ? }
? ? ? ? if (calendar2 == null) {
? ? ? ? ? ? return Integer.MAX_VALUE;
? ? ? ? }
? ? ? ? java.util.Calendar date = java.util.Calendar.getInstance();
? ? ? ? date.set(calendar1.getYear(), calendar1.getMonth() - 1, calendar1.getDay());//
? ? ? ? long startTimeMills = date.getTimeInMillis();//獲得起始時間戳
? ? ? ? date.set(calendar2.getYear(), calendar2.getMonth() - 1, calendar2.getDay());//
? ? ? ? long endTimeMills = date.getTimeInMillis();//獲得結(jié)束時間戳
? ? ? ? if (startTimeMills < endTimeMills) {
? ? ? ? ? ? return (int) ((endTimeMills - startTimeMills) / ONE_DAY);
? ? ? ? } else {
? ? ? ? ? ? return (int) ((startTimeMills - endTimeMills) / ONE_DAY);
? ? ? ? }
? ? }
(2)RangeMonthView.java的onClick(View v)方法,修改某行代碼即可,改成如下:(RangeWeekView.java同理)
//大概在168行,因為前面有改過某些代碼,不知道準確行數(shù)了,如下代碼
int compare = calendar.compareTo(mDelegate.mSelectedStartRangeCalendar);
//改成如下代碼:(原因:比較兩次日期大小,當(dāng)?shù)诙吸c擊日期小于第一次點擊日期時,會返回負數(shù),反之會放回1,相等時放回0)
int compare = calendar.compareTo(mDelegate.mSelectedStartRangeCalendar);
? ? ? ? ? ? if (compare < 0) {
? ? ? ? ? ? ? ? compare = 1;
? ? ? ? ? ? }
(3)RangeMonthView.java的isCalendarSelected(Calendar calendar),修改成如下代碼:(RangeWeekView.java同理)
? ? /**
? ? * 日歷是否被選中
? ? *
? ? * @param calendar calendar
? ? * @return 日歷是否被選中
? ? */
? ? protected boolean isCalendarSelected(Calendar calendar) {
? ? ? ? if (mDelegate.mSelectedStartRangeCalendar == null) {
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? if (onCalendarIntercept(calendar)) {
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? if (mDelegate.mSelectedEndRangeCalendar == null) {
? ? ? ? ? ? return calendar.compareTo(mDelegate.mSelectedStartRangeCalendar) == 0;
? ? ? ? }
? ? ? ? /*
? ? ? ? *原先的代碼如下:
? ? ? ? *(當(dāng)calendar在大于等于“第一次點擊日期”而且小于等于“第二次點擊日期”時為true
? ? ? ? *這個邏輯對于我們“第一次點擊日期”小于“第二次點擊日期”就會返回false了)
? ? ? ? *? return calendar.compareTo(mDelegate.mSelectedStartRangeCalendar) >= 0 &&
? ? ? ? *? ? ? calendar.compareTo(mDelegate.mSelectedEndRangeCalendar) <= 0;
? ? ? ? * */
? ? ? ? return (calendar.compareTo(mDelegate.mSelectedStartRangeCalendar) >= 0 &&calendar.compareTo(mDelegate.mSelectedEndRangeCalendar) <= 0) ||
? ? ? ? ? ? ? ? (calendar.compareTo(mDelegate.mSelectedEndRangeCalendar) >= 0 && calendar.compareTo(mDelegate.mSelectedStartRangeCalendar) <= 0);
? ? }
前方bug注意:修改三步驟后,結(jié)束日期小于開始日期是繪制了,但是不知道你選中的時間范圍是什么,解決這個bug,按如下代碼修改即可:
1.CalendarViewDelegate.java的getSelectCalendarRange()方法,當(dāng)結(jié)束日期小于開始日期時,置換startTimeMills、endTimeMills即可,改成如下:
? /**
? ? * 獲得選中范圍
? ? *
? ? * @return 選中范圍
? ? */
? ? final List<Calendar> getSelectCalendarRange() {
? ? ? ? if (mSelectMode != SELECT_MODE_RANGE) {
? ? ? ? ? ? return null;
? ? ? ? }
? ? ? ? List<Calendar> calendars = new ArrayList<>();
? ? ? ? if (mSelectedStartRangeCalendar == null ||
? ? ? ? ? ? ? ? mSelectedEndRangeCalendar == null) {
? ? ? ? ? ? return calendars;
? ? ? ? }
? ? ? ? final long ONE_DAY = 1000 * 3600 * 24;
? ? ? ? java.util.Calendar date = java.util.Calendar.getInstance();
? ? ? ? date.set(mSelectedStartRangeCalendar.getYear(),
? ? ? ? ? ? ? ? mSelectedStartRangeCalendar.getMonth() - 1,
? ? ? ? ? ? ? ? mSelectedStartRangeCalendar.getDay());//
? ? ? ? long startTimeMills = date.getTimeInMillis();//獲得起始時間戳
? ? ? ? date.set(mSelectedEndRangeCalendar.getYear(),
? ? ? ? ? ? ? ? mSelectedEndRangeCalendar.getMonth() - 1,
? ? ? ? ? ? ? ? mSelectedEndRangeCalendar.getDay());//
? ? ? ? long endTimeMills = date.getTimeInMillis();
? ? ? ? //新增的代碼,開始日期大于結(jié)束日期,置換
? ? ? ? if (startTimeMills > endTimeMills) {
? ? ? ? ? ? Long interTimeMills;
? ? ? ? ? ? interTimeMills = startTimeMills;
? ? ? ? ? ? startTimeMills = endTimeMills;
? ? ? ? ? ? endTimeMills = interTimeMills;
? ? ? ? }
? ? ? ? for (long start = startTimeMills; start <= endTimeMills; start += ONE_DAY) {
? ? ? ? ? ? date.setTimeInMillis(start);
? ? ? ? ? ? Calendar calendar = new Calendar();
? ? ? ? ? ? calendar.setYear(date.get(java.util.Calendar.YEAR));
? ? ? ? ? ? calendar.setMonth(date.get(java.util.Calendar.MONTH) + 1);
? ? ? ? ? ? calendar.setDay(date.get(java.util.Calendar.DAY_OF_MONTH));
? ? ? ? ? ? if (mCalendarInterceptListener != null &&
? ? ? ? ? ? ? ? ? ? mCalendarInterceptListener.onCalendarIntercept(calendar)) {
? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? }
? ? ? ? ? ? LunarCalendar.setupLunarCalendar(calendar);
? ? ? ? ? ? calendars.add(calendar);
? ? ? ? }
? ? ? ? addSchemesFromMap(calendars);
? ? ? ? return calendars;
? ? }
改好后,運行測試?,F(xiàn)在問題來了,怎么加入到項目里呢?原先我們引入日歷這樣子:
implementation 'com.haibin:calendarview:3.6.5'
改好源碼后,怎么引入到項目里面呢?我的做法是把calendarview一整個libar引入到項目中,步驟如下:
1.選中calendarview右鍵——Show in Explorer(就是找到calendarview的路徑);
2.選中項目(自己的項目)右鍵——New——Module——Import Gradle Project——Next——選擇calendarview路徑——Next;
3.修改calendarview的gradle,
calendarview、minSdkVersion、targetSdkVersion、versionCode、versionName這些值改成跟項目的一樣,
把compile改成implementation,androidTestCompile改成androidTestImplementation,testCompiletestImplementation,
注意appcompat-v7、recyclerview-v7、junit版本號跟項目一致
4.clean、rebuild一下,運行即可
7.左右滑動切換月份實時顯示當(dāng)前年月
我入的坑:使用的是“范圍選擇”,要求左右滑動日歷(點擊<>)時,要顯示當(dāng)前年月,不然用戶不知道滑動到哪里去了:
只需繼承CalendarView.OnMonthChangeListener的
@Override
public void onMonthChange(int year, int month) {
? //顯示年月
}
注意:記得添加calendarView.setOnMonthChangeListener(this);
8.設(shè)置今天之后的日期不可點且置灰色,重點OnCalendarInterceptListener(select_mode要設(shè)置成支持攔截)
//設(shè)置日期攔截事件
? ? @Override
? ? public boolean onCalendarIntercept(Calendar calendar) {
? ? ? ? String str = calendar.getYear() + "-" + (calendar.getMonth() < 10 ? "0" + calendar.getMonth() : calendar.getMonth()) + "-" + (calendar.getDay() < 10 ? "0" + calendar.getDay() : calendar.getDay());
? ? ? ? String now= binding.cvHomeDormitoryAttendanceCalendarView.getCurYear() + "-" + binding.cvHomeDormitoryAttendanceCalendarView.getCurMonth() + "-" + binding.cvHomeDormitoryAttendanceCalendarView.getCurDay();
? ? ? ? return TimeUtils.NowCompare(now, str);//現(xiàn)在日期,點擊日期進行對比,點擊日期>現(xiàn)在日期為true即攔截
? ? }
? ? @Override
? ? public void onCalendarInterceptClick(Calendar calendar, boolean isClick) {
? ? ? ? //點擊攔截的日期回調(diào),isClick為true時表示攔截到設(shè)置條件的日期,可以做相應(yīng)提示
? ? }
目前入過的坑就是以上這些了,并且解決,希望能幫助遇到同樣問題的你,后面若遇到新問題,會繼續(xù)更新!
————————————————
版權(quán)聲明:本文為CSDN博主「週莫」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_40420578/article/details/96485028