先上效果图:

android 单行日历选择框架 android日历控件_android 单行日历选择框架

  这样的日历控件显然已经烂大街都是了,这里小弟只是在这儿练练手,正好大作业也有用到就整理一篇博客,希望留作记录与学习。

  完整代码在https://github.com/CarsonWoo/MyCalendarView 有kotlin和java两个版本

准备工作

  首先是绘制分析工作,我们知道这个日历控件我们可以分成几个模块来看。

android 单行日历选择框架 android日历控件_android 单行日历选择框架_02

  红色框框和蓝色框框的实现比较容易,只是还要注意蓝色框框内星期的间距:要通过计算一个格子占用的空间占总宽度的1/7.有了这个间距,我们下面的日期也是同理drawText上去就好了。先上前两个框框的实现代码:

private void drawMonth(Canvas canvas) {
    mTextPaint.setTextSize(mTitleSize);
    mTextPaint.setColor(Color.BLACK);
    int x = (getWidth() - (int) mTextPaint.measureText(currentMonth)) / 2;
    canvas.drawText(currentMonth, x, mTitleHeight, mTextPaint);
}

private void drawWeek(Canvas canvas) {
    mTextPaint.setTextSize(mWeekSize);
    mTextPaint.setColor(Color.parseColor("#BBBBBB"));

    for (int i = 0; i < 7; i++) {
        // 获取星期对应的字段的长度
        int len = (int) mTextPaint.measureText(weeks[i]);
        // 设置当前从哪个位置开始drawText
        int currentX = (int) (i * perWidth + (perWidth - len) / 2);
        canvas.drawText(weeks[i], currentX, mTitleHeight
                + mWeekPaddingTop + mWeekHeight, mTextPaint);
    }

    // 绘制分隔线
    canvas.drawLine(0, mTitleHeight
                    + mWeekPaddingTop + mWeekHeight + dp2px(10), getMeasuredWidth(),
            mTitleHeight + mWeekPaddingTop + mWeekHeight + dp2px(10), mBgPaint);
}

  至于perWidth在哪里拿,其实我们只需要在onMeasure的时候初始化一遍就好了。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec); 
    perWidth = width / 7;
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), heightMeasureSpec);
}

  有了perWidth,我们甚至可以为所欲为。。。(开个玩笑)

计算工作

  其实我上面那句并不完全是玩笑话,毕竟拿到了perWidth,我们只需要在对应的currentX以及currentY下drawText就可以成功的绘制出类日历效果,但是真正的日历还是要通过计算的,所以还是得老老实实地进行计算。

  计算的过程其实也比较简单,我们只是需要拿到当前月份的第一天是星期几,并且通过Date日期类去获取到当月有多少天即可。当然我这里还做了上一个月有多少天的一个计算,这里其实可以用map去存储,避免重复计算,是一个优化的点。

  

public void setMonth(Date date) {
        SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd");
        String mCurDate = sf.format(date);
        Log.i(TAG, "mCurDate = " + mCurDate);
        String month = mCurDate.substring(4, 6);
        Log.i(TAG, "month = " + month);

        String curDate = sf.format(new Date());
        String curMonth = curDate.substring(4, 6);
        if (curMonth.equals(month)) {
            isCurMonth = true;
        } else {
            isCurMonth = false;
        }

        if (month.startsWith("0")) {
            month = month.substring(1);
        }
        Log.i(TAG, "month = " + month);
        int monthInt = Integer.parseInt(month);
        this.currentMonth = months[monthInt - 1];
        Log.i(TAG, "monthInt = " + monthInt);

        String mDate = mCurDate.substring(6, 8);
        if (mDate.startsWith("0")) {
            mDate = mDate.substring(1);
        }
        // 获取到当前日期
        this.currentDate = Integer.parseInt(mDate);

        Calendar c = Calendar.getInstance();
        c.setTime(date);
        // 当前月份第一天开始是星期几 周一开始算的话要 - 2
        firstIndex = c.get(Calendar.DAY_OF_WEEK) - 2;
        if (firstIndex < 0) {
            firstIndex += 7;
        }

        Log.i(TAG, "firstIndex = " + firstIndex);

        // 当前月份的天数
        dayOfMonth = c.getActualMaximum(Calendar.DAY_OF_MONTH);
        int days = dayOfMonth;
        Log.i(TAG, "dayOfMonth = " + dayOfMonth);

        days -= (7 - firstIndex);
        // 最后一行剩余的天数
        remains = days;
        rowCount = 1;
        while (remains >= 7) {
            remains -= 7;
            rowCount++;
        }
        if (remains > 0) rowCount++;

        Log.i(TAG, "rowCount = " + rowCount);
        Log.i(TAG, "remains = " + remains);

        // 模拟添加数据
        selectedDates.add(4);
        selectedDates.add(9);
        selectedDates.add(16);
        selectedDates.add(25);

        // 上一个月
        c.add(Calendar.MONTH, -1);
        // 上一个月总共有多少天
        lastMonthDays = c.getActualMaximum(Calendar.DAY_OF_MONTH);

    }

  至于drawDate,那还不是分分钟的事吗。这里提示一下:想要做阴影效果还是要关闭硬件加速才可以显示。

  改善问题

  如果仔细研究代码可以发现我这里的cy是没有拿到准确值的,我也是根据视觉去调整。我也会好好思考一下这里怎么去拿到准确值。也希望有大佬能和我分享一下。

android 单行日历选择框架 android日历控件_github_03