先上功能图,有图有真相

安卓引入Material Design_安卓引入Material Design

这个日历可以进行多个选择,既可以换的换月,又可以点击换月,还可以选择换年月,日历简洁,对之需要日历选择的app可以做选择。在写这个功能的时候也在网上找了好多,要不就是功能太多,要不就是没有选择年月的,而且大部分都是用ListView或者RecycleView实现的,滑动换月的时候还卡顿。所以最后就自己根据别人的写了一个。

这个日历分为三个部分,一个是头部的年月选择和显示,一个是中间用的ViewPager实现的滑动,ViewPager里面的内容是自己写的一个View,

这里就说说这个自定义View就可以了,首先是画格子,这里星期的格子要比每日的高一些,所以做了一些比例处理,然后就用Path类进行路径处理,都是画一些直线,比较简单,然后就是分为星期字体的显示,这个就纯在一个问题,怎么让它在它的格子中居中,水平居中到还简单,到锤子居中的时候就找了很久,没法,半路搞程序远没有这些基础,实验了各种网上的方法,终于实现了。下面的日期居中方法基本差不多,最大的坑就是选择日期的背景总是不能完全覆盖,总有各种超出,原因是各种设备上面从dp换成px后在分到每个模块的时候总会有小数,最后想了一个办法就是,在给他进行布局测量的时候就让每个模块的大小没有小数,就对其取余,然后减去这部分进行布局,这样分给每个模块就是正数了,就可以完全吻合每个框了。

本来打算加上农历的,那个算法……我不会,又没有找到正确的,就暂时弄了个只有阳历的,等日后加上去。


package com.kxiang.calendar;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * 项目名称:JobLogging
 * 创建人:kexiang
 * 创建时间:2016/10/25 14:17
 * <p>
 * 单个 layout_height的7.5倍
 * 单个 layout_width的7倍
 */

public class CalendarSingleView extends View {
    public CalendarSingleView(Context context, int year, int month, int today) {
        super(context);
        init(year, month, today);
    }

//    public CalendarSingleView(Context context, AttributeSet attrs) {
//        super(context, attrs);
//        init(2016, 10, 25);
//    }
//
//    public CalendarSingleView(Context context, AttributeSet attrs, int defStyleAttr) {
//        super(context, attrs, defStyleAttr);
//
//        init(2016, 10, 25);
//    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //画框
        canvas.drawPath(utils.gridPath, utils.gridPaint);
        //星期水平加垂直居中
        Paint.FontMetricsInt fontMetrics = utils.weekPaint.getFontMetricsInt();
        float baseline = (utils.weekHeight - fontMetrics.bottom - fontMetrics.top) / 2f;
        for (int i = 0; i < utils.weekText.length; i++) {
            if (i == 0 || i == utils.weekText.length - 1) {
                utils.weekPaint.setColor(utils.weekendColor);
            }
            else {
                utils.weekPaint.setColor(utils.workdayColor);
            }
            canvas.drawText(utils.weekText[i], utils.width * (1 + i * 2) / 14f,
                    baseline, utils.weekPaint);
        }
        //选中时候的背景
        // 按下状态,选择状态背景色
        if (today == NO_MONTH && downBgIndex == weakSize) {

        }
        else {
            drawSelectBg(canvas, downBgIndex);
        }

