Android自定义日历控件(附效果图)

参考了一些网上的资料,针对自己需求做了一些修改。没有难点,只是细节特别多。

package xxx.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

/**
 * 自定义日历控件
 * Created with Android Studio
 * Author:
 * Date:2017/12/21
 */
public class MyCalendar extends View {

    private static final int SINGLE_SELECT_TYPE = 1;//单选模式

    private static final int DOUBLE_SELECT_TYPE = 2;//多选模式

    public static final String FORMAT_DATE_DIVIDE = "/";

    private static final int RANGE_START = 1;

    private static final int RANGE_END = 2;

    private static final int RANGE_INSIDE = 3;

    private static final int RANGE_OUTSIDE = 4;

    private int mTitleTextColor;//标题的颜色

    private int mSubTitleTextColor;//小标题的颜色

    private float mTitleTextSize;//标题的字体大小

    private float mSubTitleTextSize;//小标题的字体大小

    private int mTitleArrowLeftId, mTitleArrowRightId;

    private float mMonthHorizontalSpace;

    private float mTitleVerticalSpac;

    private float mSubTitleVerticalSpac;

    private int mTextColorWeek;//星期的颜色

    private float mTextSizeWeek;//星期的字体大小

    private int mPastDayTxtColor;//过去的日期文本的颜色

    private int mDayTextColor;//日期文本的颜色

    private float mTextSizeDay;//日期文本大小

    private int mSelectTextColor;//选中的文本的颜色

    private int mSelectBgColor;//选中背景

    private int mSelectRangeBgColor;//选中区间背景色

    private float mSelectRadius;//日期圆形背景的半径

    private float mLineSpac;//行间距

    private Paint mPaint = new Paint();//文字画笔

    private Paint mBgPaint = new Paint();//背景画笔

    private Paint mArrowPaint = new Paint();//画箭头画笔

    private float mTitleHeight;//顶部标题高度

    private float mWeekHeight;//星期高度

    private float mSubTitleHeight;//年月小标题高度

    private float mDayHeight;//日期高度

    private float oneHeight;//一行日期的高度,包含间距

    private int mColumnWidth;//每列宽度

    private Date mUpdateMonthDate; //当前的月份

    private boolean isCurrentMonth;//展示的月份是否是当前月

    private int mCurrentDay;//当前日

    private int mUpdateYear;//当前日历所在年份

    private int mUpdateMonth;//当前日历所在月份

    private int mLastSelectDay;//上一次选中的日期(避免造成重复回调请求)

    private int mSelectDay;//选中的日期

    private int mDayOfMonth;//月份天数

    private int mFirstDayIdx;//当月第一天位置索引,第一天是不同星期,起点也就不同

    private int mFirstLineNum, mLastLineNum; //第一行、最后一行能展示多少日期

    private int mLineNum;//日期行数

    private int mArrowLeftStart;//左箭头响应触摸的起始位置

    private int mArrowRightStart;//右箭头响应触摸的起始位置

    private int mArrowWidth;//箭头图标宽度

