Android系统自定义实现日历控件

清华大佬耗费三个月吐血整理的几百G的资源,免费分享!....>>>

注:此功能在在Activity中,由三大块组成:头(上月按扭,下月按扭,当前年月文本),日历块(星期区域,日期区域),描述区域

Activity:
/**
 * Android实现日历控件
 * 注:Calendar时间与现在实的时间在月份上需要+1,因为月份是0-11
 */
public class CalenderActivity extends Activity {

    private ArrayList<DateWidgetDayView> days = new ArrayList<DateWidgetDayView>();

        //颜色代码常量
    public static int Calendar_WeekBgColor = 0;
    public static int Calendar_DayBgColor = 0;
    public static int IsHoliday_BgColor = 0;
    public static int UnPresentMonth_FontColor = 0;
    public static int IsPresentMonth_FontColor = 0;
    public static int IsToday_BgColor = 0;
    public static int Special_Reminder = 0;
    public static int Common_Reminder = 0;
    public static int Calendar_WeekFontColor = 0;

    /**
     *  表格中的第一天,一般上月的某一天
     */
    public static Calendar mFirstDateOfPanel = Calendar.getInstance();
    private Calendar mTodayDate = Calendar.getInstance();// 初始日期,即当天
    private Calendar mSelectedDate = Calendar.getInstance();//选中的日期,如果未选中则为1970-1-1
    private Calendar mViewDate = Calendar.getInstance();

    // 当前操作日期
    private int firstDayOfWeek = Calendar.SUNDAY;// 是星期日
    private int currentMonth = 0;
    private int currentYear = 0;

    private int displayWidth = 0;// 屏幕总宽度
    private int cell_Width = 0; // 日期单元格宽度
    private int cell_Height = 35; // 日期单元格高度

    // 页面控件
    TextView currentYAndM = null;
    Button preMonthButton = null;
    Button nextMonthButton = null;
    LinearLayout mainLayout = null;
    LinearLayout calendarLayout = null;
    LinearLayout contentLayout = null;
    TextView contentText = null;

    // 数据源
    Boolean[] msgs = null;

