极具个性化的【自定义日历】中式(纯干货)---超详细,记录你的学习计划与完成情况

1. 前言

  1.最近笔者在实习,需要做一个炫酷的自定义日历来在满足实际项目的需求,当让这里不能把实际需求放在这里,不然我就那啥了,你懂的。

  2.所以自己又重新设计了需求(内容完全不一样了,但UI有些类似),写了一个新的、十分炫酷的个性化日历,来记录您最近两个月的学习情况(记录您预期学习计划,以及实际完成的情况)。

  3.这篇博客我会尽可能的详细的去创作,所以这对于新手来说提升会很大,包括我自己,我也是刚刚学习Android不久,其实刚拿到需求的时候,我根本不知道该如何下手,不怕你们笑话,我当时连自定义View都不太会,业务逻辑编写能力也是弱的不行,但最后我还是完成了,所以我在这里会穿插的分享一下我学习的方法以及学习的思路,希望感兴趣的朋友耐心的阅读,同时也希望您能够喜欢。

PS:由于篇幅和知识重点分布的特点,我把它拆分成了三个章节,一点一滴记录它是如何完成的,本Demo的推荐阅读顺序是:

【 ① —> ② —> ③ 】或者【 ② —> ③】本章编号为“③”

相关参考链接:

① 极具个性化的【自定义日历】(预热篇)—超详细,记录你的学习计划与完成情况

② 极具个性化的【自定义日历】英式(纯干货)—超详细,记录你的学习计划与完成情况

2. 最终的效果图(本章效果图)如下:

              这里写图片描述

3. 本章简介

  这一章节呢,我们主要是在【英式的自定义日历】基础之上进行编写的,所以一定要先看英式的自定义日历是怎么实现的,否者会看的云里雾里的,废话不多说,接下来就是手把手分享如何修改成一个**【中式的自定义日历】。
  

4. 【自定义日历(中式)】的具体实现

(1)具体实现思路

具体的实现思路就两个字:【对比】,寻找【英式日历】【中式日历】的异同点,然后对症下药,屡试不爽,还有一个注意的点就是写完代码之后一定要测试一下,修改手机上的时间(主要改月份),不然的话,会存在你自认为的写的没有bug,其实某些特殊的月份会存在很严重的bug,你是不是对此充满了好奇呢?不着急,真相马上就会揭晓 O(∩_∩)O

(2)具体实现步骤

(1)修改星期标识

修改起来非常简单,因为我没有把星期标识包含在自定义日历中,所以只是在布局文件中修改下布局就行了。

PS:不过这里小小的抽取一下,不然每个星期标识都是一样的,就只是Text文本不一样,而且还要写七个,代码会变得繁琐,如下所示(未抽取):

  <LinearLayout
       ... ... ... >
        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="一"
            android:textColor="@color/colorGreen"
            android:textSize="16sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="二"
            android:textColor="@color/colorGreen"
            android:textSize="16sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="三"
            android:textColor="@color/colorGreen"
            android:textSize="16sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="四"
            android:textColor="@color/colorGreen"
            android:textSize="16sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="五"
            android:textColor="#2BCEA3"
            android:textSize="16sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="六"
            android:textColor="@color/colorGreen"
            android:textSize="16sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="日"
            android:textColor="@color/colorGreen"
            android:textSize="16sp" />
    </LinearLayout>

可以在style文件中把相同的属性的都抽取出来:

    <style name="weekLogo">
        <item name="android:layout_width">0dp</item>
        <item name="android:layout_height">match_parent</item>
        <item name="android:layout_weight">1</item>
        <item name="android:gravity">center</item>
        <item name="android:textColor">@color/colorGreen</item>
        <item name="android:textSize">16sp</item>
    </style>

