知足你各类姿式的最美Android开源日历

日历控件定制是移动开发平台上比较常见的并且比较难的需求,通常会遇到如下问题:

  • 性能差,加载速度慢,缘由是各类基于GridView或RecyclerView等ViewGroup实现的日历,控件数太多,假设一个月视图界面有42个item,每一个item里面分别就有2个子TextView:天数、农历数和自己3个控件,这样一个月视图就有42 * 3+1(RecyclerView or GridView),清楚ViewPager特性的开发者就会明白,通常ViewPager持有3个item,那么一个日历控件持有的View控件数的数量将达到 1(ViewPager)+ 3(RecyclerView or GridView) + 3 * 42 * 3 = 382,若是用1个View来代替RecyclerView等,用Canvas来代替各类TextView,那View的数量瞬间将降低360+,内存和性能优点将至关明显了
  • 难定制 通常日历框架发布的同时也将UI风格肯定下来了,假如人人都使用这个日历框架,那么将会千篇一概,难以突出本身的风格,要么就得改源码,成本太大,不太实际
  • 功能性不足 例如没法自定义周起始、没法更改选择模式、动态设置UI等等
  • 没法知足产品经理提出的变态需求 今天产品经历说咱们要这样的实现、明天跟你说这里得改、后天说咱们得限制一些日期...

但如今有了全新的 CalendarView 控件,它解锁了各类姿式,并且你能够不断调教它,直到你知足为止...

国际惯例:先放项目github地址

https://github.com/huanghaibin-dev/CalendarViewjava

国内惯例:无图言吊

     

     

CalendarView的骚特性

  • 基于Canvas绘制,极速性能
  • 热插拔思想,任意定制周视图、月视图,即插即用!
  • 支持单选、多选、国内手机日历默认自动选择等选择模式
  • 支持静态、动态设置周起始,一行代码搞定
  • 支持静态、动态设置日历项高度、日历填充模式
  • 支持设置任意日期范围、任意拦截日期
  • 支持多点触控、手指平滑切换过渡,拒绝界面抖动
  • 既然这么多支持,那必定支持英语、繁体、简体,任意定制实现

接下来请看CalendarView骚操做,看看它是能够怎样调教的

  • 你这样继承本身的月视图和周视图,只须要依次实现绘制选中:onDrawSelected、绘制事务:onDrawScheme、绘制文本:onDrawText这三个回调便可,参数和坐标都已经在回调函数上实现好,周视图也是同样的逻辑,只是不须要y参数
/** * 定制高仿魅族日历界面,按你的想象力绘制出各类各样的界面 * Created by huanghaibin on 2017/11/15. */

public class MeiZuMonthView extends MonthView {

    /** * 绘制选中的日子 * * @param canvas canvas * @param calendar 日历日历calendar * @param x 日历Card x起点坐标 * @param y 日历Card y起点坐标 * @param hasScheme hasScheme 非标记的日期 * @return 返回true 则绘制onDrawScheme,由于这里背景色不是是互斥的,因此返回true */
    @Override
    protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
        canvas.drawRect(x + mPadding, y + mPadding, x + mItemWidth - mPadding, y + mItemHeight - mPadding, mSelectedPaint);
        return true;
    }

    /** * 绘制标记的事件日子 * * @param canvas canvas * @param calendar 日历calendar * @param x 日历Card x起点坐标 * @param y 日历Card y起点坐标 */
    @Override
    protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
        canvas.drawCircle(x + mItemWidth - mPadding - mRadio / 2, y + mPadding + mRadio, mRadio, mSchemeBasicPaint);
        canvas.drawText(calendar.getScheme(),
                x + mItemWidth - mPadding - mRadio / 2 - getTextWidth(calendar.getScheme()) / 2,
                y + mPadding + mSchemeBaseLine, mTextPaint);
    }

    /** * 绘制文本 * * @param canvas canvas * @param calendar 日历calendar * @param x 日历Card x起点坐标 * @param y 日历Card y起点坐标 * @param hasScheme 是不是标记的日期 * @param isSelected 是否选中 */
    @Override
    protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
        int cx = x + mItemWidth / 2;
        int top = y - mItemHeight / 6;

        boolean isInRange = isInRange(calendar);

        if (isSelected) {
            canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
                    mSelectTextPaint);
            canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mSelectedLunarTextPaint);
        } else if (hasScheme) {
            canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
                    calendar.isCurrentMonth() && isInRange ? mSchemeTextPaint : mOtherMonthTextPaint);

            canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mCurMonthLunarTextPaint);
        } else {
            canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
                    calendar.isCurrentDay() ? mCurDayTextPaint :
                            calendar.isCurrentMonth() && isInRange ? mCurMonthTextPaint : mOtherMonthTextPaint);
            canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10,
                    calendar.isCurrentDay() && isInRange ? mCurDayLunarTextPaint :
                            calendar.isCurrentMonth() ? mCurMonthLunarTextPaint : mOtherMonthLunarTextPaint);
        }
    }
}
复制代码
  • 当你实现好以后,直接在xml界面上添加特性,能够即时预览效果:
<attr name="month_view" format="string" /><!--自定义月视图路径-->
<attr name="week_view" format="string" /> <!--自定义周视图路径-->