    Calendar startDate = null;//表格的第一天的日期

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.calendar_main);
        this.viewModel = new BlogViewModel(this);

        // 获得屏幕宽和高,并計算出屏幕寬度分七等份的大小
        WindowManager windowManager = getWindowManager();
        Display display = windowManager.getDefaultDisplay();
        displayWidth = display.getWidth();
        cell_Width = displayWidth / 7 + 1;

        // 制定布局文件,并设置属性
        mainLayout = (LinearLayout) this.findViewById(R.id.date_calender_framelayout);
        currentYAndM = (TextView) findViewById(R.id.Top_Date);
        preMonthButton = (Button) findViewById(R.id.btn_pre_month);
        nextMonthButton = (Button) findViewById(R.id.btn_next_month);

        preMonthButton.setOnClickListener(new OnClickPreMonthListener());
        nextMonthButton.setOnClickListener(new OnClickNextMonthListener());

        // 计算本月日历中的第一天(一般是上月的某天),并更新日历
        mFirstDateOfPanel = getCalendarStartDate();
        this.mTodayDate = getTodayDate();
        this.startDate = getStartDate();

        /*
         *  初始化日期视图
         *  Calendar部分
         */
        View calendarView = generateCalendarView();
        this.mainLayout.addView(calendarView);

        //刷新日期视图
        this.refreshCalendar();

        /*
         * Description 部分
         */
        ScrollView view = new ScrollView(this);
        contentLayout = createLayout(LinearLayout.VERTICAL);
        contentLayout.setPadding(5, 2, 0, 0);

        contentText = new TextView(this);
        contentText.setTextColor(Color.BLACK);
        contentText.setTextSize(18);

        contentLayout.addView(contentText);

        LinearLayout.LayoutParams Param1 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT);
        view.addView(contentLayout, Param1);
        mainLayout.setBackgroundColor(Color.WHITE);
        mainLayout.addView(view);

        /* 新建线程
        new Thread() {
            @Override
            public void run() {
                int day = getIndexFromDates(mTodayDate, startDate);

                Log.i("sys", "初始时  day = "+day);
            }
        }.start();*/

        Calendar_WeekBgColor = this.getResources().getColor(R.color.Calendar_WeekBgColor);
        Calendar_DayBgColor = this.getResources().getColor(R.color.Calendar_DayBgColor);
        IsHoliday_BgColor = this.getResources().getColor(R.color.isHoliday_BgColor);
        UnPresentMonth_FontColor = this.getResources().getColor(R.color.unPresentMonth_FontColor);
        IsPresentMonth_FontColor = this.getResources().getColor(R.color.isPresentMonth_FontColor);
        IsToday_BgColor = this.getResources().getColor(R.color.isToday_BgColor);
        Special_Reminder = this.getResources().getColor(R.color.specialReminder);
        Common_Reminder = this.getResources().getColor(R.color.commonReminder);
        Calendar_WeekFontColor = this.getResources().getColor(R.color.Calendar_WeekFontColor);
    }

    protected String getDateShortString(Calendar date) {
        String returnString = date.get(Calendar.YEAR) + "-";
        returnString += date.get(Calendar.MONTH) + 1 + "-";
        returnString += date.get(Calendar.DAY_OF_MONTH);

        return returnString;
    }

    /**
     * Return the Date's index of {@link returnDate} from {@link datesList};
     * First is Today's index
     * 
     * @param now
     *            today
     * @param returnDate
     *            click date
     * @return
     */
    private int getIndexFromDates(Calendar now, Calendar returnDate) {
        Calendar cNow = (Calendar) now.clone();
        Calendar cReturnDate = (Calendar) returnDate.clone();
        CalenderUtil.setTimeToMidnight(cNow);
        CalenderUtil.setTimeToMidnight(cReturnDate);

        long todayMs = cNow.getTimeInMillis();
        long returnMs = cReturnDate.getTimeInMillis();
        long intervalMs = todayMs - returnMs;
        int index = CalenderUtil.millisecondsToDays(intervalMs);
        Log.i("sys", "Index = " + index);
        return index;
    }

    /**
     * 生成日期视图 即初始化calendarLayout
     * 
     * @return
     */
    private View generateCalendarView() {
        calendarLayout = createLayout(LinearLayout.VERTICAL);
        // layContent.setPadding(1, 0, 1, 0);
        calendarLayout.setBackgroundColor(Color.argb(255, 105, 105, 103));
        calendarLayout.addView(generateCalendarWeekRows());
        days.clear();

        for (int iRow = 0; iRow < 6; iRow++) {
            calendarLayout.addView(generateCalendarDayRows());
        }

        return calendarLayout;
    }

    /**
     * 生成星期View
     * 
     * @return View
     */
    private View generateCalendarWeekRows() {
        LinearLayout weekLayoutRow = createLayout(LinearLayout.HORIZONTAL);
        weekLayoutRow.setBackgroundColor(Color.argb(255, 207, 207, 205));

        for (int iDay = 0; iDay < 7; iDay++) {
            DateWidgetWeekView dayView = new DateWidgetWeekView(this, cell_Width, cell_Height);

            final int iWeekDay = CalenderUtil.getWeekDay(iDay, firstDayOfWeek);
            dayView.setData(iWeekDay);
            weekLayoutRow.addView(dayView);
        }

        return weekLayoutRow;
    }

    /**
     * 生成日期行View
     * 
     * @return View
     */
    private View generateCalendarDayRows() {
        LinearLayout layRow = createLayout(LinearLayout.HORIZONTAL);
        //TODO  日期数据消息给添加属性

        for (int iDay = 0; iDay < 7; iDay++) {
            DateWidgetDayView dateDayView = new DateWidgetDayView(this, cell_Width, cell_Width);
            dateDayView.setItemClick(mOnDayCellClick);
            days.add(dateDayView);
            layRow.addView(dateDayView);
        }

        return layRow;
    }

    /**
     * 由于本日历上的日期都是从周一开始的,此方法可推算出上月在本月日历中显示的天数 计算出本月第一行1号前的空格数
     */
    private void updateStartDateForPanel() {
        currentMonth = mFirstDateOfPanel.get(Calendar.MONTH);
        currentYear = mFirstDateOfPanel.get(Calendar.YEAR);
        mFirstDateOfPanel.set(Calendar.DAY_OF_MONTH, 1);
        mFirstDateOfPanel.set(Calendar.HOUR_OF_DAY, 0);
        mFirstDateOfPanel.set(Calendar.MINUTE, 0);
        mFirstDateOfPanel.set(Calendar.SECOND, 0);
        // 显示当前是的年月在Header
        updateCurrentMonthDisplay();
        int iDay = 0;// 前面的空格数
        int iStartDay = firstDayOfWeek;// 当天的星期角标

        if (iStartDay == Calendar.MONDAY) {
            iDay = mFirstDateOfPanel.get(Calendar.DAY_OF_WEEK) - Calendar.MONDAY;
            if (iDay < 0)
                iDay = 6;
        }

        if (iStartDay == Calendar.SUNDAY) {
            iDay = mFirstDateOfPanel.get(Calendar.DAY_OF_WEEK) - Calendar.SUNDAY;
            if (iDay < 0)
                iDay = 6;
        }

        mFirstDateOfPanel.add(Calendar.DAY_OF_WEEK, -iDay);// 得出-2,即前面从上月30号开始
    }

    /**
     *  更新日历数据并设置日期
     *  给days中的DateWidgetDayView元素添加Text
     */
    private void refreshCalendar() {
        DateWidgetDayView dayView = null;

        boolean isSelected = false;
        final boolean bIsSelection = (mSelectedDate.getTimeInMillis() != 0);
        final int iSelectedYear = mSelectedDate.get(Calendar.YEAR);
        final int iSelectedMonth = mSelectedDate.get(Calendar.MONTH);
        final int iSelectedDay = mSelectedDate.get(Calendar.DAY_OF_MONTH);

        //取得表格中的第一天(一般为上月)
        mViewDate.setTimeInMillis(mFirstDateOfPanel.getTimeInMillis());

        for (int i = 0; i < days.size(); i++) {
            final int iYear = mViewDate.get(Calendar.YEAR);
            final int iMonth = mViewDate.get(Calendar.MONTH);
            final int iDay = mViewDate.get(Calendar.DAY_OF_MONTH);
            final int iDayOfWeek = mViewDate.get(Calendar.DAY_OF_WEEK);

            dayView = days.get(i);

            // Check isToday
            boolean isToday = false;

            if (mTodayDate.get(Calendar.YEAR) == iYear && mTodayDate.get(Calendar.MONTH) == iMonth && mTodayDate.get(Calendar.DAY_OF_MONTH) == iDay) {
                isToday = true;
            }

            // Check isHoliday
            boolean isHoliday = false;
            if ((iDayOfWeek == Calendar.SATURDAY) || (iDayOfWeek == Calendar.SUNDAY))
                isHoliday = true;
            /*if ((iMonth == Calendar.JANUARY) && (iDay == 1))
                isHoliday = true;*///在格里高利历和罗马儒略历中一年中的第一个月是 JANUARY,它为 0;最后一个月取决于一年中的月份数。

            // Check isSelected
            isSelected = false;

            if (bIsSelection)
                if ((iSelectedDay == iDay) && (iSelectedMonth == iMonth) && (iSelectedYear == iYear)) {
                    isSelected = true;
                }

            dayView.setSelected(isSelected);

            // Check hasMSG
            boolean hasMSG = false;

            if (msgs != null && msgs[i] == true ){
                //TODO
            }

            if (isSelected){
                dayView.setFocusable(true);
            }

            dayView.setData(iYear, iMonth, iDay, isToday, isHoliday, currentMonth, hasMSG);

            mViewDate.add(Calendar.DAY_OF_MONTH, 1);
            Log.i("sys", "mViewDate : "+iYear+"-"+iMonth+"-"+iDay);
            Log.i("sys", "mFirstDateOfPanel : "+mFirstDateOfPanel.get(Calendar.YEAR)+"-"+mFirstDateOfPanel.get(Calendar.MONTH)+"-"+mFirstDateOfPanel.get(Calendar.DAY_OF_MONTH));
        }
        Log.i("sys", "mSelectedDate : "+iSelectedYear+"-"+iSelectedMonth+"-"+iSelectedDay);
        Log.i("sys", "startDate : "+startDate.get(Calendar.YEAR)+"-"+startDate.get(Calendar.MONTH)+"-"+startDate.get(Calendar.DAY_OF_MONTH));

        calendarLayout.invalidate();
    }

    /**
     *  设置当天日期和第并计算出前面第一个星期天的日期{@link mFirstDateOfPanel}
     * @return
     */
    private Calendar getCalendarStartDate() {
        mTodayDate.setTimeInMillis(System.currentTimeMillis());
        mTodayDate.setFirstDayOfWeek(firstDayOfWeek);

        // 如果没有选中日期,则设置当前日期为 ?
        if (mSelectedDate.getTimeInMillis() == 0) {
            mFirstDateOfPanel.setTimeInMillis(System.currentTimeMillis());
            mFirstDateOfPanel.setFirstDayOfWeek(firstDayOfWeek);
        } else {
            mFirstDateOfPanel.setTimeInMillis(mSelectedDate.getTimeInMillis());
            mFirstDateOfPanel.setFirstDayOfWeek(firstDayOfWeek);
        }

        updateStartDateForPanel();
        return mFirstDateOfPanel;
    }

    /**
     * 得到当前日历表中的第一天
     * 
     * @return Calendar
     */
    public Calendar getStartDate() {
        int iDay = 0;
        Calendar cal_Now = Calendar.getInstance();
        cal_Now.set(Calendar.DAY_OF_MONTH, 1);
        cal_Now.set(Calendar.HOUR_OF_DAY, 0);
        cal_Now.set(Calendar.MINUTE, 0);
        cal_Now.set(Calendar.SECOND, 0);
        cal_Now.setFirstDayOfWeek(Calendar.SUNDAY);

        iDay = cal_Now.get(Calendar.DAY_OF_WEEK) - Calendar.SUNDAY;

        if (iDay < 0) {
            iDay = 6;
        }

        cal_Now.add(Calendar.DAY_OF_WEEK, -iDay);

        return cal_Now;
    }

    public Calendar getTodayDate() {
        Calendar cal_Today = Calendar.getInstance();
        cal_Today.set(Calendar.HOUR_OF_DAY, 0);
        cal_Today.set(Calendar.MINUTE, 0);
        cal_Today.set(Calendar.SECOND, 0);
        cal_Today.setFirstDayOfWeek(Calendar.MONDAY);

        return cal_Today;
    }

    /**
     * 更新日历标题上显示的年月
     */
    private void updateCurrentMonthDisplay() {
        String date = mFirstDateOfPanel.get(Calendar.YEAR) + "年" + (mFirstDateOfPanel.get(Calendar.MONTH) + 1) + "月";
        currentYAndM.setText(date);
    }

    /**
     *  点击上月按钮,触发事件
     * @author Win7
     */

    // 生成布局LinearLayout
    private LinearLayout createLayout(int iOrientation) {
        LinearLayout lay = new LinearLayout(this);
        lay.setLayoutParams(new LayoutParams(android.view.ViewGroup.LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT));
        lay.setOrientation(iOrientation);

        return lay;
    }

    class OnClickPreMonthListener implements OnClickListener {
        @Override
        public void onClick(View v) {
            contentText.setText("aaaa");
            mSelectedDate.setTimeInMillis(0);
            currentMonth--;

            if (currentMonth == -1) {
                currentMonth = 11;
                currentYear--;
            }

            mFirstDateOfPanel.set(Calendar.DAY_OF_MONTH, 1);
            mFirstDateOfPanel.set(Calendar.MONTH, currentMonth);
            mFirstDateOfPanel.set(Calendar.YEAR, currentYear);
            mFirstDateOfPanel.set(Calendar.HOUR_OF_DAY, 0);
            mFirstDateOfPanel.set(Calendar.MINUTE, 0);
            mFirstDateOfPanel.set(Calendar.SECOND, 0);
            mFirstDateOfPanel.set(Calendar.MILLISECOND, 0);
            updateStartDateForPanel();

            startDate = (Calendar) mFirstDateOfPanel.clone();

            // 新建线程
            new Thread() {
                @Override
                public void run() {
                    int day = getIndexFromDates(mTodayDate, startDate);
                                        //day是算出当前显示的月份面版第一天与当天的天数
                    Log.i("sys", "点击上月时  day = "+day);
                }
            }.start();

            refreshCalendar();
        }

    }

    /**
     *  点击下月按钮,触发事件
     * @author Win7
     */
    class OnClickNextMonthListener implements OnClickListener {
        @Override
        public void onClick(View v) {
            contentText.setText("");
            mSelectedDate.setTimeInMillis(0);
            currentMonth++;

            if (currentMonth == 12) {
                currentMonth = 0;
                currentYear++;
            }

            mFirstDateOfPanel.set(Calendar.DAY_OF_MONTH, 1);
            mFirstDateOfPanel.set(Calendar.MONTH, currentMonth);
            mFirstDateOfPanel.set(Calendar.YEAR, currentYear);
            updateStartDateForPanel();

            startDate = (Calendar) mFirstDateOfPanel.clone();

            // 新建线程
            new Thread() {
                @Override
                public void run() {
                    int day = 5;
                    Log.i("sys", "点击下月时  day = "+day);
                }
            }.start();

            refreshCalendar();
        }
    }

    // 点击日历,触发事件
    private DateWidgetDayView.OnDateItemClickListener mOnDayCellClick = new DateWidgetDayView.OnDateItemClickListener() {
        public void OnClick(DateWidgetDayView item) {
            mSelectedDate.setTimeInMillis(item.getDate().getTimeInMillis());

            int day = getIndexFromDates(mSelectedDate, startDate);

            contentText.setText(getDateShortString(mSelectedDate));
            contentText.setText("无数据");

            Log.i("sys", "mFirstDateOfPanel=" + mFirstDateOfPanel.get(Calendar.DATE) + " calCalendar=" + mViewDate.get(Calendar.DATE) + " mTodayDate=" + mTodayDate.get(Calendar.DATE)
                    + " mSelectedDate=" + mSelectedDate.get(Calendar.DATE) +" day = "+day);
            item.setSelected(true);
            refreshCalendar();
        }
    };

    @Deprecated
    public Calendar getEndDate(Calendar startDate) {
        // Calendar end = GetStartDate(enddate);
        Calendar endDate = Calendar.getInstance();
        endDate = (Calendar) startDate.clone();
        endDate.add(Calendar.DAY_OF_MONTH, 41);
        return endDate;
    }
}

