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

日历控件的效果图如下:

Android自定义控件之自定义日历控件_第1张图片

下面是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);
	}

}


下载源码


你可能感兴趣的:(android,日历控件,自定义日历控件)