简单自定义一个日历控件

package com.mingshan.jiepin;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.util.Calendar;

/**
 * <p>项目名称:jiepin
 * <p>包   名: com.mingshan.jiepin
 * <p>版   权: 深圳市铭淏网络科技有限公司 2017
 * <p>描   述:
 * <p>创 建 人: gengmingshan
 * <p>创建时间: 2018/5>>1)8 14:45
 * <p>当前版本: VERSION
 * <p>修订历史: (版本、修改时间、修改人、修改内容)
 */
public class CalendarView extends View
{
    final private String[] titleTexts = new String[]{"周日","周一","周二","周三","周四","周五","周六"};
    private int[][] showDays;//显示日
    private int mCurrYear;
    private int mCurrMonth;
    private int mCurrDayOfMonth;//当前时间
    private int mSelectYear;
    private int mSelectMonth;
    private int mSelectDay;//选中时间
    private int mShowYear;
    private int mShowMonth;//当前展示月
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private int mTitleRowTextSize = 14;
    private int mShowDayTextSize = 16;
    private int mRowSize = 6;//行数
    private int mColumnSize = 7;//列数
    private int mColumnWidth;//列宽
    private int mRowHeight;//行高
    private int mMinColumnWidth = dip2px(44);
    private int mMinColumnHeight = dip2px(36);
    private int mWidth;//控件宽
    private int mHeight;//控件高
    private int mOtherMonthColor = Color.parseColor("#44000000");
    private int mCurrentMonthColor = Color.parseColor("#99000000");
    private int mSelectColor = Color.parseColor("#7744ff88");
    private int mCurrentColor = Color.parseColor("#77ff4488");
    private Rect mBounds;

    private int downX;
    private int downY;
    private OnSelectItemListener mSelectItemListener;

    public CalendarView(Context paramContext, AttributeSet paramAttributeSet)
    {
        super(paramContext, paramAttributeSet);
        init();
    }

    private void init()
    {
        final Calendar calendar = Calendar.getInstance();
        mCurrYear = calendar.get(Calendar.YEAR);
        mCurrMonth = calendar.get(Calendar.MONTH);
        mCurrDayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);

        mSelectYear = mCurrYear;
        mSelectMonth = mCurrMonth;
        mSelectDay = mCurrDayOfMonth;
        mShowYear = mCurrYear;
        mShowMonth = mCurrMonth;

        mColumnSize = 7;

        mPaint = new Paint();
        mBounds = new Rect();
        mPaint.setTextSize(mTitleRowTextSize);
        mPaint.getTextBounds("周一", 0, "周一".length(), mBounds);
        Log.i("init", "mBounds.width():"+mBounds.width()+",mBounds.height()"+mBounds.height());
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.i("onMeasure", "widthMeasureSpec:"+widthMeasureSpec+",heightMeasureSpec:"+heightMeasureSpec);
        Log.i("onMeasure", "getMeasuredWidth:"+getMeasuredWidth()+",getMeasuredHeight:"+getMeasuredHeight());
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        if (widthMode == MeasureSpec.EXACTLY)
        {
            mWidth = widthSize;
            mColumnWidth = mWidth/mColumnSize;
        } else
        {
            int columnWidth = 2*mBounds.width();
            mColumnWidth = Math.max(mMinColumnWidth, columnWidth);
            Log.i("onMeasure", "mColumnWidth="+mColumnWidth+",mMinColumnWidth="+mMinColumnWidth);
            mWidth =  getPaddingLeft() + mColumnWidth*mColumnSize + getPaddingRight();
        }