星期View:

public class DateWidgetWeekView extends View {
    // 字体大小
    private final static int fTextSize = 22;
    private Paint pt = new Paint();
    private RectF rect = new RectF();
    private int iWeekDay = -1;

    public DateWidgetWeekView(Context context, int iWidth, int iHeight) {
        super(context);
        setLayoutParams(new LayoutParams(iWidth, iHeight));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 设置矩形大小
        rect.set(0, 0, this.getWidth(), this.getHeight());
        rect.inset(1, 1);

        // 绘制日历头部
        drawDayHeader(canvas);
    }

    private void drawDayHeader(Canvas canvas) {
        // 画矩形,并设置矩形画笔的颜色
        pt.setColor(CalenderActivity.Calendar_WeekBgColor);
        canvas.drawRect(rect, pt);

        // 写入日历头部,设置画笔参数
        pt.setTypeface(null);
        pt.setTextSize(fTextSize);
        pt.setAntiAlias(true);
        pt.setFakeBoldText(true);
        pt.setColor(CalenderActivity.Calendar_WeekFontColor);

        // draw day name
        final String sDayName = CalenderUtil.getWeekDayName(iWeekDay);
        final int iPosX = (int) rect.left + ((int) rect.width() >> 1)
                - ((int) pt.measureText(sDayName) >> 1);
        final int iPosY = (int) (this.getHeight()
                - (this.getHeight() - getTextHeight()) / 2 - pt
                .getFontMetrics().bottom);
        canvas.drawText(sDayName, iPosX, iPosY, pt);
    }

