Android自定义View日历界面

一、自定义日历界面
(1)自定义View日历星期
绘制两条直线,将View的宽度等分为七份,绘制星期

public class WeekDayView extends View {

    private Paint paint;
    private String[] weekString = new String[]{"S", "M", "T", "W", "T", "F", "S"};
    private int lineColor = Color.parseColor("#CCE4F2");
    private int lineWidth = 4;
    private int weekColor = Color.parseColor("#1FC2F3");
    private int weekSize = 34;

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

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

    public WeekDayView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true); //设置抗锯齿的效果
        paint.setStyle(Paint.Style.STROKE); //设置画笔的样式为描边
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
    }

    /**
     * 测量宽度
     *
     * @param measureSpec
     * @return
     */
    private int measureWidth(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        int result = 200;
        if (specMode == MeasureSpec.AT_MOST) {//设置为wrap_content
            result = specSize;
        } else if (specMode == MeasureSpec.EXACTLY) {//设置为match_parent或者为一个具体的值
            result = specSize;
        }
        return result;
    }

    /**
     * 测量高度
     *
     * @param measureSpec
     * @return
     */
    private int measureHeight(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        int result = 500;
        if (specMode == MeasureSpec.AT_MOST) {
            result = specSize;
        } else if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        }
        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //获取控件的宽高
        int width = getWidth();
        int height = getHeight();
        //设置画笔的颜色
        paint.setColor(lineColor);
        //设置画笔的宽度
        paint.setStrokeWidth(lineWidth);
        //绘制上下两条线
        canvas.drawLine(0, 0, width, 0, paint);
        canvas.drawLine(0, height, width, height, paint);
        int columnWidth = width / 7;
        //绘制星期
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(weekColor);
        paint.setTextSize(weekSize);
        for (int i = 0; i < weekString.length; i++) {
            String text = weekString[i];
            int fontWidth = (int) paint.measureText(text);
            int startX = columnWidth * i + (columnWidth - fontWidth) / 2;
            int startY = (int) (height / 2 - (paint.ascent() + paint.descent()) / 2);
            canvas.drawText(text, startX, startY, paint);
        }
    }

    /**
     * 设置线的颜色
     *
     * @param lineColor
     */
    public void setLineColor(int lineColor) {
        this.lineColor = lineColor;
    }

    /**
     * 设置线的宽度
     *
     * @param lineWidth
     */
    public void setLineWidth(int lineWidth) {
        this.lineWidth = lineWidth;
    }

    /**
     * 设置字体的颜色
     *
     * @param weekColor
     */
    public void setWeekColor(int weekColor) {
        this.weekColor = weekColor;
    }

    /**
     * 设置字体的大小
     *
     * @param weekSize
     */
    public void setWeekSize(int weekSize) {
        this.weekSize = weekSize;
    }
}

(2)自定义日历日期
获取当前日期的天数,获取当前月的第一天是星期几,开始绘制将View设置为六行七列相同大小的空格,在空格中添加绘制日期。

public class MediaDayController extends View {

    private Paint paint;
    private int widthSize, heightSize;
    private int daySize = 30;
    private Calendar calendar;
    private String day;
    private int monthDay;
    private int setYear, setMonth;

    @RequiresApi(api = Build.VERSION_CODES.N)
    public MediaDayController(Context context) {
        this(context, null);
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    public MediaDayController(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    public MediaDayController(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setTextSize(daySize);
        calendar = Calendar.getInstance();
        setYear = calendar.get(Calendar.YEAR);
        setMonth = calendar.get(Calendar.MONTH) + 1;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
    }

    /**
     * 测量宽度
     *
     * @param measureSpec
     * @return
     */
    private int measureWidth(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        int result = 500;
        if (specMode == MeasureSpec.AT_MOST) {//设置为wrap_content
            result = specSize;
        } else if (specMode == MeasureSpec.EXACTLY) {//设置为match_parent或者为一个具体的值
            result = specSize;
        }
        return result;
    }

    /**
     * 测量高度
     *
     * @param measureSpec
     * @return
     */
    private int measureHeight(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        int result = 500;
        if (specMode == MeasureSpec.AT_MOST) {
            result = specSize;
        } else if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        }
        return result;
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //获取日期字体的宽高
        widthSize = getWidth() / 7;
        heightSize = getHeight() / 6;
        //先指定年份和月份
        calendar.set(Calendar.YEAR, setYear);
        calendar.set(Calendar.MONTH, setMonth - 1);
        //获取当前月的总天数
        monthDay = calendar.getActualMaximum(Calendar.DATE);
        //获取当前月的第一天是星期几
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        int weekNumber = calendar.get(Calendar.DAY_OF_WEEK);
        //循环绘制日期数字
        for (int i = 0; i < monthDay; i++) {
            day = (i + 1) + "";
            //一行中的第几个
            int column = (i + weekNumber - 1) % 7;
            //第几行
            int row = (i + weekNumber - 1) / 7;
            int startX = (int) (widthSize * column + (widthSize - paint.measureText(day)) / 2);
            int startY = (int) (heightSize * row + heightSize / 2 - (paint.ascent() + paint.descent()) / 2);
            canvas.drawText(day, startX, startY, paint);
        }
    }

    public void setSelect(int year, int month) {
        setYear = year;
        setMonth = month;
    }
}

二、添加点击效果的日历界面
重新onTouchEvent方法,在方法中获取当前的点击坐标,计算当前坐标位置在第几行第几列,在onDraw判断是否存在,存在绘制背景。

public class MediaDayController extends View {