抽取出来之后你看看,现在每个星期标识就只要写两行代码:

    <LinearLayout
        ... ... ...>
        <TextView
            style="@style/weekLogo"
            android:text="一" />

        <TextView
            style="@style/weekLogo"
            android:text="二" />

        <TextView
            style="@style/weekLogo"
            android:text="三" />

        <TextView
            style="@style/weekLogo"
            android:text="四" />

        <TextView
            style="@style/weekLogo"
            android:text="五" />

        <TextView
            style="@style/weekLogo"
            android:text="六" />

        <TextView
            style="@style/weekLogo"
            android:text="日" />
    </LinearLayout>

(2)在DayController里面修改日期对象的位置

            这里写图片描述

有了上面的分析,就好修改了:只要处理两种情况就好了:

            if (dayOfWeek != 1) {//【星期1 ~ 星期6】
                //行号
                mDayModel.locationY = weekOfMonth - 1;
                //列号
                mDayModel.locationX = dayOfWeek - 2;
            }
            else {//特殊情况【星期天】
                //行号
                mDayModel.locationY = weekOfMonth - 2;//原来
                //列号
                mDayModel.locationX = dayOfWeek + 5;
            }

(3)在CalendarView里面修改日期对象的点击事件

1.首先修改的无效点击事件

有了上面的分析很简单,不管是月初还是月末的无效点击事件,都只向左移动了一个位置,所以相比【英式】的只要都减一就好了,如下图:
            这里写图片描述

2.在修改点击之后显示的日期

这个比上一个又更简单,因为它设置的是日期,不是位置,所以只要清楚周一到周六都是行号不变,之前不是减一嘛,现在设置回去,就要加一,对于特殊情况【星期天】来说呢,相对与周一到周六:行号加一就行了,列号不变,因为我是设置日期。

   if (locationX != 6) {//【星期1 ~ 星期6】
            calendar.set(Calendar.WEEK_OF_MONTH,  locationY + 1);//相比【英式】:不变
            calendar.set(Calendar.DAY_OF_WEEK, (locationX + 2));//相比【英式】:加一
        }

    else { //特殊情况【星期天】
            calendar.set(Calendar.WEEK_OF_MONTH,  locationY + 2);//相比【星期1 ~ 星期6】:加一
            calendar.set(Calendar.DAY_OF_WEEK,  (locationX + 2));//相比【星期1 ~ 星期6】:不变
        }

3.写到这里,效果图如下:(日期的显示和点击事件都正确)

          这里写图片描述

(4)测试是否存在潜在的BUG

从刚刚的效果图来看,确实没有任何的问题,但你要知道一年有12个月,这里只有一两个月,不能断定写的代码就没有任何的问题。

这时你需要更换系统的时间,测试一下其它的月份是否有误,你至少测一年以上(12次)以上,把所有的月份给测到。

测试的结果有【两种】特殊的情况会出现BUG:

【1】: 当一个月的【1号】是星期天的时候,会少一行,并且【1号】显示不出来!例如:(2017年1月xx号)

          这里写图片描述

【2】: 当一个月的最后一号(仅当一个月有六周的时候),并且【30号/31号】是星期天,会多一行空白!例如:(2017年4月xx号)和(2016年7月xx号)

(2017年4月xx号):

          这里写图片描述

(2016年7月xx号):
          这里写图片描述

(5)修改潜在的BUG

(1)首先改行数:

分析:
首先我们来分析一下怎么改,改哪里,最好修改的就是【行数】了,第一种情况是【少一行】第二种情况是【多一行】,感受到了吗?第一种情况只要行数加一,第二种情况只要减一就行了。

