日历控件的效果图如下:
下面是MyCalendarView类的源码,注释也写得比较详细了:
package com.example.testview.view;
import java.util.Calendar;
import java.util.Date;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
/**
* 自定义的日历控件
* @author yubo<br/>
* 2014年12月12日
*/
public class MyCalendarView extends View implements OnTouchListener {
private static final int ROW_NUM = 7;// 行数
private static final int COLUMN_NUM = 7;// 列数
private float width;// MyView的宽度
private float height;// MyView的高度
private float cellWidth;// 格子的宽度
private float cellHeight;// 格子的高度
private Paint linePaint;// 画线条的画笔
private Paint cellBgPaint;// 格子背景的画笔
private Paint textPaint;// 字体的画笔
private Paint datePaint;// 日期画笔
private Paint preOrNextMonthPaint;//画上月或下月的画笔
private int touchXIndex = -1;// 按下的格子的索引值
private int touchYIndex = -1;// 按下的格子的索引值
private Rect textRect = new Rect();// 字符串的矩形区域
public String[] weekText = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
"Sat" };// 星期
private Calendar calendar;
private Calendar dateNow;
private int[] daysArray = new int[42];// 存放42个格子中应该绘制的数字
private int startIndex;//要显示的月份的第一天在上面的数组中的索引
private int endIndex;//要显示的月份的最后一天在上面的数组中的索引
private OnCellSelectedListener listener;
public MyCalendarView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public MyCalendarView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyCalendarView(Context context) {
super(context);
init(context);
}
private void init(Context context) {
linePaint = new Paint();
linePaint.setColor(Color.parseColor("#e3e3e3"));
cellBgPaint = new Paint();
cellBgPaint.setColor(Color.parseColor("#1e696969"));
textPaint = new Paint();
textPaint.setColor(Color.parseColor("#000000"));
textPaint.setTypeface(Typeface.DEFAULT_BOLD);
datePaint = new Paint();
datePaint.setColor(Color.BLACK);
preOrNextMonthPaint = new Paint();
preOrNextMonthPaint.setColor(Color.GRAY);
calendar = Calendar.getInstance();
calendar.setTime(new Date());
dateNow = Calendar.getInstance();
dateNow.setTime(new Date());
setOnTouchListener(this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 得到MyView的显示区域长宽
width = getMeasuredWidth();
height = getMeasuredHeight();
// 计算每个格子的长宽
cellWidth = width / COLUMN_NUM;
cellHeight = height / ROW_NUM;
}
// 画网格线,边缘的线不画,通过View的background属性指定
private void drawLines(Canvas canvas) {
// 画横线
for (int row = 1; row < ROW_NUM; row++) {
canvas.drawLine(0, row * cellHeight, width, row * cellHeight,
linePaint);
}
// 画竖线
for (int col = 1; col < COLUMN_NUM; col++) {
canvas.drawLine(col * cellWidth, 0, col * cellWidth, height,
linePaint);
}
}
// 画出格子被按下的背景
private void drawCellTouchBg(Canvas canvas) {
// 画出按下的格子的背景
if (touchXIndex >= 0 && touchYIndex > 0) {//第一行显示的星期,所以touchYIndex > 0
// 计算按下的点所在的格子的上下左右坐标
float left = touchXIndex * cellWidth;
float top = touchYIndex * cellHeight;
float right = left + cellWidth;
float bottom = top + cellHeight;
// 画出按下的格子的背景
canvas.drawRect(left, top, right, bottom, cellBgPaint);
}
}
// 画第一排显示的星期
private void drawWeekText(Canvas canvas) {
// 画出头部的星期
for (int i = 0; i < COLUMN_NUM; i++) {
String week = weekText[i];
textPaint.setTextSize(cellWidth * 0.4f);
textPaint.getTextBounds(week, 0, week.length(), textRect);
float textWidth = textRect.width();
float textHeight = textRect.height();
// 这里注意参数中的y坐标
canvas.drawText(week, (cellWidth - textWidth) / 2 + cellWidth * i,
(cellHeight + textHeight) / 2, textPaint);
}
}
// 画出日期
private void drawDays(Canvas canvas) {
// 设置时间为当前月的1号
calendar.set(Calendar.DAY_OF_MONTH, 1);
// 得到1号对应的星期(星期天对应1,星期一对应2...)
int weekIndex = calendar.get(Calendar.DAY_OF_WEEK);
if (weekIndex == 1) {// 1号是周日,则把1号绘制到第二行去
startIndex = 7;
} else {
startIndex = weekIndex - 1;
}
// 获取当月的天数
int days = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
// 把本月的天放入数组
for (int i = 1; i <= days; i++) {
daysArray[startIndex + i - 1] = i;
}
endIndex = startIndex + days - 1;
// 获取上月的最大天数
calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) - 1);
int preMaxDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
//恢复
calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + 1);
// 把上月的天数放入数组
for (int i = startIndex - 1; i >= 0; i--) {
daysArray[i] = preMaxDays--;
}
// 把下月的天数放入数组
int count = 1;
for (int i = endIndex + 1; i < 42; i++) {
daysArray[i] = count++;
}
datePaint.setTextSize(cellWidth * 0.4f);
preOrNextMonthPaint.setTextSize(cellWidth * 0.4f);
for (int i = 0; i < 42; i++) {
if(i < startIndex || i > endIndex){
drawDayText(canvas, daysArray[i] + "", i, preOrNextMonthPaint);
}else{
drawDayText(canvas, daysArray[i] + "", i, datePaint);
}
}
}
// 在坐标为(posX,posY)的格子中画日期
private void drawDayText(Canvas canvas, String text, int index, Paint paint) {
int posX = index / 7 + 1;
int posY = index % 7;
Rect rect = new Rect();
datePaint.getTextBounds(text, 0, text.length(), rect);
int textWidth = rect.width();
int textHeight = rect.height();
canvas.drawText(text, cellWidth * posY + (cellWidth - textWidth) / 2,
cellHeight * posX + (cellHeight + textHeight) / 2, paint);
}
/** 显示上一月 */
public void showPreMonth() {
calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) - 1);
invalidate();
}
/** 显示下一月 */
public void showNextMonth() {
calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + 1);
invalidate();
}
/**获取当前显示的日期*/
public String getShowDate(){
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1;
return year + "年" + month + "月";
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 画网格线
drawLines(canvas);
// 画头部的星期
drawWeekText(canvas);
// 画日期
drawDays(canvas);
// 画按下的格子的背景
drawCellTouchBg(canvas);
}
public boolean onTouch(View v, MotionEvent event) {
// 获取按下的点相对于MyView的坐标
float x = event.getX();
float y = event.getY();
// 根据坐标计算按下的格子
touchXIndex = (int) (x / cellWidth);
touchYIndex = (int) (y / cellHeight);
// 通知重绘
if (touchXIndex >= 0 && touchYIndex > 0) {
this.invalidate();
//计算选中的日期
int index = touchYIndex * 7 + touchXIndex - 7;
int day = daysArray[index];
Calendar c = Calendar.getInstance();
c.setTime(calendar.getTime());
c.set(Calendar.DAY_OF_MONTH, day);
if(index < startIndex){
//选则的是上个月
c.set(Calendar.MONTH, c.get(Calendar.MONTH) - 1);
}else if(index > endIndex){
//选则的是下个月
c.set(Calendar.MONTH, c.get(Calendar.MONTH) + 1);
}
if(listener != null){
listener.onCellSelected(c.getTime());
}
}
return false;
}
public void setOnCellSelectedListener(OnCellSelectedListener listener){
this.listener = listener;
}
public interface OnCellSelectedListener{
void onCellSelected(Date date);
}
}