    private Paint paint;
    private int widthSize, heightSize;
    private int daySize = 30;
    private Calendar calendar;
    private String day;
    private int monthDay;
    private int setYear, setMonth;

    private int carryY;
    private int columnX;

    @RequiresApi(api = Build.VERSION_CODES.N)
    public MediaDayController(Context context) {
        this(context, null);
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    public MediaDayController(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    public MediaDayController(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setTextSize(daySize);
        calendar = Calendar.getInstance();
        setYear = calendar.get(Calendar.YEAR);
        setMonth = calendar.get(Calendar.MONTH) + 1;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
    }

    /**
     * 测量宽度
     *
     * @param measureSpec
     * @return
     */
    private int measureWidth(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        int result = 500;
        if (specMode == MeasureSpec.AT_MOST) {//设置为wrap_content
            result = specSize;
        } else if (specMode == MeasureSpec.EXACTLY) {//设置为match_parent或者为一个具体的值
            result = specSize;
        }
        return result;
    }

    /**
     * 测量高度
     *
     * @param measureSpec
     * @return
     */
    private int measureHeight(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        int result = 500;
        if (specMode == MeasureSpec.AT_MOST) {
            result = specSize;
        } else if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        }
        return result;
    }

    private int carry;
    private int column;


    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //获取日期字体的宽高
        widthSize = getWidth() / 7;
        heightSize = getHeight() / 6;
        //先指定年份和月份
        calendar.set(Calendar.YEAR, setYear);
        calendar.set(Calendar.MONTH, setMonth - 1);
        //获取当前月的总天数
        monthDay = calendar.getActualMaximum(Calendar.DATE);
        //获取当前月的第一天是星期几
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        int weekNumber = calendar.get(Calendar.DAY_OF_WEEK);
        //循环绘制日期数字
        for (int i = 0; i < monthDay; i++) {
            day = (i + 1) + "";
            //第几行
            carry = (i + weekNumber - 1) / 7;
            //第几列
            column = (i + weekNumber - 1) % 7;
            //获取当前日期开始位置
            int startX = (int) (widthSize * column + (widthSize - paint.measureText(day)) / 2);
            int startY = (int) (heightSize * carry + heightSize / 2 - (paint.ascent() + paint.descent()) / 2);
            //绘制背景圆
            if (carry == carryY && column == columnX) {
                int startRecX = widthSize * column;
                int startRecY = heightSize * carry;
                int endRecX = startRecX + widthSize;
                int endRecY = startRecY + heightSize;
                int px = DensityUtils.dip2px(getContext(), 12);
                paint.setColor(Color.parseColor("#FFA500"));
                canvas.drawCircle((startRecX + endRecX) / 2, (startRecY + endRecY) / 2, px, paint);
            }
            //绘制日期字体
            paint.setColor(Color.BLACK);
            canvas.drawText(day, startX, startY, paint);
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            //获取点击位置
            float x = event.getX();
            float y = event.getY();
            //计算点击位置在第几行第几列
            carryY = (int) (y / heightSize);
            columnX = (int) (x / widthSize);
            //刷新界面
            invalidate();
            return true;
        }
        return super.onTouchEvent(event);
    }

    /**
     * 设置日期
     *
     * @param year
     * @param month
     */
    public void setSelect(int year, int month) {
        setYear = year;
        setMonth = month;
    }
}
    原文作者:kjy_112233
    原文地址: https://www.jianshu.com/p/698bd3bf38c5
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