先上效果图:
好了,接下来对这个日历逐步解析:
1.设定日历的宽高。这里宽为屏幕宽度,高为屏幕高度的2/5
//widthMeasureSpec heightMeasureSpec,他们是和宽高相关的,
//但它们其实不是宽和高, 而是由宽、高和各自方向上对应的测量模式来合成的一个值
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d(TAG,"onMeasure");
//屏幕宽度
surface.width = getResources().getDisplayMetrics().widthPixels;
//屏幕高度的2/5
surface.height = (int) (getResources().getDisplayMetrics().heightPixels*2/5);
widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(surface.width,View.MeasureSpec.EXACTLY);
heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(surface.height,View.MeasureSpec.EXACTLY);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
2.现在来画这两条线(粉色的2条线)
PS:图中标注的绿色点为该日历的坐标原点
//路径和画笔
canvas.drawPath(surface.boxPath, surface.borderPaint);
接下来给出路径
boxPath = new Path();
boxPath.rLineTo(width, 0);
//移动下一次操作的起点位置
boxPath.moveTo(0, weekHeight);
boxPath.rLineTo(width, 0);
关于rLineTo的讲解(lineTo和rLineTo的区别),请移步到 http://blog.youkuaiyun.com/wzping435/article/details/78583555
3.接下来绘制周的文字(日、一、二、三、四、五、六)
float weekTextY = surface.weekHeight * 2 / 3f;
for (int i = 0; i < surface.weekText.length; i++) { //长度为7
float weekTextX = i * surface.cellWidth + surface.cellWidth/2 - surface.weekPaint.measureText(surface.weekText[i])/2;
//drawText(String text, float x, float y, Paint paint)
//text:要绘制的文字 x:基点x坐标 y:基点y坐标 paint:画笔
canvas.drawText(surface.weekText[i], weekTextX, weekTextY, surface.weekPaint);
}
这里基点y坐标设定为周的高度的2/3(图中红线部分),让其看起来是居中显示的
这里基点x坐标是动态改变的,为图中绿色点标注的
关于drawText(String text, float x, float y, Paint paint)具体讲解请看 http://blog.youkuaiyun.com/wzping435/article/details/78584162
4:接下来讲解具体的日历部分
存放日历需要6行7列(42),所以我们定义一个大小为42的int型数组来存放相应的日历数字
int[] date = new int[42];
那么我们要做的事情就是将正确的日历数字存放进相应的数组位置中,这里分为三部分处理(本月的上月部分,本月部分,本月的下月部分)
1)设置本月的1号
//设置日历的时间为当前时间
calendar.setTime(curDate);//Sun Nov 19 21:07:41 GMT+08:00 2017
//将日历设定为本月的1号
calendar.set(Calendar.DAY_OF_MONTH, 1);//Wed Nov 01 21:13:40 GMT+08:00 2017
//本月的1号在日历上是第四个(角标从1开始)
int dayInWeek = calendar.get(Calendar.DAY_OF_WEEK);//4
//date[3]=1
date[dayInWeek-1] = 1;//数组角标从0开始 设定本月的1号
2)设置上月部分
int monthStart = dayInWeek;
monthStart -= 1;
//将日历设定为上个月的最后一号(10月有31号)
calendar.set(Calendar.DAY_OF_MONTH, 0); //Tue Oct 31 21:37:57 GMT+08:00 2017
//上句中设置为哪号,这里获取的天数即为多少天
int dayInmonth = calendar.get(Calendar.DAY_OF_MONTH);//31
for (int i = monthStart - 1; i >= 0; i--) {
date[i] = dayInmonth; //data[2]=31 data[1]=30 data[0]=29
dayInmonth--;
}
3)设置本月部分
//设置日历的时间为当前时间 Sun Nov 19 21:51:26 GMT+08:00 2017
calendar.setTime(curDate);
//将日历设定为 当月日期加上一个月 即Tue Dec 19 21:51:26 GMT+08:00 2017
calendar.add(Calendar.MONTH, 1);
//将日历设定为上个月的最后一号
calendar.set(Calendar.DAY_OF_MONTH, 0);//Thu Nov 30 21:54:51 GMT+08:00 2017
//上句中设置为哪号,这里获取的天数即为多少天
int monthDay = calendar.get(Calendar.DAY_OF_MONTH);//30
for (int i = 1; i < monthDay; i++) {
date[monthStart + i] = i + 1; //date[4]=2 date[5]=3 ... date[32]=30
}
4)设置下月部分
//当前系统月份结束索引 3+30 = 33
curEndIndex = monthStart + monthDay;
// next month
for (int i = monthStart + monthDay; i < 42; i++) {
date[i] = i - (monthStart + monthDay) + 1; //date[33]=1 date[34]=2 ... date[41]=9
}
至此,日历中相应的日期都存储好了,同样调用drawText方法绘制即可
5.关于 日历中今日的日期的显示颜色和背景圆圈(可替换)、日历的月份切换、日历的点击事件等等就不一一讲解,
(今日日期是以 2017年11月19(当前系统时间)为准的)
具体有需要请下载Demo:http://download.youkuaiyun.com/download/wzping435/10126172