        if (heightMode == MeasureSpec.EXACTLY)
        {
            mHeight = heightSize;
            mRowHeight = mHeight/mRowSize;
        } else
        {
            int columnHeight = 2*mBounds.height();
            mRowHeight = Math.max(mMinColumnHeight, columnHeight);
            Log.i("onMeasure", "mRowHeight="+ mRowHeight +",mMinColumnHeight="+mMinColumnHeight);
            mHeight = getPaddingTop() + mRowHeight *mRowSize + getPaddingBottom();
        }
        setMeasuredDimension(mWidth, mHeight);
        Log.i("onMeasure", "mWidth:"+mWidth+",mHeight"+mHeight);
    }

    private int dip2px(int paramInt)
    {
        float f = getResources().getDisplayMetrics().density;
        return (int)(paramInt * f + 0.5F);
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        mPaint.setStyle(Paint.Style.FILL);
        final DisplayMetrics metrics = getResources().getDisplayMetrics();
        mPaint.setColor(mCurrentMonthColor);
        mPaint.setTextSize(mTitleRowTextSize*metrics.scaledDensity);
//        canvas.drawText("啊哈哈哈哈哈",0,mBounds.height(), mPaint);
        int x;
        int y;
        y = getPaddingTop()+ (mRowHeight >>1)+(mBounds.height()>>1);
        x = getPaddingLeft()+(mColumnWidth>>1)-(mBounds.width()>>1);
        Log.i("onDraw", "mColumnWidth"+mColumnWidth+",mBounds.width()"+mBounds.width());
        Log.i("onDraw", "x"+x);
        Log.i("onDraw", "mRowHeight"+mRowHeight+",mBounds.height()"+mBounds.height());
        Log.i("onDraw", "y"+y);
        for(int i=0;i<mColumnSize;i++){
            canvas.drawText(titleTexts[i],x,y,mPaint);
            x+=mColumnWidth;
        }
        y+= mRowHeight;

        mPaint.setColor(mOtherMonthColor);
        final int lastMonthDayCount;
        final int currentMonthDayCount = getMonthDayCount(mShowYear, mShowMonth);
        final int firstDayWeek = getFirstDayWeek(mShowYear, mShowMonth);
        mRowSize = (currentMonthDayCount + firstDayWeek - 1)/mColumnSize + 1;
        showDays = new int[mRowSize][mColumnSize];
        if(mShowMonth==0){
            lastMonthDayCount = getMonthDayCount(mShowYear-1, 11);
        }else {
            lastMonthDayCount = getMonthDayCount(mShowYear, mShowMonth-1);
        }
        mPaint.setTextSize(mShowDayTextSize*metrics.scaledDensity);
        if(firstDayWeek>1){//开始绘制上月days
            mPaint.setColor(mOtherMonthColor);
        }
        int position;
        int day;
        for (int i = 0;i < mRowSize;i++)
        {
            for (int j = 0;j<mColumnSize;j++){
                position = i*mColumnSize + j;
                if(position==firstDayWeek-1){//开始绘制当前月days
                    mPaint.setColor(mCurrentMonthColor);
                }else if(position==firstDayWeek+currentMonthDayCount-1){//开始绘制下月days
                    mPaint.setColor(mOtherMonthColor);
                }
                if(position<firstDayWeek-1){
                    day = lastMonthDayCount - firstDayWeek + 2 + position;
                }else if(position<firstDayWeek+currentMonthDayCount-1){
                    day = position - firstDayWeek + 2;
                }else {
                    day = position - firstDayWeek - currentMonthDayCount + 2;
                }
                showDays[i][j] = day;
                final String dayString = ""+day;
                Rect bounds = new Rect();
                mPaint.getTextBounds(dayString,0,dayString.length(),bounds);

                x = getPaddingLeft()+j*mColumnWidth+(mColumnWidth>>1)-(bounds.width()>>1);
                canvas.drawText(day + "", x, y, mPaint);
            }
            y+= mRowHeight;
        }
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(3*metrics.scaledDensity);
        if(mSelectYear==mShowYear&&mShowMonth==mShowMonth){//画选中日
            mPaint.setColor(mSelectColor);
            int posY = (mSelectDay+firstDayWeek-2)/mColumnSize;
            int posX = firstDayWeek-2+mSelectDay-posY*mColumnSize;
            Log.i("onDra--w", "mSelectDay="+mSelectDay);
            Log.i("onDraw--", "posY="+posY);
            Log.i("onDra--w", "posX"+posX);
            canvas.drawCircle(getPaddingLeft()+posX*mColumnWidth+(mColumnWidth>>1),
                    getPaddingTop()+(posY+1)*mRowHeight+(mRowHeight>>1),
                    (mBounds.width()>>1)*metrics.scaledDensity,mPaint);
//            canvas.draw
        }
        if(mCurrYear==mShowYear&&mCurrMonth==mShowMonth){//画当前日
            mPaint.setColor(mCurrentColor);
            int posY = (mCurrDayOfMonth+firstDayWeek-2)/mColumnSize;
            int posX = firstDayWeek-2+mCurrDayOfMonth-posY*mColumnSize;
            canvas.drawCircle(getPaddingLeft()+posX*mColumnWidth+(mColumnWidth>>1),
                    getPaddingTop()+(posY+1)*mRowHeight+(mRowHeight>>1),
                    (mBounds.width()>>1)*metrics.scaledDensity,mPaint);
        }
    }

    private int getMonthDayCount(int year, int month)
    {
        switch (month + 1)
        {
            default:
                return -1;
            case 1:case 3:case 5:case 7:case 8:case 10:case 12:
                return 31;
            case 4:case 6:case 9:case 11:
                return 30;
            case 2:
                if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
                    return 29;
                }else {
                    return 28;
                }
        }

    }

    public static int getFirstDayWeek(int year, int month)
    {
        Calendar localCalendar = Calendar.getInstance();
        localCalendar.set(year, month, 1);
        return localCalendar.get(Calendar.DAY_OF_WEEK);
    }

    public void showMonth(int year, int month){
        mShowYear = year;
        mShowMonth = month;
        invalidate();
    }

    public void selectDay(int day){
        mSelectYear = mShowYear;
        mSelectMonth = mShowMonth;
        mSelectDay = day;
        invalidate();
    }

    public boolean onTouchEvent(MotionEvent event)
    {
        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                downX = (int)event.getX();
                downY = (int)event.getY();
                Log.i("ACTION_DOWN", "downX:"+downX+",downY:"+downY);
                if(downX>getPaddingLeft()&&downX<mWidth-getPaddingRight()
                        &&downY>getPaddingTop()+mRowHeight&&downY<mHeight-getPaddingBottom()){
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
                int upX = (int)event.getX();
                int upY = (int)event.getY();
                Log.i("ACTION_UP", "upX:"+upX+",upY:"+upY);
                if ((Math.abs(upX - downX) < 10) && (Math.abs(upY - downY) < 10))
                {
//                    performClick();
                    doClickAction(downX, downY);
                    return true;
                }
                break;
        }
        return false;
    }

    private void doClickAction(int x, int y)
    {
        final int posX = (x-getPaddingLeft())/mColumnWidth;
        final int posY = (y-getPaddingTop())/mRowHeight-1;
        final int day = showDays[posY][posX];
        Log.i("doClickAction", "posX="+posX+",posY="+posY+",day="+day);

        final int currentMonthDayCount = getMonthDayCount(mShowYear, mShowMonth);
        final int firstDayWeek = getFirstDayWeek(mShowYear, mShowMonth);
        final int position = posY*mColumnSize+posX;
        if(position<firstDayWeek-1){
            showMonth(mShowYear,mShowMonth-1);
        }else if(position<firstDayWeek-1+currentMonthDayCount){
            selectDay(day);
            if(mSelectItemListener!=null){
                mSelectItemListener.onSelect(mSelectYear, mSelectMonth, day);
                Log.i("onSelect", "onSelect"+day);
            }
        }else {
            showMonth(mShowYear,mShowMonth+1);
        }
    }

    public void setOnSelectItemListener(OnSelectItemListener listener){
        mSelectItemListener = listener;
    }

    static interface OnSelectItemListener{
        void onSelect(int year, int month, int day);
    }
}
需要补充头和事件点展示

猜你喜欢

转载自blog.csdn.net/weixin_42167360/article/details/80496942