UI框架应该逻辑与界面实现分离,该日历控件使用了热插拔的设计 ,简单几步即可实现你需要的UI效果,热插拔的思想是你提供你的实现,我提供我的插座接口,与自定义Behavior是一样的思想。
听说第一页无效果图就看不下去了?先上个高仿魅族日历界面
项目开源地址
CalendarView的优势:
1、热插拔设计,根据不同的UI需求完全自定义UI,简单几步即可实现,自定义事件日历标记、颜色、农历等
2、完全Canvas绘制,性能和速度都很不错,相比大多数基于GridView或RecyclerView实现的占用内存更低,启动速度更快
3、支持收缩、展开、快速年月份选择等
4、简洁易懂的源码,易学习。
先看看控件的attr
复制代码
XML用法
如果需要在日历控件下方使用其它控件,使用CalendarLayout控件即可,calendar_content_view_id为其它控件的id,支持任意控件,如RecyclerView、ListView。CalendarView的calendar_card_view为任意自定义实现的日历绘制控件路径。
复制代码
熟悉一下这几个简单的特性,看看日历内容界面的绘制BaseCalendarCardView,根据需求实现以下部分方法即可
/** * 开始绘制前的回调钩子,这里做一些初始化的操作,每次绘制只调用一次,性能高效 * 没有需要可忽略不实现 * 例如: * 1、需要绘制圆形标记事件背景,可以在这里计算半径 * 2、绘制矩形选中效果,也可以在这里计算矩形宽和高 */ protected void onPreviewHook() { // TODO: 2017/11/16 } /** * 循环绘制开始的回调,不需要可忽略 * 绘制每个日历项的循环,用来计算baseLine、圆心坐标等都可以在这里实现 * * @param x 日历Card x起点坐标 * @param y 日历Card y起点坐标 */ protected void onLoopStart(int x, int y) { // TODO: 2017/11/16 } /** * 绘制选中的日期 * * @param canvas canvas * @param calendar 日历日历calendar * @param x 日历Card x起点坐标 * @param y 日历Card y起点坐标 * @param hasScheme hasScheme 非标记的日期 */ protected abstract void onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme); /** * 绘制标记的日期UI * * @param canvas canvas * @param calendar 日历calendar * @param x 日历Card x起点坐标 * @param y 日历Card y起点坐标 */ protected abstract void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y); /** * 绘制日历文本 * * @param canvas canvas * @param calendar 日历calendar * @param x 日历Card x起点坐标 * @param y 日历Card y起点坐标 * @param hasScheme 是否是标记的日期 * @param isSelected 是否选中 */ protected abstract void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected);复制代码
举个例子:如果你的需求是类似魅族日历的UI,那么第一步,继承BaseCalendarCardView,然后实现onDrawSelected、onDrawScheme、onDrawText三个回调函数即可
public class MeiZuCalendarCardView extends BaseCalendarCardView { private Paint mTextPaint = new Paint(); private Paint mSchemeBasicPaint = new Paint(); private float mRadio; private int mPadding; private float mSchemeBaseLine; public MeiZuCalendarCardView(Context context) { super(context); mTextPaint.setTextSize(dipToPx(context, 8)); mTextPaint.setColor(0xff111111); mTextPaint.setAntiAlias(true); mTextPaint.setFakeBoldText(true); mSchemeBasicPaint.setAntiAlias(true); mSchemeBasicPaint.setStyle(Paint.Style.FILL); mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER); mSchemeBasicPaint.setColor(0xffed5353); mSchemeBasicPaint.setFakeBoldText(true); mRadio = dipToPx(getContext(), 7); mPadding = dipToPx(getContext(), 4); Paint.FontMetrics metrics = mSchemeBasicPaint.getFontMetrics(); mSchemeBaseLine = mRadio - metrics.descent + (metrics.bottom - metrics.top) / 2 + dipToPx(getContext(), 1); } /** * 绘制选中的日期 * * @param canvas canvas * @param calendar 日历日历calendar * @param x 日历Card x起点坐标 * @param y 日历Card y起点坐标 * @param hasScheme hasScheme 非标记的日期 */ @Override protected void onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) { mSelectedPaint.setStyle(Paint.Style.FILL); mSelectedPaint.setColor(0x80cfcfcf); canvas.drawRect(x + mPadding, y + mPadding, x + mItemWidth - mPadding, y + mItemHeight - mPadding, mSelectedPaint); } /** * 绘制标记的日期UI 这里魅族界面不需要绘制多彩风格,忽略即可 * * @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) { } /** * 绘制日历文本 * * @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; if (hasScheme) { //绘制日期 canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top, calendar.isCurrentDay() ? mCurDayTextPaint : calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint); //绘制农历 canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mCurMonthLunarTextPaint); mTextPaint.setColor(Color.WHITE); mSchemeBasicPaint.setColor(calendar.getSchemeColor()); //绘制圆圈 canvas.drawCircle(x + mItemWidth - mPadding - mRadio / 2, y + mPadding + mRadio, mRadio, mSchemeBasicPaint); //绘制事件文本 canvas.drawText(calendar.getScheme(), x + mItemWidth - mPadding - mRadio, y + mPadding + mSchemeBaseLine, mTextPaint); } else { canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top, calendar.isCurrentDay() ? mCurDayTextPaint : calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint); canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mCurMonthLunarTextPaint); } } /** * dp转px * * @param context context * @param dpValue dp * @return px */ private static int dipToPx(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); }}复制代码
第二步:使用方法、app:calendar_card_view="xxx.xx.MeiZuCalendarCardView"
效果预览
高仿魅族日历界面
快速年月份选择
其它作者实现的几个UI效果预览,简单源码都在demo可以看到
多彩风格界面
下标风格界面
简单没有农历界面