    private String[] mWeekArray = new String[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

    private long mStartMills;//选中区域的起始点

    private long mEndMills;//选中区域的结束点

    private IClickListener mListener;

    private boolean showTitle;//是否显示标题

    private boolean showWeek;//是否显示星期

    private boolean showSubTitle;//是否显示年月小标题

    private float mPadTop, mPadBottom;

    private GestureDetector mGestureDetector;

    private int mType = SINGLE_SELECT_TYPE;

    public MyCalendar(Context context) {
        this(context, null);
    }

    public MyCalendar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyCalendar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context
                .obtainStyledAttributes(attrs, R.styleable.MyCalendar, defStyleAttr, 0);
        mTitleArrowLeftId = a.getResourceId(R.styleable.MyCalendar_arrowLeftResId, 0);
        showTitle = a.getBoolean(R.styleable.MyCalendar_showTitle, true);
        showWeek = a.getBoolean(R.styleable.MyCalendar_showWeek, true);
        showSubTitle = a.getBoolean(R.styleable.MyCalendar_showSubTitle, false);
        mTitleArrowRightId = a.getResourceId(R.styleable.MyCalendar_arrowRightResId, 0);
        mMonthHorizontalSpace = a.getDimension(R.styleable.MyCalendar_titleHozSpace, 20);
        mTitleVerticalSpac = a.getDimension(R.styleable.MyCalendar_titleVerticalSpace, 20);
        mSubTitleVerticalSpac = a
                .getDimension(R.styleable.MyCalendar_subTitleVerticalSpace, 20);
        mTitleTextColor = a.getColor(R.styleable.MyCalendar_titleTextColor, Color.BLACK);
        mSubTitleTextColor = a.getColor(R.styleable.MyCalendar_subTitleTextColor, Color.BLACK);
        mTitleTextSize = a.getDimension(R.styleable.MyCalendar_titleTextSize, 100);
        mPadTop = a.getDimension(R.styleable.MyCalendar_padTop, 10);
        mPadBottom = a.getDimension(R.styleable.MyCalendar_padBottom, 10);
        mSubTitleTextSize = a.getDimension(R.styleable.MyCalendar_subTitleTextSize, 100);
        mTextColorWeek = a.getColor(R.styleable.MyCalendar_weekTextColor, Color.BLACK);
        mTextSizeWeek = a.getDimension(R.styleable.MyCalendar_weekTextSize, 70);
        mDayTextColor = a.getColor(R.styleable.MyCalendar_dayTextColor, Color.GRAY);
        mPastDayTxtColor = a.getColor(R.styleable.MyCalendar_pastDayTextColor, Color.GRAY);
        mTextSizeDay = a.getDimension(R.styleable.MyCalendar_dayTextSize, 70);
        mSelectTextColor = a.getColor(R.styleable.MyCalendar_selectTextColor, Color.YELLOW);
        mSelectBgColor = a.getColor(R.styleable.MyCalendar_selectBgColor, Color.YELLOW);
        mSelectRangeBgColor = a.getColor(R.styleable.MyCalendar_selectRangeBgColor, Color.YELLOW);
        mSelectRadius = a.getDimension(R.styleable.MyCalendar_selectRadius, 20);
        mLineSpac = a.getDimension(R.styleable.MyCalendar_daylineSpace, 20);
        a.recycle();
        init();
    }

    public void setType(int type) {
        mType = type;
    }

    /** 计算相关常量,构造方法中调用 */
    private void init() {
        mGestureDetector = new GestureDetector(getContext(), new GestureCallback());
        mPaint.setAntiAlias(true); //抗锯齿
        mBgPaint.setAntiAlias(true); //抗锯齿
        mPaint.setTextSize(mTitleTextSize);
        mTitleHeight = getFontHeight(mPaint) + 2 * mTitleVerticalSpac;//标题高度
        mPaint.setTextSize(mTextSizeWeek);
        mWeekHeight = getFontHeight(mPaint);//星期高度
        mPaint.setTextSize(mSubTitleTextSize);
        mSubTitleHeight = getFontHeight(mPaint) + 2 * mSubTitleVerticalSpac;//小标题高度
        mPaint.setTextSize(mTextSizeDay);
        mDayHeight = getFontHeight(mPaint);//单行日期高度
        oneHeight = mLineSpac + mDayHeight;/*每行高度 = 行间距 + 日期字体高度*/
        updateMonth(new Date());//默认当前月份
    }

