先上功能图,有图有真相
这个日历可以进行多个选择,既可以换的换月,又可以点击换月,还可以选择换年月,日历简洁,对之需要日历选择的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或者点击这里哦