最近项目有个日历列表的需求,样式如上所示,之前考虑过用MaterialCalendarView,但是滑动的时候不刷新年月title,且MaterialCalendarView使用ViewPager等嵌套完成的,这样使用性能不太优秀,且比较卡顿所以自定义了一个日历控件
一.自定义日历控件
**
* 作者:徐敏敏 on 2016/12/25 0025 17:47
* 邮箱:15067596185@163.com
* 自定义日历控件
*/
public class CalendarScheduleView extends View {
// 画笔
private Paint paint;
// 列数
private static final int NUMS_COLUMN = 7;
// 行数(星期一行加日期六行)
private static final int NUMS_ROW = 7;
// 周日到周六的颜色
private int mWeekColor = Color.parseColor("#8B8B8B");
// 本月日期的颜色
private int mMonthDateColor = Color.parseColor("#000000");
// 可选日期的背景颜色
private int mSelectableDayColor = Color.parseColor("#00bcbe");
// 星期字体大小
private int mWeekSize = 10;
// 日期字体大小
private int mDateSize = 10;
// 可选、选择日期的圆圈半径
private float mCircleR;
//当前Date
private Date date;
// 当前年
private int mCurrentYear;
// 当前月
private int mCurrentMonth;
// 已记录日期
private List<Date> mSelectableDates = new ArrayList<>();
// 7行7列(第一行没有数据,为了计算位置方便,将星期那一行考虑进去)
private int[][] days = new int[NUMS_ROW][NUMS_COLUMN];
// 列宽
private int mColumnWidth;
// 行高
private int mRowHeight;
// DisplayMetrics对象
private DisplayMetrics displayMetrics;
/**
* 构造函数
*
* @param context
* @param attrs
* @description 初始化
*/
public CalendarScheduleView(Context context, AttributeSet attrs) {
super(context, attrs);
// 获取DisplayMetrics实例
displayMetrics = getResources().getDisplayMetrics();
// 获取日历实例
Calendar calendar = Calendar.getInstance();
// new一个Paint实例(抗锯齿)
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
// 获取当前年份
mCurrentYear = calendar.get(Calendar.YEAR);
// 获取当前月份
mCurrentMonth = calendar.get(Calendar.MONTH);
this.date = new Date();
}
public void setCurrentDate(Date date) {
mCurrentYear = Integer.parseInt(TimeUtils.date2String(date, "yyyy"));
mCurrentMonth = Integer.parseInt(TimeUtils.date2String(date, "MM"));
this.date = date;
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode == MeasureSpec.AT_MOST) {
widthSize = displayMetrics.densityDpi * 100;
}
int heightSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(widthMeasureSpec);
if (heightMode == MeasureSpec.AT_MOST) {
heightSize = displayMetrics.densityDpi * 120;
}
setMeasuredDimension(widthSize, heightSize);
}
@Override
protected void onDraw(Canvas canvas) {
// 计算每一列宽度
mColumnWidth = getWidth() / NUMS_COLUMN;
// 计算每一行高度
mRowHeight = getHeight() / NUMS_ROW;
// 绘制星期
drawDayOfWeekText(canvas);
// 绘制本月日期
drawDateText(canvas);
}
/**
* 绘制星期
*
* @param canvas
*/
private void drawDayOfWeekText(Canvas canvas) {
for (int dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) {
// 星期在第0行
int row = 0;
// 列数
int column = dayOfWeek;
// 填写星期
String day[] = {"日", "一", "二", "三", "四", "五", "六"};
// 设置字体大小
paint.setTextSize(mWeekSize * displayMetrics.scaledDensity);
// 设置画笔颜色
paint.setColor(mWeekColor);
// 左边坐标(居中显示)
int left = (int) (mColumnWidth * column + (mColumnWidth - paint.getTextSize()) / 2);
// 顶部坐标 (注意,竖直方向上是以baseline为基准写字的,因此要 - (paint.ascent() + paint.descent()) / 2)
int top = (int) (mRowHeight * row + mRowHeight / 2 - (paint.ascent() + paint.descent()) / 2);
// 绘制文字
canvas.drawText(day[dayOfWeek] + "", left, top, paint);
}
}
/**
* 绘制本月日期(若有背景,需要先绘制背景,否则会覆盖文字)
*
* @param canvas
*/
private void drawDateText(Canvas canvas) {
// 获取日历实例
Calendar calendar = Calendar.getInstance();
calendar.setTime(this.date);
// 获取当前月份天数
int daysOfMonth = calendar.getActualMaximum(Calendar.DATE);
// 获取当月第一天是一周中的第几天(周日为第一天)
int firstDayOfWeekInMonth = TimeUtils.getWeekIndex(TimeUtils.string2Date(String.valueOf(mCurrentYear) + "-" + String.valueOf(mCurrentMonth) + "-01", "yyyy-MM-dd"));
// 写入文字
for (int date = 1; date <= daysOfMonth; date++) {
// 当前日期所在行数(第一行为1:由于第一行显示星期,故日期行数加一)
int row = (date + firstDayOfWeekInMonth - 1 - 1) / 7 + 1;
// 当前日期所在列数(第一列为0)
int column = (date + firstDayOfWeekInMonth - 1 - 1) % 7;
// 储存日期信息
days[row][column] = date;
// 若是可选日期,绘制背景
for (Date day : mSelectableDates) {
calendar.setTime(day);
int i = calendar.get(Calendar.DAY_OF_MONTH);
if (i == date) {
drawSelectableBackground(canvas, row, column);
}
}
// 设置字体大小
paint.setTextSize(mDateSize * displayMetrics.scaledDensity);
// 设置画笔颜色
paint.setColor(mMonthDateColor);
// 日期左边坐标(居中显示)
int left = (int) (mColumnWidth * column + (mColumnWidth - paint.measureText(date + "")) / 2);
// 日期顶部坐标 (注意,竖直方向上是以baseline为基准写字的,因此要 - (paint.ascent() + paint.descent()) / 2)
int top = (int) (mRowHeight * row + mRowHeight / 2 - (paint.ascent() + paint.descent()) / 2);
// 绘制文字
canvas.drawText(date + "", left, top, paint);
}
}
/**
* 绘制可选日期背景
*
* @param canvas
* @param row
* @param column
*/
private void drawSelectableBackground(Canvas canvas, int row, int column) {
Paint paint = new Paint();
// 画笔颜色
paint.setColor(mSelectableDayColor);
paint.setAntiAlias(true); //消除锯齿
paint.setStyle(Paint.Style.STROKE);//绘制空心圆或 空心矩形
paint.setStrokeWidth(2);
// 圆心位置
float cX = (float) (mColumnWidth * column + mColumnWidth / 2);
float cY = (float) (mRowHeight * row + mRowHeight / 2);
// 圆形半径
mCircleR = (float) (mColumnWidth / 2 * 0.8);
// 绘制圆形背景
canvas.drawCircle(cX, cY, mCircleR, paint);
}
public int getmWeekColor() {
return mWeekColor;
}
public void setmWeekColor(int mWeekColor) {
this.mWeekColor = mWeekColor;
}
public int getmMonthDateColor() {
return mMonthDateColor;
}
public void setmMonthDateColor(int mMonthDateColor) {
this.mMonthDateColor = mMonthDateColor;
}
public int getmSelectableDayColor() {
return mSelectableDayColor;
}
public void setmSelectableDayColor(int mSelectableDayColor) {
this.mSelectableDayColor = mSelectableDayColor;
}
public int getmWeekSize() {
return mWeekSize;
}
public void setmWeekSize(int mWeekSize) {
this.mWeekSize = mWeekSize;
}
public int getmDateSize() {
return mDateSize;
}
public void setmDateSize(int mDateSize) {
this.mDateSize = mDateSize;
}
public float getmCircleR() {
return mCircleR;
}
public void setmCircleR(float mCircleR) {
this.mCircleR = mCircleR;
}
public List<Date> getmSelectableDates() {
return mSelectableDates;
}
public void setmSelectableDates(List<Date> mSelectableDates) {
this.mSelectableDates.clear();
this.mSelectableDates = mSelectableDates;
}
}
二.之前在滑动中还是会遇到卡顿,发现是绘制特殊日期圆环造成的,检查了很久代码,才校验SimpleDateFormat转化比Calendar转换效率低得多,当然几次转换是察觉不到卡顿的,但假如循环多次,肉眼就会察觉到明显的卡顿,即使设置滑动不加载数据,停止时还是需要好几秒才绘制成功。
1.转化2017-11-11这种类型的日期文本使用SimpleDateFormat
2.获取年或月或日还是用Calendar较好