Android原生的CalendarView根本无法满足我们日常开发的需要,在开发吾记APP的过程中,我觉得需要来一款高性能且美观简洁的日历控件,觉得魅族的日历风格十分适合,于是打算撸一款。

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

compile 'com.haibin:calendarview:1.0.2'

先上效果图:

动手之前我们需要分析一下魅族是怎么设计如此高性能的日历的,我们打开开发者选项中的显示布局边界:

好吧,一开始我以为日历界面是ViewPager+RecyclerView的,但是这么一看明显就不是了,如果是RecyclerView,那么我们假设每个月的卡片都有5*7=35个item,每个item根布局是RelativeLayout+3个TextView,我们大概估算一下日历初始化时要加载的控件:

3个ViewPager的item * 35个RecyclerView的Item * 4(每个item的控件数) + 8 (星期栏)= 420+

我的天,这可不能这么干,明显性能大打折扣,我们再来看看月份控件:

好吧,这里看上去就是ViewPager+RecyclerView来做的,每个RecyclerView的item都只是一个控件,里面绘制了文本 ,这里大概就分析清楚了。

我们采取折中的方式,日历界面和月份卡界面均采用ViewPager+RecyclerView的方式,不同的是所有的item我们都采用自定义ViewCanvas绘制的方式来做,这样性能虽然比不上魅族,但速度体验基本差不多,下面先看日历界面的item代码:只需要绘制3个文本即可

public class CellView extends View {

    private int mDay = 20;
private String mLunar;
private String mScheme;
private Paint mDayPaint = new Paint();
private Paint mLunarPaint = new Paint();
private Paint mSchemePaint = new Paint();
private Paint mCirclePaint = new Paint();
private int mRadius;
private int mCirclePadding;
private int mCircleColor; public CellView(Context context) {
this(context, null);
} public CellView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs); mDayPaint.setAntiAlias(true);
mDayPaint.setColor(Color.BLACK);
mDayPaint.setFakeBoldText(true);
mDayPaint.setTextAlign(Paint.Align.CENTER); mLunarPaint.setAntiAlias(true);
mLunarPaint.setColor(Color.GRAY);
mLunarPaint.setTextAlign(Paint.Align.CENTER); mSchemePaint.setAntiAlias(true);
mSchemePaint.setColor(Color.WHITE);
mSchemePaint.setFakeBoldText(true);
mSchemePaint.setTextAlign(Paint.Align.CENTER); mCirclePaint.setAntiAlias(true);
mCirclePaint.setStyle(Paint.Style.FILL); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CellView);
mDayPaint.setTextSize(array.getDimensionPixelSize(R.styleable.CellView_cell_day_text_size, 18));
mLunarPaint.setTextSize(array.getDimensionPixelSize(R.styleable.CellView_cell_lunar_text_size, 12));
mRadius = (int) array.getDimension(R.styleable.CellView_cell_scheme_radius, 8);
mSchemePaint.setTextSize(array.getDimensionPixelSize(R.styleable.CellView_cell_scheme_text_size, 6));
mCirclePadding = array.getDimensionPixelSize(R.styleable.CellView_cell_circle_padding, 4);
mCirclePaint.setColor(array.getColor(R.styleable.CellView_cell_circle_color, 0xff16BB7F));
array.recycle();
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
int w = (width - getPaddingLeft() - getPaddingRight());
int h = (height - getPaddingTop() - getPaddingBottom()) / 4;
canvas.drawText(String.valueOf(mDay), w / 2, 2 * h + getPaddingTop(), mDayPaint);
canvas.drawText(mLunar, w / 2, 4 * h + getPaddingTop(), mLunarPaint);
if (!TextUtils.isEmpty(mScheme)) {
canvas.drawCircle(w / 2 + mCirclePadding + mDayPaint.getTextSize(), getPaddingTop() + h, mRadius, mCirclePaint);
canvas.drawText(mScheme, w / 2 + mCirclePadding + mDayPaint.getTextSize(), getPaddingTop() + mRadius / 2 + h, mSchemePaint);
}
} /**
* 初始化日历
* @param day 天
* @param lunar 农历
* @param scheme 事件标记
*/
void init(int day, String lunar, String scheme) {
this.mDay = day;
this.mLunar = lunar;
this.mScheme = scheme;
} void setTextColor(int textColor) {
mDayPaint.setColor(textColor);
mLunarPaint.setColor(textColor);
} void setCircleColor(int circleColor) {
mCirclePaint.setColor(circleColor);
invalidate();
}
}