DayController类中添加的一个标识变量,在把原来计算日期对象高度的那行代码重写,行数就解决了

    /**
     * 设置一个标记位置
     * 1:代表第一种特殊情况; 2:代表第二种特殊情况;
     */
    static int flag = 0;
        /**
         * 处理特殊情况
         *
         * 【1】: 当一个月的【1号】是星期天的时候,会少一行,并且【1号】显示不出来!
         *      例如:(2017年1月xx号)
         * 【2】: 当一个月的最后一号(仅当一个月有六周的时候),并且【30号/31号】是星期天,会多一行空白!
         *      例如:(2017年4月xx号)或者(2016年7月xx号)
         */

        int total_week_of_month = calendar.getActualMaximum(Calendar.WEEK_OF_MONTH);
        int dayModelHeight = height / total_week_of_month;

        //由于是静态的变量,存在记忆,复位一下,不然一定有问题
        if (flag != 0) {
            flag = 0;
        }

        //设置为当月一号,处理第一种情况【1】: 当一个月的【1号】是星期天的时候,会少一行,并且【1号】显示不出来!
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        if ((calendar.get(Calendar.DAY_OF_WEEK)) == 1) {
            dayModelHeight = height / (total_week_of_month + 1);
            flag = 1;
        }

        //设置为当月最后一号,处理第二种情况【2】:当一个月的最后一号(仅当一个月有六周的时候),并且【30号/31号】是星期天,会多一行空白!
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        if (total_week_of_month == 6 && (calendar.get(Calendar.DAY_OF_WEEK)) == 1) {
            dayModelHeight = height / (total_week_of_month - 1);
            flag = 2;
        }

CalendarView类中,修改两种特殊情况下的LocationY(日期对象的行号):

            /**
             * 处理特殊情况
             *
             * 【1】: 当一个月的【1号】是星期天的时候,会少一行,并且【1号】显示不出来!
             *      例如:(2017年1月xx号)
             * 【2】: 当一个月的最后一号(仅当一个月有六周的时候),并且【30号/31号】是星期天,会多一行空白!
             *      例如:(2017年4月xx号)和(2016年7月xx号)
             */
            if (DayController.getFlag() != 0) {

                //【1】: 当一个月的【1号】是星期天的时候,会少一行,并且【1号】显示不出来!
                if (DayController.getFlag() == 1) {
                    locationY = (int) ((calendar.getActualMaximum(Calendar.WEEK_OF_MONTH) + 1) * y / getMeasuredHeight());
                }
                //【2】: 当一个月的最后一号(仅当一个月有六周的时候),并且【30号/31号】是星期天,会多一行空白!
                else if (DayController.getFlag() == 2) {
                    locationY = (int) ((calendar.getActualMaximum(Calendar.WEEK_OF_MONTH) - 1) * y / getMeasuredHeight());
                }
            }
(2)修改行数之后看看有什么效果:

第一种情况:当一个月的【1号】是星期天的时候,会少一行,并且【1号】显示不出来! 例如:(2017年1月xx号):

结果是:【行数正确】,【日期对象显示依然有误】,【点击事件错乱】(点击空白地方竟然从2017年一月跑到二月去了),如下图所示(2017年1月xx号):

               这里写图片描述

第二种情况:当一个月的最后一号(仅当一个月有六周的时候),并且【30号/31号】是星期天,会多一行空白!例如:(2017年4月xx号)和(2016年7月xx号)

结果是:你会神奇的发现,【行数正确】,【日期对象显示正确】,【点击事件也正确】,这种情况已经解决了,如下图所示(2017年4月xx号):

               这里写图片描述

(3)继续修改第一种特殊情况(第二种特殊情况已解决):

