使用cavans绘制小日历(未来15天)

这里分享一个使用canvans绘制小日历的demo。主要的思路就是由于只要显示接下来的15天时间,并增加点击时间。所以想到了,使用cavans的绘制,当然使用gridview的item的显示或者隐藏也是可以的。由于项目需要的只是未来15天的绘制,所以没有做过多的拓展。比如拓展成日历啥的。看效果图:
这里写图片描述
这里写图片描述

一、分析
首先我们会想到绘制星期一到星期天这1行7列的数据。然后根据当前时间,推算出明天是星期几,在绘制列数的循环中进行判断,从哪里开始绘制,然后第二行永远是一整行,所以获取星期一的日期进行绘制一整行,最后一行是算出剩余天数,使用15-前面两行用掉的日期。当然我们需要注意的是判断下一天是否到了下个月。所以需要做一个取余数的操作。

 if (firstnum > monthNum) {
   firstnum = firstnum % monthNum;//取余数
 }

代码已经很详细了。就是一个循环,绘制所有行。当然比较笨的方法。后面希望能拓展写出一个真的日历。

二、难点
1、使得绘制的文字居中。我们这里当然采用宽度等分的思想,以及高度多个文字高度/2的行距。
公式:

以绘制星期几为例:


日期标题行:
int startX = mColumSize * i + (mColumSize - fontWidth) / 2;

int startY = (int) (mRowSize * 0 + mRowSize / 2 - (mWeekPaint.ascent() + mWeekPaint.descent()) / 2);

日期第一行:
 int startX1 = mColumSize * i + (mColumSize - (int) mDatePaint.measureText(text1)) / 2;
 int startY1 = (int) (mRowSize * 1 + mRowSize / 2 - (mDatePaint.ascent() + mDatePaint.descent()) / 2);

日期第二行:

int startX2 = mColumSize * i + (mColumSize - (int) mDatePaint.measureText(t)) / 2;
int startY2 = (int) (mRowSize * 2 + mRowSize / 2 - (mDatePaint.ascent() + mDatePaint.descent()) / 2);

日期第三行:

 int startX3 = mColumSize * i + (mColumSize - (int) mDatePaint.measureText(tx)) / 2;
int startY3 = (int) (mRowSize * 3 + mRowSize / 2 - (mDatePaint.ascent() + mDatePaint.descent()) / 2);

2、点击日期的监听事件并绘制实心圆。

就是点击的时候获取坐标。判断是在那个网格的坐标内。进行回调操作,给选中日期mSelDay全局变量赋值,然后调用invalidate();方法,重绘该视图,调用ondraw()方法。思路比较清晰

  //判断是否为选中的日期
 if (!mSelDay.equals("") && mSelDay.equals(text1)) {
   //先绘制背景,不然会覆盖
    mSelPaint.setColor(mCircleColor);
    mSelPaint.setAntiAlias(true);
    mSelPaint.setStyle(Paint.Style.FILL);
    drawCircle(1, i, canvas);
     //字体
    mSelPaint.setColor(Color.WHITE);
    mSelPaint.setTextSize(mDateTextSize);
    canvas.drawText(text1, startX1, startY1, mSelPaint);
   //清空
    mSelDay = "";
    } else {
  canvas.drawText(text1, startX1, startY1, mDatePaint);
                }

三、实现

1、自定义view的实现,CalendarCard.java.java:

package com.xxx.xxx.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;

import com.coofond.carservices.R;
import com.coofond.carservices.utils.DataUtil;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * @description: 日历小控件显示未来15天的日期并可点击
 * @Author zsj on 2017/2/24 14:21.
 */

public class CalendarCard extends View {

    /**
     * 几行几列
     */
    private static final int NUM_COLUMS = 7;
    private static final int NUM_ROWS = 4;
    /**
     * 标题星期的颜色
     */
    private int mWeekdayColor;
    /**
     * 星期标题
     */
    private String[] mWeekString = new String[]{"一", "二", "三", "四", "五", "六", "日"};
    /**
     * 星期画笔
     */
    private Paint mWeekPaint;
    /**
     * 星期文字大小
     */
    private int mWeekTextsize;
    /**
     * 屏幕参数
     */
    private DisplayMetrics mDisplayMetrics;
    /**
     * 日期画笔
     */
    private Paint mDatePaint;
    /**
     * 日期颜色
     */
    private int mDateColor;
    /**
     * 选中字体专属画笔
     */
    private Paint mSelPaint;
    /**
     * 日期文字大小
     */
    private int mDateTextSize;

