日历控件的效果图如下:
下面是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); } }
下载源码