    // 得到字体高度
    private int getTextHeight() {
        return (int) (-pt.ascent() + pt.descent());
    }

    // 得到一星期的第几天的文本标记
    public void setData(int iWeekDay) {
        this.iWeekDay = iWeekDay;
    }
}

日期View:

/**
 * 日历控件单元格绘制类
 * @Description: 日历控件单元格绘制类

 * @FileName: DateWidgetDayView.java 
 */
public class DateWidgetDayView extends View {
    // 字体大小
    private static final int fTextSize = 28;

    // 基本元素
    private OnDateItemClickListener itemClick = null;
    private Paint mPaint = new Paint();
    private RectF rect = new RectF();
    private String sDate = "";

    // 当前日期
    private int iDateYear = 0;
    private int iDateMonth = 0;
    private int iDateDay = 0;

    // 布尔变量
    private boolean hasSelected = false;
    private boolean isActiveMonth = false;
    private boolean isToday = false;
    private boolean isTouchedDown = false;
    private boolean isHoliday = false;
    private boolean hasMSG = false;

    public static int ANIM_ALPHA_DURATION = 100;

    public interface OnDateItemClickListener {
        public void OnClick(DateWidgetDayView item);
    }

    // 构造函数
    public DateWidgetDayView(Context context, int iWidth, int iHeight) {
        super(context);
        setFocusable(true);
        setLayoutParams(new LayoutParams(iWidth, iHeight));
    }