月份卡自定义View

public class MonthView extends View {
private int mDiff;//第一天偏离周日多少天
private int mCount;//总数
private int mLastCount;//最后一行的天数
private int mLine;//多少行
private Paint mPaint = new Paint();
private Paint mSchemePaint = new Paint();
private List<Calendar> mSchemes;
private Calendar mCalendar; public MonthView(Context context) {
this(context, null);
} public MonthView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mPaint.setAntiAlias(true);
mPaint.setTextAlign(Paint.Align.CENTER);
mSchemePaint.setAntiAlias(true);
mSchemePaint.setTextAlign(Paint.Align.CENTER);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MonthView);
mPaint.setTextSize(array.getDimensionPixelSize(R.styleable.MonthView_month_view_text_size, 12));
mSchemePaint.setTextSize(array.getDimensionPixelSize(R.styleable.MonthView_month_view_text_size, 12));
mPaint.setColor(array.getColor(R.styleable.MonthView_month_view_text_color, Color.BLACK));
mSchemePaint.setColor(array.getColor(R.styleable.MonthView_month_view_remark_color, Color.RED));
array.recycle();
measureLine();
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
int pLeft = getPaddingLeft();
int w = (width - getPaddingLeft() - getPaddingRight()) / 7;
int h = (height - getPaddingTop() - getPaddingBottom()) / 6;
int d = 0;
for (int i = 0; i < mLine; i++) {
if (i == 0) {//第一行
for (int j = 0; j < (7 - mDiff); j++) {
++d;
canvas.drawText(String.valueOf(j + 1), mDiff * w + j * w + pLeft + w / 2, h, isScheme(d) ? mSchemePaint : mPaint);
}
} else if (i == mLine - 1 && mLastCount != 0) {
int first = mCount - mLastCount + 1;
for (int j = 0; j < mLastCount; j++) {
++d;
canvas.drawText(String.valueOf(first), j * w + pLeft + w / 2, (i + 1) * h, isScheme(d) ? mSchemePaint : mPaint);
++first;
}
} else {
int first = i * 7 - mDiff + 1;
for (int j = 0; j < 7; j++) {
++d;
canvas.drawText(String.valueOf(first), j * w + pLeft + w / 2, (i + 1) * h, isScheme(d) ? mSchemePaint : mPaint);
++first;
}
}
}
} /**
* 计算行数
*/
private void measureLine() {
int offset = mCount - (7 - mDiff);
mLine = 1 + (offset % 7 == 0 ? 0 : 1) + offset / 7;
mLastCount = offset % 7;
} /**
* 初始化月份卡
* @param mDiff 偏离天数
* @param mCount 当月总天数
* @param mYear 哪一年
* @param mMonth 哪一月
*/
void init(int mDiff, int mCount, int mYear, int mMonth) {
this.mDiff = mDiff;
this.mCount = mCount;
mCalendar = new Calendar();
mCalendar.setYear(mYear);
mCalendar.setMonth(mMonth);
measureLine();
invalidate();
} void setSchemes(List<Calendar> mSchemes) {
this.mSchemes = mSchemes;
} void setSchemeColor(int schemeColor) {
if (schemeColor != 0)
mSchemePaint.setColor(schemeColor);
if(schemeColor == 0xff30393E)
mSchemePaint.setColor(Color.RED);
} private boolean isScheme(int day) {
if (mSchemes == null || mSchemes.size() == 0)
return false;
mCalendar.setDay(day);
return mSchemes.contains(mCalendar);
}
}

  

其它代码没有什么难度,日历算法是github上找的,更多详情请看仓库地址:https://github.com/huanghaibin-dev/CalendarView