DayController类【设置对象位置方法中】单独为第一种情况加一个判断,其实也很简单,由于只需要将日历整个向下移动一个距离,所以【列号不变】,【行号加一】就可以了。

    /**
     * 设置日期对象位置
     */
    public static void setLocation(int weekOfMonth,int dayOfWeek, DayModel mDayModel) {

        //【1】: 当一个月的【1号】是星期天的时候,会少一行,并且【1号】显示不出来!
        if (flag == 1) {
            //一般情况(星期1~6)
            if (dayOfWeek != 1) {
                //行号
                mDayModel.locationY = weekOfMonth;
                //列号
                mDayModel.locationX = dayOfWeek - 2;
            }
            //特殊情况(星期天)
            else {
                //行号
                mDayModel.locationY = weekOfMonth - 1;
                //列号
                mDayModel.locationX = dayOfWeek + 5;
            }
        } else {
        //... ...
    }

Calendar类中:
先修改无效点击事件,再【屏蔽没有日期处的点击事件方法】中单独为第一种情况加一个判断,第一行:很好判断,就一号一个对象可以点击,其它都屏蔽;最后一行,只是日历整体向下移动了一个距离,所以【locationX不变】,【locationY加一】就行了。

    /**
     *屏蔽没有日期处的点击事件
     */
    private boolean invalidClick(int locationY,int locationX) {

        //单独处理第一种特殊情况
        if (DayController.getFlag() == 1) {
            //选中的日历第一行的时候,前面有几个空格点击的时候肯定不能做相应的操作
            if (locationY == 0) {
                if (locationX < 6) {//location值为0 1 2 3 4 5的都不能点击
                    Toast.makeText(context, "无效点击", Toast.LENGTH_SHORT).show();
                    return false;
                }
            }
            //选中的日历第最后一行的时候,后面有几个空格点击的时候肯定也是不能做相应的操作
            else if (locationY == calendar.getActualMaximum(Calendar.WEEK_OF_MONTH)) {//相比原来我添加了一行,所以行数加一
                calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
                if (locationX > calendar.get(Calendar.DAY_OF_WEEK) - 2) {
                    Toast.makeText(context, "无效点击", Toast.LENGTH_SHORT).show();
                    return false;
                }
            }

        }else{
            //... ... 
        }
        return true;
    }

再再【设置选中日期的方法】中单独,为第一种情况加一个判断,原理也很简单,由于整体向下移动一个距离,所以【locationX不变】,【locationY 加一】就可以了。

    /**
     * 设置选中的日期
     */
    private void setSelectedDay(int locationY,int locationX) {
        //单独处理第一种特殊情况
        if (DayController.flag == 1) {

            if (locationX != 6) { //【星期天1~6】
                calendar.set(Calendar.WEEK_OF_MONTH, (int) locationY);//相比正常行号加一
                calendar.set(Calendar.DAY_OF_WEEK, (int) (locationX + 2));
            }
            else { //特殊情况【星期天】
                calendar.set(Calendar.WEEK_OF_MONTH, (int) locationY + 1);//相比正常行号加一
                calendar.set(Calendar.DAY_OF_WEEK, (int) (locationX + 2));
            }

        }else{
            //... ...
        }

    }
(4)修改完成之后再看看第一种特殊情况解决了没有:

答案是:解决了~,如下图所示:

               这里写图片描述

到此为止【自定义日历(中式)】就从【自定义日历(英式)】中修改过来了。

5. 本章小结:

这是篇博文详细的记录了【自定义日历(中式)】是如何从【自定义日历(英式)】中修改过来的,之前也说了我会尽可能的详细的去创作,由于篇幅和知识重点分布的特点,我把它拆分成了三个章节,一点一滴记录它是如何完成的,中间会扩展一些东西,包括一些常用到的android开发技术,和我自己开发过程中遇到的问题,我都会提到,因为这样创作的话对于新手来说提升会很大,因为我自己也是新手,刚刚学习Android不久,没有太多的经验,所以大神勿喷噢O(∩_∩)O

PS:本Demo的推荐阅读顺序是【 ① —> ② —> ③ 】或者【 ② —> ③】,本章编号为“③”

6. 相关参考链接

① 极具个性化的【自定义日历】(预热篇)—超详细,记录你的学习计划与完成情况
② 极具个性化的【自定义日历】英式(纯干货)—超详细,记录你的学习计划与完成情况

7. Demo下载链接:

【自定义日历(英式)】下载地址为:http://download.csdn.net/detail/ld_dragonliu/9875880
【自定义日历(中式)】下载地址为:http://download.csdn.net/detail/ld_dragonliu/9875883
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值