    // 取变量值
    public Calendar getDate() {
        Calendar calDate = Calendar.getInstance();
        calDate.clear();
        calDate.set(Calendar.YEAR, iDateYear);
        calDate.set(Calendar.MONTH, iDateMonth);
        calDate.set(Calendar.DAY_OF_MONTH, iDateDay);
        return calDate;
    }

    // 是否有消息
    public boolean hasMSG() {
        return this.hasMSG;
    }

    // 是否假期
    public boolean isHoliday() {
        return this.isHoliday;
    }

    // 设置变量值
    public void setData(int iYear, int iMonth, int iDay, Boolean bToday,
            Boolean bHoliday, int iActiveMonth, boolean hasRecord) {
        iDateYear = iYear;
        iDateMonth = iMonth;
        iDateDay = iDay;

        this.sDate = Integer.toString(iDateDay);
        this.isActiveMonth = (iDateMonth == iActiveMonth);
        this.isToday = bToday;
        this.isHoliday = bHoliday;
        this.hasMSG = hasRecord;
    }

    // 重载绘制方法
    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);

        rect.set(0, 0, this.getWidth(), this.getHeight());
        rect.inset(1, 1);

        final boolean bFocused = IsViewFocused();

        drawDayView(canvas, bFocused);
        drawDayNumber(canvas);
    }

    public boolean IsViewFocused() {
        return (this.isFocused() || isTouchedDown);
    }

    // 绘制日历方格
    private void drawDayView(Canvas canvas, boolean bFocused) {

        if (hasSelected || bFocused) {
            LinearGradient lGradBkg = null;

            if (bFocused) {
                lGradBkg = new LinearGradient(rect.left, 0, rect.right, 0,
                        0xffaa5500, 0xffffddbb, Shader.TileMode.CLAMP);
            }

            if (hasSelected) {
                lGradBkg = new LinearGradient(rect.left, 0, rect.right, 0,
                        0xff225599, 0xffbbddff, Shader.TileMode.CLAMP);
            }

            if (lGradBkg != null) {
                mPaint.setShader(lGradBkg);
                canvas.drawRect(rect, mPaint);
            }

            mPaint.setShader(null);

        } else {
            mPaint.setColor(getColorBkg(isHoliday, isToday));
            canvas.drawRect(rect, mPaint);
        }

        if(isHoliday){
            mPaint.setColor(CalenderActivity.IsHoliday_BgColor);
            canvas.drawRect(rect, mPaint);
        }

        if (hasMSG) {
            markHasMSGReminder(canvas, CalenderActivity.Special_Reminder);
        }
        // else if (!hasRecord && !bToday && !bSelected) {
        // CreateReminder(canvas, Calendar_TestActivity.Calendar_DayBgColor);
        // }
    }

    // 绘制日历中的数字
    public void drawDayNumber(Canvas canvas) {
        // draw day number
        mPaint.setTypeface(null);
        mPaint.setAntiAlias(true);
        mPaint.setShader(null);
        mPaint.setFakeBoldText(true);
        mPaint.setTextSize(fTextSize);
        mPaint.setColor(CalenderActivity.IsPresentMonth_FontColor);
        mPaint.setUnderlineText(false);

        if (!isActiveMonth)
            mPaint.setColor(CalenderActivity.UnPresentMonth_FontColor);

        if (isToday)
            mPaint.setUnderlineText(true);

        final int iPosX = (int) rect.left + ((int) rect.width() >> 1) - ((int) mPaint.measureText(sDate) >> 1);

        final int iPosY = (int) (this.getHeight() - (this.getHeight() - getTextHeight()) / 2 - mPaint .getFontMetrics().bottom);

        canvas.drawText(sDate, iPosX, iPosY, mPaint);

        mPaint.setUnderlineText(false);
    }

    // 得到字体高度
    private int getTextHeight() {
        return (int) (-mPaint.ascent() + mPaint.descent());
    }

    // 根据条件返回不同颜色值
    public static int getColorBkg(boolean bHoliday, boolean bToday) {
        if (bToday)
            return CalenderActivity.IsToday_BgColor;
        // if (bHoliday) //如需周末有特殊背景色,可去掉注释
        // return Calendar_TestActivity.isHoliday_BgColor;
        return CalenderActivity.Calendar_DayBgColor;
    }

    // 设置是否被选中
    @Override
    public void setSelected(boolean bEnable) {
        if (this.hasSelected != bEnable) {
            this.hasSelected = bEnable;
            this.invalidate();
        }
    }

    public void setItemClick(OnDateItemClickListener itemClick) {
        this.itemClick = itemClick;
    }

    public void doItemClick() {
        if (itemClick != null)
            itemClick.OnClick(this);
    }

    // 点击事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean bHandled = false;
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            bHandled = true;
            isTouchedDown = true;
            invalidate();
            startAlphaAnimIn(DateWidgetDayView.this);
        }
        if (event.getAction() == MotionEvent.ACTION_CANCEL) {
            bHandled = true;
            isTouchedDown = false;
            invalidate();
        }
        if (event.getAction() == MotionEvent.ACTION_UP) {
            bHandled = true;
            isTouchedDown = false;
            invalidate();
            doItemClick();
        }
        return bHandled;
    }

    // 点击事件
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        boolean bResult = super.onKeyDown(keyCode, event);
        if ((keyCode == KeyEvent.KEYCODE_DPAD_CENTER)
                || (keyCode == KeyEvent.KEYCODE_ENTER)) {
            doItemClick();
        }
        return bResult;
    }

    // 不透明度渐变
    public static void startAlphaAnimIn(View view) {
        AlphaAnimation anim = new AlphaAnimation(0.5F, 1);
        anim.setDuration(ANIM_ALPHA_DURATION);
        anim.startNow();
        view.startAnimation(anim);
    }

    //右上角画倒三角
    public void markHasMSGReminder(Canvas canvas, int Color) {
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setColor(Color);
        Path path = new Path();
        path.moveTo(rect.right - rect.width() / 4, rect.top);
        path.lineTo(rect.right, rect.top);
        path.lineTo(rect.right, rect.top + rect.width() / 4);
        path.lineTo(rect.right - rect.width() / 4, rect.top);
        path.close();
        canvas.drawPath(path, mPaint);
    }
}