    /**
     * 选中圆圈颜色
     */
    private int mCircleColor;
    /**
     * 选中圆圈半径
     */
    private int mRaduis;
    /**
     * 当前年份
     */
    private int mYear;
    /**
     * 当前月份
     */
    private int mMonth;
    /**
     * 当天日期
     */
    private int mDay;
    /**
     * 列宽
     */
    private int mColumSize;
    /**
     * 行高
     */
    private int mRowSize;
    /**
     * 记录选中的日期,后面重绘的时候进行判断
     */
    private String mSelDay = "";
    /**
     * 第一列日期
     */
    private List<String> dayString1 = new ArrayList<>();//第一列存储日期
    /**
     * 第二列日期
     */
    private List<String> dayString2 = new ArrayList<>();//第二列存储日期
    /**
     * 第三列日期
     */
    private List<String> dayString3 = new ArrayList<>();//第三列存储日期

    /**
     * 当前日期
     */
    private String mToday;

    /**
     * @return 获取选中的日期
     */
    public String getmChooseday() {
        return mChooseday;
    }

    /**
     * @param mChooseday 设置选中的日期
     */
    public void setmChooseday(String mChooseday) {
        this.mChooseday = mChooseday;
        mSelDay = DataUtil.getDay(mChooseday, new SimpleDateFormat("yyyy-MM-dd"));//设置选中的日期
    }

    /**
     * 选中的日期
     */
    private String mChooseday;
    /**
     * 日期点击事件
     */
    private DateClick dateClick;


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

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