    private void computeRowsAndColumns(Calendar calendar) {
        mUpdateYear = calendar.get(Calendar.YEAR);
        mUpdateMonth = calendar.get(Calendar.MONTH) + 1;
        mDayOfMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);/*该月天数*/
        mFirstDayIdx = calendar.get(Calendar.DAY_OF_WEEK) - 1;/*第一行1号显示在什么位置(星期几)*/
        mLineNum = 1;
        mFirstLineNum = 7 - mFirstDayIdx;/*第一行能展示的天数*/
        mLastLineNum = 0;/*最后一行能展示的天数*/
        int remainDays = mDayOfMonth - mFirstLineNum;
        while (remainDays > 7) {
            mLineNum++;
            remainDays -= 7;
        }
        if (remainDays > 0) {
            mLineNum++;
            mLastLineNum = remainDays;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);//获取宽的尺寸,宽度 = 填充父窗体
        mColumnWidth = widthSize / 7;
        int height = (int) (mLineNum * oneHeight + mPadTop + mPadBottom);
        height = (int) computeY(height);
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (showTitle) {
            drawTitle(canvas);
        }
        if (showWeek) {
            drawWeek(canvas);
        }
        if (showSubTitle) {
            drawSubTitle(canvas);
        }
        drawDay(canvas);
    }

    /** 画年月小标题 */
    private void drawSubTitle(Canvas canvas) {
        mPaint.setTextSize(mSubTitleTextSize);
        mPaint.setColor(mSubTitleTextColor);
        float titleStart = getWidth() / 14;
        float y = mSubTitleVerticalSpac + getFontLeading(mPaint);
        if (showTitle) {
            y += mTitleHeight;
        }
        if (showWeek) {
            y += mWeekHeight;
        }
        String subTitle = getMonthYear(mUpdateMonthDate);
        canvas.drawText(subTitle, titleStart, y, mPaint);//画年月标题
    }

    /** 绘制年月、左右箭头 */
    private void drawTitle(Canvas canvas) {
        mPaint.setTextSize(mTitleTextSize);
        mPaint.setColor(mTitleTextColor);
        float textLen = getFontlength(mPaint, getMonthStr(mUpdateMonthDate));
        float titleStart = (getWidth() - textLen) / 2;
        float y = mPadTop + mTitleVerticalSpac + getFontLeading(mPaint);
        canvas.drawText(getMonthStr(mUpdateMonthDate), titleStart, y, mPaint);//画年月标题
        if (mTitleArrowLeftId != 0) {//画左箭头
            Bitmap bitmap = BitmapFactory.decodeResource(getResources(), mTitleArrowLeftId);
            int arrowHeight = bitmap.getHeight();
            mArrowWidth = bitmap.getWidth();
            mArrowLeftStart = (int) (titleStart - 2 * mMonthHorizontalSpace - mArrowWidth);
            float left = mArrowLeftStart + mMonthHorizontalSpace;
            float top = mPadTop + (mTitleHeight - arrowHeight) / 2;
            canvas.drawBitmap(bitmap, left, top, mArrowPaint);
        }
        if (mTitleArrowRightId != 0) {//画右箭头
            Bitmap bitmap = BitmapFactory.decodeResource(getResources(), mTitleArrowRightId);
            int arrowHeight = bitmap.getHeight();
            mArrowRightStart = (int) (titleStart + textLen);
            float left = mArrowRightStart + mMonthHorizontalSpace;
            float top = mPadTop + (mTitleHeight - arrowHeight) / 2;
            canvas.drawBitmap(bitmap, left, top, mArrowPaint);
        }
    }

    /** 绘制星期 */
    private void drawWeek(Canvas canvas) {
        mPaint.setTextSize(mTextSizeWeek);
        mPaint.setColor(mTextColorWeek);
        float y = mPadTop + getFontLeading(mPaint);
        if (showTitle) {
            y += mTitleHeight;
        }
        for (int i = 0; i < mWeekArray.length; i++) {
            int len = (int) getFontlength(mPaint, mWeekArray[i]);
            float x = i * mColumnWidth + (mColumnWidth - len) / 2;
            canvas.drawText(mWeekArray[i], x, y, mPaint);
        }
    }

    /** 绘制日期 */
    private void drawDay(Canvas canvas) {
        float lineTop = computeY(mPadTop);//某行开始绘制的Y坐标,第一行开始的y坐标为标题高度+星期部分高度
        for (int line = 0; line < mLineNum; line++) {
            if (line == 0) {//第一行
                drawDay(canvas, lineTop, mFirstLineNum, 0, mFirstDayIdx);
            } else if (line == mLineNum - 1) {//最后一行
                lineTop += oneHeight;
                int overDay = mFirstLineNum + (line - 1) * 7;
                drawDay(canvas, lineTop, mLastLineNum, overDay, 0);
            } else {//满行
                lineTop += oneHeight;
                int overDay = mFirstLineNum + (line - 1) * 7;
                drawDay(canvas, lineTop, 7, overDay, 0);
            }
        }
    }

    private float computeY(float y) {
        if (showTitle) {
            y += mTitleHeight;
        }
        if (showWeek) {
            y += mWeekHeight;
        }
        if (showSubTitle) {
            y += mSubTitleHeight;
        }
        return y;
    }

    /**
     * 绘制某一行的日期
     *
     * @param top        顶部坐标
     * @param count      此行需要绘制的日期数量
     * @param overDay    已经绘制过的日期,从overDay+1开始绘制
     * @param startIndex 此行第一个日期的星期索引
     */
    private void drawDay(Canvas canvas, float top, int count, int overDay, int startIndex) {
        mPaint.setTextSize(mTextSizeDay);
        float dayTextLeading = getFontLeading(mPaint);
        for (int i = 0; i < count; i++) {
            int left = (startIndex + i) * mColumnWidth;
            int day = (overDay + i + 1);
            float centerX = left + mColumnWidth / 2;
            float centerY = top + mLineSpac + mDayHeight / 2;
            drawSelectRange(canvas, centerX, centerY, day, left);
            String dayStr = day + "";
            int dayTxtLen = (int) getFontlength(mPaint, dayStr);
            int x = left + (mColumnWidth - dayTxtLen) / 2;
            canvas.drawText(dayStr, x, top + mLineSpac + dayTextLeading, mPaint);
        }
    }

    /** 画选中状态 */
    private void drawSelectRange(Canvas canvas, float centerX, float centerY, int day, int left) {
        switch (beyondRange(day)) {
            case RANGE_START:
                if (mEndMills > mStartMills) {
                    mBgPaint.setColor(mSelectRangeBgColor);
                    Path startPath = new Path();
                    float startY = centerY + mSelectRadius;
                    startPath.moveTo(centerX, startY);
                    RectF startRect = new RectF(centerX - mSelectRadius, centerY - mSelectRadius,
                            centerX + mSelectRadius, centerY + mSelectRadius);
                    startPath.arcTo(startRect, 90, 180);
                    startPath.lineTo(centerX + mColumnWidth / 2, centerY - mSelectRadius);
                    startPath.lineTo(centerX + mColumnWidth / 2, centerY + mSelectRadius);
                    startPath.close();
                    canvas.drawPath(startPath, mBgPaint);
                }
                mBgPaint.setColor(mSelectBgColor);
                canvas.drawCircle(centerX, centerY, mSelectRadius, mBgPaint);
                mPaint.setColor(mSelectTextColor);
                break;
            case RANGE_END:
                mBgPaint.setColor(mSelectRangeBgColor);
                Path endPath = new Path();
                float endY = centerY - mSelectRadius;
                endPath.moveTo(centerX, endY);
                RectF endRect = new RectF(centerX - mSelectRadius, centerY - mSelectRadius,
                        centerX + mSelectRadius, centerY + mSelectRadius);
                endPath.arcTo(endRect, -90, 180);
                endPath.lineTo(centerX - mColumnWidth / 2, centerY + mSelectRadius);
                endPath.lineTo(centerX - mColumnWidth / 2, centerY - mSelectRadius);
                endPath.close();
                canvas.drawPath(endPath, mBgPaint);
                mBgPaint.setColor(mSelectBgColor);
                canvas.drawCircle(centerX, centerY, mSelectRadius, mBgPaint);
                mPaint.setColor(mSelectTextColor);
                break;
            case RANGE_INSIDE:
                mBgPaint.setColor(mSelectRangeBgColor);
                RectF rect = new RectF(left, centerY - mSelectRadius, left + mColumnWidth,
                        centerY + mSelectRadius);
                canvas.drawRect(rect, mBgPaint);
                mPaint.setColor(mSelectTextColor);
                break;
            default:
                mPaint.setColor(mDayTextColor);
                if (isCurrentMonth && day < mCurrentDay) {/*不可选的日期颜色(今天之前的日期),按需求展示本月及之后12个月份,那么当前月为最小的月份,如果不是当前月,那么一定是今天之后的日期*/
                    mPaint.setColor(mPastDayTxtColor);
                } else {//普通日期文字颜色
                    mPaint.setColor(mDayTextColor);
                }
                if (mSelectDay == day) {//绘制选中的日期
                    mPaint.setColor(mSelectTextColor);
                    mBgPaint.setColor(mSelectBgColor);
                    canvas.drawCircle(centerX, centerY, mSelectRadius, mBgPaint);
                }
                break;
        }
    }

    /** 相对于选中区间的位置(在区间内、区间外、区间头部、区间尾部) */
    private int beyondRange(int day) {
        Date todayDate = dayStr2Date(
                mUpdateYear + FORMAT_DATE_DIVIDE + mUpdateMonth + FORMAT_DATE_DIVIDE + day);
        if (todayDate == null) {
            return RANGE_OUTSIDE;
        }
        long todayMills = todayDate.getTime();
        if (mStartMills == todayMills) {
            return RANGE_START;
        } else if (mStartMills > todayMills) {
            return RANGE_OUTSIDE;
        } else {
            if (mEndMills < todayMills) {
                return RANGE_OUTSIDE;
            } else if (mEndMills == todayMills) {
                return RANGE_END;
            } else {
                return RANGE_INSIDE;
            }
        }
    }

    /** 获取月份标题 */
    private String getMonthStr(Date month) {
        SimpleDateFormat df = new SimpleDateFormat("yyyy/MM", Locale.getDefault());
        return df.format(month);
    }

    private Date str2Date(String str) {
        try {
            SimpleDateFormat df = new SimpleDateFormat("yyyy/MM", Locale.getDefault());
            return df.parse(str);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private Date dayStr2Date(String str) {
        try {
            SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault());
            return df.parse(str);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return mGestureDetector.onTouchEvent(event);
    }

    /** 焦点滑动 */
    private void touchFocusMove(final PointF point, boolean isEndEvent) {
        if (isTouchTitle(point)) {//事件在标题上
            if (!isEndEvent || mListener == null) {//标题只有在事件结束后才响应
                return;
            }
            if (isTouchLeftArrow(point)) {//点击左箭头
                monthChange(-1);
            } else if (isTouchRightArrow(point)) {//点击右箭头
                monthChange(1);
            }
        } else if (isTouchDay(point)) {//触摸的是日期
            if (!isEndEvent) {
                return;
            }
            touchDay(point, isEndEvent);
        }
    }

    /** 是否触摸的是左箭头 */
    private boolean isTouchLeftArrow(final PointF point) {
        return point.x >= mArrowLeftStart && point.x < (mArrowLeftStart + 2 * mMonthHorizontalSpace
                + mArrowWidth);
    }

    /** 是否触摸的是右箭头 */
    private boolean isTouchRightArrow(final PointF point) {
        return point.x > mArrowRightStart && point.x < (mArrowRightStart + 2 * mMonthHorizontalSpace
                + mArrowWidth);
    }

    /** 是否触摸的是标题(年月、箭头) */
    private boolean isTouchTitle(final PointF point) {
        if (!showTitle) {
            return false;
        }
        return point.y > mPadTop && point.y <= mPadTop + mTitleHeight;
    }

    /** 是否触摸的是日期 */
    private boolean isTouchDay(final PointF point) {
        return point.y > computeY(mPadTop);
    }

    /** 事件点在 日期区域 范围内 */
    private void touchDay(final PointF point, boolean isEndEvent) {
        float top = computeY(mPadTop);
        float diffY = point.y - top;
        int focusLine = (int) (diffY / oneHeight);
        if (diffY % oneHeight > 0) {
            focusLine++;
        }
        boolean availability = focusLine <= mLineNum;//触摸是否在日期范围内
        if (availability) {
            int xIdx = (int) Math.ceil(point.x / mColumnWidth);//根据X坐标找到具体的焦点日期
            if (xIdx <= 0) {
                xIdx = 1;
            }
            if (xIdx > 7) {
                xIdx = 7;
            }
            if (focusLine == 1) {//第一行
                if (xIdx <= mFirstDayIdx) {//到开始空位
                    updateSelectedDay(mSelectDay, true);
                } else {
                    updateSelectedDay(xIdx - mFirstDayIdx, isEndEvent);
                }
            } else if (focusLine == mLineNum) {//最后一行
                if (xIdx > mLastLineNum) {//到结尾空位
                    updateSelectedDay(mSelectDay, true);
                } else {
                    updateSelectedDay(mFirstLineNum + (focusLine - 2) * 7 + xIdx, isEndEvent);
                }
            } else {
                updateSelectedDay(mFirstLineNum + (focusLine - 2) * 7 + xIdx, isEndEvent);
            }
        } else {//超出日期区域后,视为事件结束,响应最后一个选择日期的回调
            updateSelectedDay(mSelectDay, true);
        }
    }

    /** 设置选中的日期 */
    private void updateSelectedDay(int day, boolean eventEnd) {
        if (isCurrentMonth && day < mCurrentDay) {//所在月份是当前月,且触摸的日期是今天之前,则不可选中
            return;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(mUpdateMonthDate);
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH) + 1;
        Date date = dayStr2Date(year + FORMAT_DATE_DIVIDE + month + FORMAT_DATE_DIVIDE + day);
        if (date == null) {
            return;
        }
        if (mType == SINGLE_SELECT_TYPE) {
            mSelectDay = day;
            invalidate();
            if (mListener != null && eventEnd && mLastSelectDay != mSelectDay) {
                mLastSelectDay = mSelectDay;
                mListener.onDayClick(date.getTime());
            }
        } else {
            if (mStartMills != 0) {
                if (date.getTime() < mStartMills) {
                    mStartMills = date.getTime();
                } else {
                    mEndMills = date.getTime();
                }
                invalidate();
            } /*else {
                mStartMills = date.getTime();
                invalidate();
            }*/
        }
    }

    public void updateSelectedDay(long selectMills) {
        if (selectMills == 0) {
            return;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date(selectMills));
        mSelectDay = calendar.get(Calendar.DAY_OF_MONTH);
        invalidate();
    }

    /** 月份增减 */
    private void monthChange(int change) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(mUpdateMonthDate);
        calendar.add(Calendar.MONTH, change);
        updateMonth(calendar.getTime());
        invalidate();
    }

    private float getFontlength(Paint paint, String str) {
        return paint.measureText(str);
    }

    /** 返回指定笔的文字高度 */
    private float getFontHeight(Paint paint) {
        Paint.FontMetrics fm = paint.getFontMetrics();
        return fm.descent - fm.ascent;
    }

    /** 返回指定笔离文字顶部的基准距离 */
    private float getFontLeading(Paint paint) {
        Paint.FontMetrics fm = paint.getFontMetrics();
        return fm.leading - fm.ascent;
    }

    /** 设置月份 */
    public void updateMonth(Date updateMonthDate) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        mCurrentDay = calendar.get(Calendar.DAY_OF_MONTH);//当前日期
        Date currentMonthDate = dayStr2Date(day2Str(new Date()));//当前月份
        mUpdateMonthDate = dayStr2Date(day2Str(updateMonthDate));
        if (currentMonthDate != null && currentMonthDate.getTime() == mUpdateMonthDate.getTime()) {
            isCurrentMonth = true;
            mSelectDay = mCurrentDay;//当月默认选中当前日
        } else {
            isCurrentMonth = false;
            mSelectDay = 0;
        }
        calendar.setTime(mUpdateMonthDate);
        computeRowsAndColumns(calendar);
    }

    private String day2Str(Date date) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault());
        return format.format(date);
    }

    public void clearSelect() {
        mSelectDay = 0;
        mLastSelectDay = 0;
        invalidate();
    }

    public void setSelectRange(long startMills, long endMills) {
        if (startMills > endMills) {
            return;
        }
        mStartMills = startMills;
        mEndMills = endMills;
        invalidate();
    }

    public void showWeek(boolean showWeek) {
        this.showWeek = showWeek;
        invalidate();
    }

    public void setStartMills(long startMills) {
        mStartMills = startMills;
        invalidate();
    }

    public void setEndMills(long endMills) {
        mEndMills = endMills;
        invalidate();
    }

    public long getStartMills() {
        return mStartMills;
    }

    public long getEndMills() {
        return mEndMills;
    }

    public boolean isStartMills() {
        return mStartMills == 0;
    }

    public void setOnClickListener(IClickListener listener) {
        this.mListener = listener;
    }

    public class GestureCallback extends GestureDetector.SimpleOnGestureListener {

        private PointF mFocusPoint = new PointF();//焦点坐标

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            mFocusPoint.set(e.getX(), e.getY());
            touchFocusMove(mFocusPoint, true);
            return super.onSingleTapConfirmed(e);
        }

        @Override
        public boolean onDown(MotionEvent e) {
            return true;//true,否则无法触发onSingleTap、onFling等方法
        }
    }
	
	/** Desc 2017*/
    public static String getMonthYear(Date date) {
        if (date == null) {
            date = new Date();
        }
        long countryId = LocaleManager.Country.ID;
        CountryItemInfo country = DefaultCountryManager.getInstance().getCurrentCountryInfo();
        if (country != null) {
            countryId = country.id;
        }
        Locale locale = LocaleManager.getLocale(countryId);
        DateFormat df = new SimpleDateFormat("MMM", locale);
        String month = df.format(date);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int year = calendar.get(Calendar.YEAR);
        return month + " " + year;
    }

    /** Desc 28,2017*/
    public static String getMonthDayYear(Date date) {
        if (date == null) {
            date = new Date();
        }
        long countryId = LocaleManager.Country.ID;
        CountryItemInfo country = DefaultCountryManager.getInstance().getCurrentCountryInfo();
        if (country != null) {
            countryId = country.id;
        }
        Locale locale = LocaleManager.getLocale(countryId);
        DateFormat df = new SimpleDateFormat("MMM dd,yyyy", locale);
        return df.format(date);
    }

    public interface IClickListener {
        void onDayClick(long mills);
    }
}

attr.xml


    
        
        
                   
                   
                   
                   
                
        
            
                   
                
            
                    
                 
                  
                     
                     
                     
                    
                    
                  
                         
                         
                 
     

效果图:单选效果、多选效果

Android自定义日历控件(附效果图)_第1张图片Android自定义日历控件(附效果图)_第2张图片Android自定义日历控件(附效果图)_第3张图片



你可能感兴趣的:(笔记)