工具类:

public class CalenderUtil {
    private final static String[] vecStrWeekDayNames = getWeekDayNames();

    private static String[] getWeekDayNames() {
        String[] vec = new String[10];

        vec[Calendar.SUNDAY] = "星期日";
        vec[Calendar.MONDAY] = "星期一";
        vec[Calendar.TUESDAY] = "星期二";
        vec[Calendar.WEDNESDAY] = "星期三";
        vec[Calendar.THURSDAY] = "星期四";
        vec[Calendar.FRIDAY] = "星期五";
        vec[Calendar.SATURDAY] = "星期六";

        return vec;
    }

    public static String getWeekDayName(int iDay) {
        return vecStrWeekDayNames[iDay];
    }

    public static int getWeekDay(int index, int iFirstDayOfWeek) {
        int iWeekDay = -1;

        if (iFirstDayOfWeek == Calendar.MONDAY) {
            iWeekDay = index + Calendar.MONDAY;

            if (iWeekDay > Calendar.SATURDAY)
                iWeekDay = Calendar.SUNDAY;
        }

        if (iFirstDayOfWeek == Calendar.SUNDAY) {
            iWeekDay = index + Calendar.SUNDAY;
        }

        return iWeekDay;
    }

    /**
     * Calculate the days with milliseconds
     * @param intervalMs
     * @return
     */
    public static int millisecondsToDays(long intervalMs) {
        return Math.round((intervalMs / (1000 * 86400)));
    }

    /**
     * Return the milliseconds from 1970 to just
     * @param calendar
     */
    public static void setTimeToMidnight(Calendar calendar) {
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
    }
}

calendar_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical" >

    <RelativeLayout
        android:id="@+id/relativeLayout1"
        android:layout_width="fill_parent"
        android:layout_height="60dip"
        android:background="#EDE8DD" >

        <TextView
            android:id="@+id/Top_Date"
            android:layout_width="150dip"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center_horizontal|center"
            android:textColor="#424139"
            android:textSize="19sp"
            android:textStyle="bold" />

        <Button
            android:id="@+id/btn_pre_month"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:layout_marginLeft="30dp"
            android:background="@drawable/previous_month"
            android:text="" />

        <Button
            android:id="@+id/btn_next_month"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="30dp"
            android:background="@drawable/next_month"
            android:text="" />
    </RelativeLayout>

    <LinearLayout 
        android:id="@+id/date_calender_framelayout"
        android:layout_height="fill_parent"
        android:layout_width="fill_parent"
        ></LinearLayout>

</LinearLayout>