    public CalendarCard(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        /**
         * 获取我们的自定义属性
         */
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CalendarCard, defStyleAttr, 0);
        int n = a.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                case R.styleable.CalendarCard_mWeekdayColor:
                    mWeekdayColor = a.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.CalendarCard_mWeekTextsize:
                    mWeekTextsize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12,
                            getResources().getDisplayMetrics()));
                    break;
                case R.styleable.CalendarCard_mDateColor:
                    mDateColor = a.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.CalendarCard_mDateTextSize:
                    mDateTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12,
                            getResources().getDisplayMetrics()));
                    break;
                case R.styleable.CalendarCard_mCircleColor:
                    mCircleColor = a.getColor(attr, Color.RED);
                    break;
                case R.styleable.CalendarCard_mCaRaduis:
                    mRaduis = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15,
                            getResources().getDisplayMetrics()));
                    break;
                case R.styleable.CalendarCard_mToday:
                    mToday = a.getString(attr);
                    break;
                case R.styleable.CalendarCard_mChooseday:
                    mChooseday = a.getString(attr);
                    break;
            }
        }
        a.recycle();
        mDisplayMetrics = getResources().getDisplayMetrics();
        mWeekPaint = new Paint();//标题画笔
        mDatePaint = new Paint();//日期画笔
        mSelPaint = new Paint();//选中的画笔
    }

    @Override
    protected void onDraw(Canvas canvas) {
        initSize();
        mWeekPaint.setStyle(Paint.Style.FILL);
        mWeekPaint.setAntiAlias(true);//去锯齿
        //适配,*屏幕密度   * mDisplayMetrics.scaledDensity加上这个字体会好大。。。
        mWeekPaint.setTextSize(mWeekTextsize);
        mWeekPaint.setColor(mWeekdayColor);
        mDatePaint.setStyle(Paint.Style.FILL);
        mDatePaint.setAntiAlias(true);
        mDatePaint.setTextSize(mDateTextSize);
        mDatePaint.setColor(mDateColor);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        //获取当天的星期数以及当月最大天数
        List<Integer> listInt = DataUtil.getWeekAndWeekCount(mToday, simpleDateFormat);
        int weekIndex = listInt.get(1);//明天星期几下标
        int monthNum = listInt.get(2);//当月最大天数
        int firstnum = listInt.get(0);//第一个星期下标。注意是当前日期+1
        int secondnum = listInt.get(0) + (7 - weekIndex + 1);//第二个星期下标
        int thirdnum = listInt.get(0) + (7 - weekIndex + 1) + 7;//第三个星期下标
        int thirdRelease = 15 - 7 - (7 - weekIndex + 1);//剩余天数
        //初始化
        dayString1.clear();
        dayString2.clear();
        dayString3.clear();
        //遍历进行绘制标题星期几
        for (int i = 0; i < mWeekString.length; i++) {
            String text = mWeekString[i];
            int fontWidth = (int) mWeekPaint.measureText(text);//获取到文本宽度
            int startX = mColumSize * i + (mColumSize - fontWidth) / 2;
            int startY = (int) (mRowSize * 0 + mRowSize / 2 - (mWeekPaint.ascent() + mWeekPaint.descent()) / 2);
            //绘制文字
            canvas.drawText(text, startX, startY, mWeekPaint);
            //判断是否为星期六,日。最后两次循环改变画笔颜色
            if (i == 5 || i == 6) {
                mDatePaint.setColor(mCircleColor);//修改一次就可以了
            }
            /**
             * 绘制日期第一行
             * 绘制从明天开始
             */
            if (i + 1 >= weekIndex) {
                if (firstnum > monthNum) {
                    firstnum = firstnum % monthNum;//取余数
                }
                String text1 = String.valueOf(firstnum);
                int startX1 = mColumSize * i + (mColumSize - (int) mDatePaint.measureText(text1)) / 2;
                int startY1 = (int) (mRowSize * 1 + mRowSize / 2 - (mDatePaint.ascent() + mDatePaint.descent()) / 2);
                //判断是否为选中的日期
                if (!mSelDay.equals("") && mSelDay.equals(text1)) {
                    //先绘制背景,不然会覆盖
                    mSelPaint.setColor(mCircleColor);
                    mSelPaint.setAntiAlias(true);
                    mSelPaint.setStyle(Paint.Style.FILL);
                    drawCircle(1, i, canvas);
                    //字体
                    mSelPaint.setColor(Color.WHITE);
                    mSelPaint.setTextSize(mDateTextSize);
                    canvas.drawText(text1, startX1, startY1, mSelPaint);
                    //清空
                    mSelDay = "";
                } else {
                    canvas.drawText(text1, startX1, startY1, mDatePaint);
                }
                dayString1.add(text1);
                firstnum++;
            } else {
                dayString1.add("");//如果不是就添加空,保持下标一致
            }
            /**
             * 绘制日期第二行
             */
            if (secondnum > monthNum) {
                secondnum = secondnum % monthNum;//取余数
            }
            String t = String.valueOf(secondnum);
            int startX2 = mColumSize * i + (mColumSize - (int) mDatePaint.measureText(t)) / 2;
            int startY2 = (int) (mRowSize * 2 + mRowSize / 2 - (mDatePaint.ascent() + mDatePaint.descent()) / 2);
            if (!mSelDay.equals("") && mSelDay.equals(t)) {
                //先绘制背景,不然会覆盖
                mSelPaint.setColor(mCircleColor);
                mSelPaint.setStyle(Paint.Style.FILL);
                drawCircle(2, i, canvas);
                //字体
                mSelPaint.setColor(Color.WHITE);
                mSelPaint.setAntiAlias(true);
                mSelPaint.setTextSize(mDateTextSize);
                canvas.drawText(t, startX2, startY2, mSelPaint);
                //清空
                mSelDay = "";
            } else {
                canvas.drawText(t, startX2, startY2, mDatePaint);
            }
            dayString2.add(t);
            secondnum++;
            /**
             * 绘制日期第三行
             */
            //剩余循环次数
            if (thirdRelease > 0) {
                if (thirdnum > monthNum) {
                    thirdnum = thirdnum % monthNum;//取余数
                }
                String tx = String.valueOf(thirdnum);
                int startX3 = mColumSize * i + (mColumSize - (int) mDatePaint.measureText(tx)) / 2;
                int startY3 = (int) (mRowSize * 3 + mRowSize / 2 - (mDatePaint.ascent() + mDatePaint.descent()) / 2);
                if (!mSelDay.equals("") && mSelDay.equals(tx)) {
                    //先绘制背景,不然会覆盖
                    mSelPaint.setColor(mCircleColor);
                    mSelPaint.setAntiAlias(true);
                    mSelPaint.setStyle(Paint.Style.FILL);
                    drawCircle(3, i, canvas);
                    //字体
                    mSelPaint.setColor(Color.WHITE);
                    mSelPaint.setTextSize(mDateTextSize);
                    canvas.drawText(tx, startX3, startY3, mSelPaint);
                    //清空
                    mSelDay = "";
                } else {
                    canvas.drawText(tx, startX3, startY3, mDatePaint);
                }
                dayString3.add(tx);
                thirdnum++;
                thirdRelease--;
            }
        }
    }


    /**
     * 初始化列宽行高
     */
    private void initSize() {
        mColumSize = getWidth() / NUM_COLUMS;
        mRowSize = mRaduis * 2 * 4 / NUM_ROWS;
    }

    /**
     * 监听点击事件
     */
    private int downX = 0, downY = 0;

    //监听触摸事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int eventCode = event.getAction();
        switch (eventCode) {
            //按下
            case MotionEvent.ACTION_DOWN:
                downX = (int) event.getX();
                downY = (int) event.getY();
                break;
            //移动
            case MotionEvent.ACTION_MOVE:
                break;
            //抬起
            case MotionEvent.ACTION_UP:
                int upX = (int) event.getX();
                int upY = (int) event.getY();
                //判断点击事件
                if (Math.abs(upX - downX) < 10 && Math.abs(upY - downY) < 10) {
                    performClick();
                    doClickAction((upX + downX) / 2, (upY + downY) / 2);
                }
                break;
        }
        return true;
    }

    //绘制圆形背景
    private void drawCircle(int row, int column, Canvas canvas) {
        mSelPaint.setColor(mCircleColor);
        float circleX = (float) (mColumSize * column + mColumSize * 0.5);
        float circley = (float) (mRowSize * row + mRowSize * 0.5);
        canvas.drawCircle(circleX, circley, mRaduis, mSelPaint);
    }

    //点击之前做的事情
    @Override
    public boolean performClick() {
        return super.performClick();
    }

    //点击日期
    private void doClickAction(int x, int y) {
        int row = y / mRowSize;
        int column = x / mColumSize;
        switch (row) {
            case 0:
                break;
            case 1:
                //这里需要注意的是dayString1.get(column)获取的是点击的位置(列数)
                if (!dayString1.get(column).equals("")) {
                    mSelDay = dayString1.get(column);
                    invalidate();
                }
                break;
            case 2:
                mSelDay = dayString2.get(column);
                invalidate();
                break;
            case 3:
                if (column < dayString3.size()) {
                    mSelDay = dayString3.get(column);
                    invalidate();
                }
                break;
        }
        //执行activity发送过来的点击处理事件
        if (dateClick != null) {
            dateClick.onClickOnDate();
        }
    }

    /**
     * 设置日期的点击回调事件
     */
    public interface DateClick {
        void onClickOnDate();
    }

    /**
     * 设置日期点击事件
     *
     * @param dateClick
     */
    public void setDateClick(DateClick dateClick) {
        this.dateClick = dateClick;
    }

    /**
     * 返回选中的日期
     */
    public String getmSelDay() {
        return mSelDay;
    }


}

