CalendarView学习使用挖坑填坑秘籍

github地址:https://github.com/huanghaibin-dev/CalendarView

推荐一篇讲解CalendarView特别好的博客:https://blog.csdn.net/huanghaibin_dev/article/details/79040147

我的这篇文章重点不是讲解CalendarView怎么使用的,是说我在使用过程中,由于自己脑抽,本来很容易的事情,被自己搞复杂了。尤其是下文第3点,到现在都怀疑当时的自己怎么看资料的。怎么修改“时间选择范围”背景色、结束日期小于开始日期依旧绘制及处理逻辑、根据项目需求实时显示当前年月……,以达到UI效果

一:基本属性的使用:

1.日历显示模式有三种(由CalendarLayout控制,直接在布局里面设置,eg:app:calendar_show_mode="both_month_week_view ",):

both_month_week_view //月模式和周模式(默认模式,可切换月周模式)

only_week_view //周模式

only_month_view //月模式

2.设置日历间距calendarView.setCalendarItemHeight

3.想要日历月周模式切换时,下面(指日历下面的布局)的内容跟着上下走动。这个梗,一开始接触这个日历时,研究了好久,一直没有做出来,主要是:

1)没有设置calendar_show_mode属性为both_month_week_view;

2)一直误认为com.haibin.calendarviewproject.group.GroupRecyclerView才是真正实现,下面跟随日历上下收缩的关键。其实项目的要求是,日历下面跟随着一个Webview,这个Webview跟随日历收缩而进行上下。到了某天,突然间理解了

以WebView为例,重点是:

(1)在CalendarLayout设置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起点坐标

    * @param y              日历Card y起点坐标

    * @param hasScheme      hasScheme 非标记的日期

    * @param isSelectedPre  上一个日期是否选中

    * @param isSelectedNext 下一个日期是否选中

    * @return 是否继续绘制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.日期选择范围,结束日期小于开始日期时,依旧绘制(修改源码三步骤)

在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();//获得结束时间戳

        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行,因为前面有改过某些代码,不知道准确行数了,如下代码

int compare = calendar.compareTo(mDelegate.mSelectedStartRangeCalendar);

//改成如下代码:(原因:比较两次日期大小,当第二次点击日期小于第一次点击日期时,会返回负数,反之会放回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;

        }

        /*

        *原先的代码如下:

        *(当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注意:修改三步骤后,结束日期小于开始日期是绘制了,但是不知道你选中的时间范围是什么,解决这个bug,按如下代码修改即可:

1.CalendarViewDelegate.java的getSelectCalendarRange()方法,当结束日期小于开始日期时,置换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();

        //新增的代码,开始日期大于结束日期,置换

        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;

    }

改好后,运行测试。现在问题来了,怎么加入到项目里呢?原先我们引入日历这样子:

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.左右滑动切换月份实时显示当前年月

我入的坑:使用的是“范围选择”,要求左右滑动日历(点击<>)时,要显示当前年月,不然用户不知道滑动到哪里去了:

只需继承CalendarView.OnMonthChangeListener的

@Override

public void onMonthChange(int year, int month) {

  //显示年月

}

注意:记得添加calendarView.setOnMonthChangeListener(this);

8.设置今天之后的日期不可点且置灰色,重点OnCalendarInterceptListener(select_mode要设置成支持拦截)

//设置日期拦截事件

    @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);//现在日期,点击日期进行对比,点击日期>现在日期为true即拦截

    }

    @Override

    public void onCalendarInterceptClick(Calendar calendar, boolean isClick) {

        //点击拦截的日期回调,isClick为true时表示拦截到设置条件的日期,可以做相应提示

    }

目前入过的坑就是以上这些了,并且解决,希望能帮助遇到同样问题的你,后面若遇到新问题,会继续更新!

————————————————

版权声明:本文为CSDN博主「週莫」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_40420578/article/details/96485028

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容