撸一个Android高性能日历控件,高仿魅族的更多相关文章

  1. Android自定义日历控件(继承系统控件实现)

    Android自定义日历控件(继承系统控件实现) 主要步骤 编写布局 继承LinearLayout设置子控件 设置数据 继承TextView实现有圆圈背景的TextView 添加Attribute 添 ...

  2. android 自定义日历控件

    日历控件View: /** * 日历控件 功能:获得点选的日期区间 * */ public class CalendarView extends View implements View.OnTouc ...

  3. 在iOS上实现一个简单的日历控件

    http://blog.csdn.net/jasonblog/article/details/21977481 近期需要写一个交互有点DT的日历控件,具体交互细节这里略过不表. 不过再怎么复杂的控件, ...

  4. 分享一个WPF下日历控件(Calendar)的样式

    WPF日历控件的一个样式 WPF自带的日历控件样式可能会比较丑,要修改其样式看起来挺复杂的,实际上很简单,用Blend打开,修改三个模板,基本就能改变全部面貌,也很容易 先上图 样式如下: <S ...

  5. Android图表日历控件组件

    1.图表引擎 - AChartEngine AChartEngine是一款基于Android的图表绘制引擎,它为Android开发人员提供了非常多有用的图表绘制工具类,假设你须要在Android应用中 ...

  6. 一个android的各种控件库

    在这里 https://github.com/Trinea/android-open-project 很多的listview,非常棒

  7. Android自己定义控件实战——仿淘宝商品浏览界面

    转载请声明出处http://blog.csdn.net/zhongkejingwang/article/details/38656929 用手机淘宝浏览商品详情时,商品图片是放在后面的,在第一个Scr ...

  8. Android 一个日历控件的实现代码

    转载  2017-05-19   作者:Othershe   我要评论 本篇文章主要介绍了Android 一个日历控件的实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看 ...

  9. Android自定义控件之日历控件

      标签: android 控件 日历 应用 需求 2015年09月26日 22:21:54 25062人阅读 评论(109) 收藏 举报 分类: Android自定义控件系列(7) 版权声明:转载注 ...

随机推荐

  1. mysql学习之权限管理

    数据库权限的意义: 为了保证数据库中的业务数据不被非授权的用户非法窃取,需要对数据库的访问者进行各种限制,而数据库安全性控制措施主要有这三种,第一种用户身份鉴别,手段可以是口令,磁卡,指纹等技术,只有 ...

  2. java中File类的常用所有方法及其应用

    创建:createNewFile()在指定位置创建一个空文件,成功就返回true,如果已存在就不创建,然后返回false.mkdir()  在指定位置创建一个单级文件夹.mkdirs()  在指定位置 ...

  3. hashcode的一些了解

    1.hashcode的作用? hashcode代表对象的特征,在集合类中广泛的使用. 2.hashcode是如何生成的? 在普通的对象中,获得对象的内存的地址,经过一些算法,不同对象生成不同的hash ...

  4. 融云发送手机短信验证短信(.net版本)

    首先本次需求是在MVC5中进行的,通过收费的融云服务来验证手机号码,而且本次的项目也是前后台分离,所以添加了WEBAPI2,那么先添加WEBAPI的接口 using System; using Sys ...

  5. Monkey实例测试

    三.Monkey实例测试 Windows下(注:2-4步是为了查看我们可以测试哪些应用程序包,可省略): 1. 通过eclipse启动一个Android的emulator或者2 2. 在命令行中输入: ...

  6. 连载《一个程序猿的生命周期》-《发展篇》 - 7.是什么阻碍了"程序猿"的发展?

    有两件事想记录一下,具有普遍性和代表性."程序猿"加了引号,是泛指一类人,也并非局限于IT行业.       山东子公司的总经理是公司大股东之一,个子不高.有些秃顶.面容显老,但看 ...

  7. TCP和UDP的"保护消息边界”

    转自:http://blog.csdn.net/zhangxinrun/article/details/6721427 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP ...

  8. rsyslog+mariadb+loganalyzer实现日志服务器搭建

    rsyslog+mariadb+loganalyzer实现日志服务器搭建 一.概述 Linux的日志记录了用户在系统上一切操作,包括系统自身运作产生的日志,这些日志是应使用者了解服务器的情况最好的资料 ...

  9. SUN SERVER X3-2 服务器数据写入缓慢

    使用一台sun server x3-2,SAS 300G 2.5寸硬盘两块:8G内存条*2,CPU E5-2609V3 安装一套服务器系统时感觉安装进度很慢,但一直找不到原因,因为要重做系统,同事练手 ...

  10. 启程、对C的继承

    Cocoa和Object-c是苹果公司OS X和IOS操作系统的核心.   .m扩展名表示文件使用的是Objective-c代码.   与C语言一样,Objective-c使用头文件来包含结构体.符号 ...