2、xml中的使用

 <com.xxx.xxx.widget.CalendarCard
            android:id="@+id/calendar"
            android:layout_width="526px"
            android:layout_height="120dp"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="48px"
            app:mCaRaduis="15dp"
            app:mChooseday="2017-03-23"
            app:mCircleColor="@color/orange"
            app:mDateColor="@color/textcolordark"
            app:mDateTextSize="13sp"
            app:mToday="2017-03-18"
            app:mWeekTextsize="12sp"
            app:mWeekdayColor="@color/textcolorgray" />

3、attrs中的自定义属性

<!--日历控件-->
    <declare-styleable name="CalendarCard">
        <attr name="mWeekdayColor" format="color" />
        <attr name="mWeekTextsize" format="dimension" />
        <attr name="mDateColor" format="color" />
        <attr name="mDateTextSize" format="dimension" />
        <attr name="mCircleColor" format="color" />
        <attr name="mCaRaduis" format="dimension" />
        <attr name="mToday" format="string" />
        <attr name="mChooseday" format="string" />
    </declare-styleable>

3、像普通的view一样使用

CalendarCard mCalendar=(CalendarCard )findViewById(R.id.calendar);
  //日历点击事件
    mCalendar.setDateClick(new CalendarCard.DateClick() {
            @Override
            public void onClickOnDate() {
 ToastUtil.toastCenter2(mContext, mCalendar.getmSelDay());
            }
        });

四、总结:
代码很详细了。直接拿去用,就不上demo了。纯粹做个总结,给自己当个笔记,如果有一行代码能帮到你,那我也很开心。have a nice day~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值