app:month_view="com.haibin.calendarviewproject.MeiZuCalendarCardView"
app:week_view="com.haibin.calendarviewproject.MeiZuWeekView"

复制代码
  • 但这种静态模式可能没法知足你的需求,你可能须要动态变换定制的视图界面,因而你可使用热插拔特性,即插即用,不爽就换:
mCalendarView.setWeekView(MeizuWeekView.class);

mCalendarView.setMonthView(MeizuMonthView.class);
 
复制代码
  • CalendarView也提供了高效便利的年视图,能够快速切换年份、月份,十分便利
  • 但年视图也不必定就适合产品经理的胃口,产品经理但愿像小米日历同样,弹出DatePickerView,经过它来跳转日期,因而你可使用如下的API来让日历与其它控件联动
CalendarView.scrollToCalendar();

CalendarView.scrollToNext();

CalendarView.scrollToPre();

CalendarView.scrollToXXX();

复制代码
  • 你也许须要像魅族日历同样,能够静态、动态更换周起始
app:week_start_with="mon、sun、sat"

CalendarView.setWeekStarWithSun();

CalendarView.setWeekStarWithMon();

CalendarView.setWeekStarWithSat();

复制代码
  • 假如你是作酒店、旅游等应用场景的APP的,那么须要可选范围的日历,你能够这样继承,和普通视图实现彻底同样
public class CustomRangeMonthView extends RangeMonthView{
    
}

public class CustomRangeWeekView extends RangeWeekView{
    
}

复制代码
  • 而后你须要设置选择模式为范围模式:select_mode="range_mode"git

  • 酒店式日历场景固然是不能从昨天开始订房的,也不能无限期订房,因此你须要静态或动态设置日历范围、精确到具体某一天!!!github

<attr name="min_year" format="integer" />
<attr name="max_year" format="integer" />
<attr name="min_year_month" format="integer" />
<attr name="max_year_month" format="integer" />
<attr name="min_year_day" format="integer" />
<attr name="max_year_day" format="integer" />

CalendarView.setRange(int minYear, int minYearMonth, int minYearDay,
         int maxYear, int maxYearMonth, int maxYearDay)

复制代码
  • 固然还有更特殊的日子也是不能选择的,例如:某月某号起这N天时间内由于超强台风来袭,酒店需中止营业N天,这段期间不可订房,这时日期拦截器就排上用场了
//设置日期拦截事件
mCalendarView.setOnCalendarInterceptListener(new CalendarView.OnCalendarInterceptListener() {
     @Override
     public boolean onCalendarIntercept(Calendar calendar) {
         //这里写拦截条件,返回true表明拦截
         return calendar.isWeekend();
     }

     @Override
     public void onCalendarInterceptClick(Calendar calendar, boolean isClick) {
         //todo 点击拦截的日期回调
     }
});
复制代码
  • 添加日期拦截器和范围设置后,你能够在周月视图按需求得到他们的结果
boolean isInRange = isInRange(calendar);//日期是否在范围内,超出范围的能够置灰

boolean isEnable = !onCalendarIntercept(calendar);//日期是否可用,没有被拦截,被拦截的能够置灰

复制代码
  • 假如你是作清单类、任务类APP的,可能会有这样的需求:标记某天事务的进度,这也很简单,由于:日历界面长什么样,你本身说了算!!!
  • 也许你只须要像原生日历那样就够了,但原生日历那奇怪且十分不友好的style,受到theme的影响,各类头疼,使用此控件,你只须要简简单单定制月视图就够了,CalendarView 能很是简单就高仿各类日历UIcanvas

  • CalendarView 提供了 setSchemeDate(Map<String, Calendar> mSchemeDates) 这个十分高效的API用来动态标记事务,即时你的数据量达到数千、数万、数十万,都不会对UI渲染形成影响bash

  • 日历类 Calendar 提供了许多十分有用的APIapp

boolean isWeekend();//判断是否是周末,能够用不一样的画笔绘制周末的样式

int getWeek();//获取星期

String getSolarTerm();//获取24节气,能够用不一样颜色标记不一样节日

String getGregorianFestival();//获取公历节日,自由判断,把节日换上喜欢的颜色

String getTraditionFestival();//获取传统节日

boolean isLeapYear();//是不是闰年

int getLeapMonth();//获取闰月

boolean isSameMonth(Calendar calendar);//是否相同月

int compareTo(Calendar calendar);//毕竟日期大小 -1 0 1

long getTimeInMillis();//获取时间戳

int differ(Calendar calendar);//日期运算,相差多少天
复制代码

其它各类场景姿式就很少说了,你得本身去解锁,一块儿看Demo以及各类APP的风骚实现

     

     

     

写在最后,框架自己是为了解决各类各样的场景而设计的,UI自己是靠本身绘制的,很是简单,不懂的请优先看Demo,你能够自由发挥想象力定制最喜欢的日历,只有你想不到,Demo基本给出了各类场景的实现思路。以为能够的请给个star或者留下你宝贵的意见。

博客惯例:结尾再放github地址,否则你就不肯意翻到最上面点击了

https://github.com/huanghaibin-dev/CalendarView框架

相关文章
相关标签/搜索