        //日期水平加垂直居中
        int day = 0;
        float weakDayOneHight = utils.height * 4 / 5f / 6f;
        for (int i = 0; i < 6; i++) {
            float weakDayLine = baseline + utils.weekHeight / 2f + weakDayOneHight * (i * 2 + 1) / 2f;
            for (int j = 0; j < 7; j++) {

                int color = utils.noSelectTextColor;
                if (monthBean.getDay().get(day).isDimBright()) {
                    if (monthBean.getDay().get(day).getSolarCalendar().equals(today + "")) {
                        color = utils.todayTextColor;
                    }
                    else {
                        color = utils.textColor;
                    }
                }
                utils.weekDayPaint.setColor(color);
                canvas.drawText(monthBean.getDay().get(day).getSolarCalendar(),
                        utils.width * (1 + j * 2) / 14f,
                        weakDayLine, utils.weekDayPaint);
                day++;
            }
        }

    }


    private void drawSelectBg(Canvas canvas, int index) {
        int x = getXByIndex(index);
        int y = getYByIndex(index);
        float left = utils.singleDayWith * (x - 1) + 1;
        float top = utils.singleDayHeigh * (y - 1) + utils.weekHeight + 1;
        canvas.drawRect(left, top,
                left + utils.singleDayWith - 1,
                top + utils.singleDayHeigh - 1,
                utils.selectBgPaint);
    }

    private int getXByIndex(int i) {
        return i % 7 + 1; // 1 2 3 4 5 6 7
    }

    private int getYByIndex(int i) {
        return i / 7 + 1; // 1 2 3 4 5 6
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                setSelectedDateByCoor(event.getX(), event.getY());
                break;
            case MotionEvent.ACTION_UP:
                invalidate();
                if (onItemClickListener != null && dayBoolean) {

                    if (monthBean.getDay().get(downDataIndex).isDimBright()) {
                        //响应监听事件
                        onItemClickListener.OnItemClick(monthBean.getDay().
                                get(downDataIndex).getSolarCalendar());
                    }
                }
                break;
        }
        return true;
    }


    private int downDataIndex;
    private boolean dayBoolean = false;

    private void setSelectedDateByCoor(float x, float y) {

        if (y > utils.weekHeight) {
            dayBoolean = true;
            int m = (int) (Math.floor(x / utils.singleDayWith) + 1);
            int n = (int) (Math
                    .floor((y - (utils.weekHeight))
                            / Float.valueOf(utils.singleDayHeigh)) + 1);
            downDataIndex = (n - 1) * 7 + m - 1;
            if (monthBean.getDay().get(downDataIndex).isDimBright()) {
                downBgIndex = downDataIndex;
            }

        }
        else {
            dayBoolean = false;
        }
    }

    private class Utils {
        private String[] weekText = {"星期天", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
        private int width;
        private int height;
        private float weekHeight;
        //网格画笔
        private Paint gridPaint;
        //网格路径
        private Path gridPath;
        //星期显示画笔
        private Paint weekPaint;
        //工作日颜色
        private int workdayColor = Color.parseColor("#00CD66");
        //休息日颜色
        private int weekendColor = Color.RED;

        //具体日期画笔
        private Paint weekDayPaint;
        //当天日期颜色
        private int todayTextColor = Color.RED;
        //当月日期颜色
        private int textColor = Color.BLACK;
        //非当月日期颜色
        private int noSelectTextColor = Color.parseColor("#CCCCCC");

        //选中背景颜色
        private Paint selectBgPaint;
        private int selectBgColor = Color.parseColor("#99CCFF");

        private float singleDayWith;
        private float singleDayHeigh;

        void init() {

            weekHeight = height / 5f;
            if (weekHeight < width / 6f) {
                weekHeight = width / 6f;
            }
            singleDayHeigh = (height - weekHeight) / 6f;
            singleDayWith = width / 7f;


            weekPaint = new Paint();
            weekPaint.setColor(Color.BLACK);
            weekPaint.setAntiAlias(true);
            weekPaint.setTextSize(width / 6 * 0.28f);
            weekPaint.setStyle(Paint.Style.FILL);
            weekPaint.setTextAlign(Paint.Align.CENTER);

            weekDayPaint = new Paint();
            weekDayPaint.setColor(textColor);
            weekDayPaint.setAntiAlias(true);
            weekDayPaint.setTextSize(width / 6 * 0.35f);
            weekDayPaint.setStyle(Paint.Style.FILL);
            weekDayPaint.setTextAlign(Paint.Align.CENTER);

            gridPaint = new Paint();
            gridPaint.setColor(Color.RED);
            gridPaint.setStyle(Paint.Style.STROKE);
            //gridPaint.setStrokeWidth(2);

            gridPath = new Path();
            gridPath.moveTo(0, weekHeight);
            gridPath.lineTo(utils.width, weekHeight);

            for (int i = 1; i < 6; i++) {
                gridPath.moveTo(0, weekHeight + singleDayHeigh * i);
                gridPath.lineTo(width, weekHeight + singleDayHeigh * i);
                gridPath.moveTo(singleDayWith * i, 0);
                gridPath.lineTo(singleDayWith * i, height);
            }
            gridPath.moveTo(singleDayWith * 6, 0);
            gridPath.lineTo(singleDayWith * 6, height);

            selectBgPaint = new Paint();
            selectBgPaint.setAntiAlias(true);
            selectBgPaint.setStyle(Paint.Style.FILL);
            selectBgPaint.setColor(selectBgColor);

        }
    }

    private Utils utils;
    private DataMonthBean monthBean;
    private SpecialCalendar specialCalendar;
    private int today;

    private void init(int year, int month, int today) {
        this.today = today;
        utils = new Utils();
        specialCalendar = new SpecialCalendar();
        monthBean = getCalenderBean(year, month, today);

        setBackgroundColor(Color.parseColor("#FFFFFF"));
    }


    public int getWeek(int y, int m, int d) {
        if (m < 3) {
            m += 12;
            --y;
        }
        return (d + 1 + 2 * m + 3 * (m + 1) / 5 + y + (y >> 2) - y / 100 + y / 400) % 7;
    }

    private int downBgIndex = 0; // 按下的格子索引
    public final static int NO_MONTH = -1;
    private int weakSize = 0;

    private DataMonthBean getCalenderBean(int year, int month, int today) {


        int lastMonth = month - 1;

        int firstWeak = weakSize = getWeek(year, month, 1);

        weakSize = downBgIndex = downDataIndex = today + firstWeak - 1;

        int showMonthDay = specialCalendar.getDaysOfMonth(
                specialCalendar.isLeapYear(year)
                , month
        );
        if (specialCalendar.isLeapYear(year) && month == 2) {
            showMonthDay = specialCalendar.getDaysOfMonth(
                    specialCalendar.isLeapYear(year)
                    , month
            );
        }

        int lastMonthDay = specialCalendar.getDaysOfMonth(
                specialCalendar.isLeapYear(year)
                , lastMonth
        ) + 1 - firstWeak;
        if (specialCalendar.isLeapYear(year) && (month - 1) == 2) {
            lastMonthDay = specialCalendar.getDaysOfMonth(
                    specialCalendar.isLeapYear(year)
                    , lastMonth
            ) + 1 - firstWeak;
        }


        DataMonthBean monthBean = new DataMonthBean();
        List<DataMonthBean.DayBean> beanList = new ArrayList<>();
        int next = 1;
        for (int i = 0; i < 42; i++) {

            if (firstWeak == 0) {
                //下个月
                if ((i - firstWeak) >= showMonthDay) {
                    DataMonthBean.DayBean dayBean = new DataMonthBean.DayBean();
                    //这个是设置农历的,犹豫在网上找了好久都没有找到正确的农历算法,暂时没有弄上去
                    dayBean.setLunarCalendar("");
//                            lunarCalendar.getLunarDate(lastMonthYear, lastMonth, next, false));
                    dayBean.setSolarCalendar("" + next);
                    dayBean.setDimBright(false);
                    beanList.add(dayBean);
                    next++;
                }
                //当前月
                else {
                    DataMonthBean.DayBean dayBean = new DataMonthBean.DayBean();
                    dayBean.setLunarCalendar("");
//                            lunarCalendar.getLunarDate(year, month, i + 1, false));
                    dayBean.setSolarCalendar("" + (i + 1));
                    dayBean.setDimBright(true);
                    beanList.add(dayBean);
                }
            }
            else {
                //上个月
                if (i < firstWeak) {
                    DataMonthBean.DayBean dayBean = new DataMonthBean.DayBean();
                    dayBean.setLunarCalendar("");
//                            lunarCalendar.getLunarDate(lastMonthYear, lastMonth, lastMonthDay, false));
                    dayBean.setSolarCalendar("" + lastMonthDay);
                    dayBean.setDimBright(false);
                    beanList.add(dayBean);
                    lastMonthDay++;
                }
                else {
                    //下个月
                    if ((i - firstWeak) >= showMonthDay) {
                        DataMonthBean.DayBean dayBean = new DataMonthBean.DayBean();
                        dayBean.setLunarCalendar("");
//                                lunarCalendar.getLunarDate(nextMonthYear, nextMonth, next, false));
                        dayBean.setSolarCalendar("" + next);
                        dayBean.setDimBright(false);
                        beanList.add(dayBean);
                        next++;
                    }
                    //当前月
                    else {
                        DataMonthBean.DayBean dayBean = new DataMonthBean.DayBean();
                        dayBean.setLunarCalendar("");
//                                lunarCalendar.getLunarDate(year, month,
//                                        (i + 1 - firstWeak), false));
                        dayBean.setSolarCalendar("" + (i + 1 - firstWeak));
                        dayBean.setDimBright(true);
                        beanList.add(dayBean);
                    }
                }
            }

        }
        monthBean.setDay(beanList);
        return monthBean;
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {


        int tempWidth = MeasureSpec.getSize(widthMeasureSpec);
        int tempHeight = MeasureSpec.getSize(heightMeasureSpec);
        //保证每个格子不纯在小数
        utils.width = tempWidth - tempWidth % 7;
        utils.height = tempHeight - tempHeight % 15;
        widthMeasureSpec = MeasureSpec.makeMeasureSpec(utils.width,
                MeasureSpec.EXACTLY);
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(utils.height,
                MeasureSpec.EXACTLY);
        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
                            int bottom) {
        if (changed) {
            utils.init();
        }
        super.onLayout(changed, left, top, right, bottom);
    }


    private OnItemClickListener onItemClickListener;

    //给控件设置监听事件
    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    //监听接口
    public interface OnItemClickListener {
        void OnItemClick(String date);
    }
}


下面是该项目的个github地址:https://github.com/luck-xiang/CalendarDialog或者点击这里哦

CalendarDialog