java 日历签到功能_自定义Calendar实现签到功能

本文介绍了一个自定义的Java Calendar组件,用于实现签到功能。讲解了组件的运作流程,包括onMeasure、onLayout和onDraw方法的使用,并展示了如何使用Path对象进行绘制。组件提供了设置签到标记、颜色等功能,并提供了详细的源码注释。
摘要由CSDN通过智能技术生成

前言

这篇文章没有什么可看性,主要是源码注释太多,推荐自己看源码,更容易理解些,在这里主要介绍,其运作流程,贴代码片段。

自定义View要重写三个方法:onMeasure,onLayout,onDraw,这三个方法各有个的作用,onMeasure是对组件的宽高进行测量,onLayout是对子控件的位置进行摆放,onDraw是对自定义控件进行绘制,已经对onMeasure,onLayout方法进行了运用,那个源码注释也很多,如果有兴趣的可以去看看,本章是对onDraw方法进行使用,顺带使用Path对象。

好了,先谈谈为什么我要重复造轮子,要做一个有签到功能的日历,由于自己对自定义的组件ondraw方法还没怎么用过,所以重复造轮子咯,是不是理由不是很充分,没关系,开心就好。

先来张效果图

243728_0.gif

这个CalendarView的API

String clickLeftMonth(); //上一个月 return String(年-月)

String clickRightMonth(); //下一个月 return String(年-月)

Surface getSurface(); //获取整个组件画图对象,可进行设置字体颜色等 return Surface

String getYearAndmonth(); // 获得当前应该显示的年月 return String(年-月)

boolean isSelectMore(); //返回是否多选

setSelectMore(boolean flag);//设置是否多选

setFlagData(String[] flags);//设置要进行标记的数据

setOnItemClickListener(OnItemClickListener); //点击一个日期的回调事件

setWritingFlag(String str); //设置标记字符,默认为签到

OK,先来简述下这个组件跑起来的流程,

1.初始化数据。

2.测量组件大小,即调用了OnMeasure方法

3.调用onDraw方法。

步骤是不是很简单呀?OK,通过源码简单的跑一下流程。

初始化数据

public CalendarView(Context context) {

super(context);

// 初始化数据

init();

}

public CalendarView(Context context, AttributeSet attrs) {

super(context, attrs);

// 初始化数据

init();

}

/**

* 初始化数据 ,初始化事件对象 ,初始化日期格式类对象 ,Surface布局对象初始化 ,获取屏幕密度比例 ,设置View背景 ,设置触摸事件

*/

private void init() {

// 创建一个Date对象并将引用给显示的月,选择开始,选择结束,今天的日期

curDate = selectedStartDate = selectedEndDate = today = new Date();

// 获取一个日期类对象

calendar = Calendar.getInstance();

// 设置日期

calendar.setTime(curDate);

// 创建一个布局路径

surface = new Surface(this);

// 获取屏幕密度比例

surface.density = getResources().getDisplayMetrics().density;

// 给整个控件设置触摸事件

setOnTouchListener(this);

}

这一块看出,在组件进行实例化的时候调用了init方法,然后看见了new Surface() 创建了一个Surface对象。ok来看下这个Surface类,其他的应该都知道是什么。(像我注释这么密的看不懂才怪(*^__^*))。

public void init() {

float temp = height / 7f;// 将整个视图分成了7份,每份所占的高度

monthHeight = 0;// (float) ((temp + temp * 0.3f) * 0.6);

weekHeight = (float) ((temp + temp * 0.3f) * 0.5);

cellHeight = (height - monthHeight - weekHeight) / 6f;

cellWidth = width / 7f;

// 创建一个边框的画笔并设置其属性

borderPaint = new Paint();

borderPaint.setColor(cellBorderColor);

borderPaint.setStyle(Paint.Style.STROKE);

// 边框的宽度

borderWidth = (float) (0.5 * density);

borderWidth = borderWidth < 1 ? 1 : borderWidth;

borderPaint.setStrokeWidth(borderWidth);

// 创建星期画笔并设置其属性

weekPaint = new Paint();

weekPaint.setColor(textWeekColor);

weekPaint.setAntiAlias(true);

float weekTextSize = weekHeight * 0.6f;

weekPaint.setTextSize(weekTextSize);

weekPaint.setTypeface(Typeface.DEFAULT_BOLD);

// 创建时间画笔并设置其属性

datePaint = new Paint();

datePaint.setAntiAlias(true);

float cellTextSize = cellHeight * 0.3f;

datePaint.setTextSize(cellTextSize);

datePaint.setTypeface(Typeface.DEFAULT_BOLD);

// 创建一个Path对象用于记录画笔所画的路径

boxPath = new Path();

// 画第一行,现在起点是0,0

boxPath.rLineTo(width, 0);

// 将起点向下移动一个星期格子的高度

boxPath.moveTo(0, monthHeight + weekHeight);

// 画第二行

boxPath.rLineTo(width, 0);

// 循环画纵线和号数的横线

for (int i = 1; i < 7; i++) {

// 纵线

boxPath.moveTo(i * cellWidth, monthHeight);

boxPath.rLineTo(0, height - monthHeight);

// 横线

boxPath.moveTo(0, monthHeight + weekHeight + i * cellHeight);

boxPath.rLineTo(width, 0);

}

// 表格被选择后使用的画笔

cellBgPaint = new Paint();

cellBgPaint.setAntiAlias(true);

cellBgPaint.setStyle(Paint.Style.FILL);

cellBgPaint.setColor(cellSelectBgColor);

}

其实这个类也没做什么,就一个init方法就是初始化各种画笔,然后动态计算各种高度和宽度。这里面的那个for循环里面的boxPath就是通过path对象记录绘制的表格路径。

ok回到CalendarView类,这个组件被实例化了,就开始进行调用onMeasure方法了。这方法里面没啥可说的就是测量这个组件的大小,确定这个组件需要的宽高是多少如果

onMeasure和onLayout会被执行两次,然后才执行onDraw方法,看下这个onDraw方法。

首先调用了这个calculateDate方法。这个方法是动态计算日期的。

/**

* 计算日期,计算出上月,这月下月的日期装入到一个数组里面进行保存

*/

private void calculateDate() {

calendar.setTime(curDate);

calendar.set(Calendar.DAY_OF_MONTH, 1);

int dayInWeek = calendar.get(Calendar.DAY_OF_WEEK);

Log.d(TAG, "day in week:" + dayInWeek);

int monthStart = dayInWeek;

monthStart -= 1; // 以日为开头-1,以星期一为开头-2

curStartIndex = monthStart;

date[monthStart] = 1;

// last month

if (monthStart > 0) {

calendar.set(Calendar.DAY_OF_MONTH, 0);

int dayInmonth = calendar.get(Calendar.DAY_OF_MONTH);

for (int i = monthStart - 1; i >= 0; i--) {

date[i] = dayInmonth;

dayInmonth--;

}

calendar.set(Calendar.DAY_OF_MONTH, date[0]);

}

showFirstDate = calendar.getTime();

// this month

calendar.setTime(curDate);

calendar.add(Calendar.MONTH, 1);

calendar.set(Calendar.DAY_OF_MONTH, 0);

int monthDay = calendar.get(Calendar.DAY_OF_MONTH);

for (int i = 1; i < monthDay; i++) {

date[monthStart + i] = i + 1;

}

curEndIndex = monthStart + monthDay;

// next month

for (int i = monthStart + monthDay; i < 42; i++) {

date[i] = i - (monthStart + monthDay) + 1;

}

if (curEndIndex < 42) {

// 显示了下一月的

calendar.add(Calendar.DAY_OF_MONTH, 1);

}

calendar.set(Calendar.DAY_OF_MONTH, date[41]);

showLastDate = calendar.getTime();

}

这个方法动态计算日期,显示计算上个月所剩下的日期装入数组date里面,然后装当前月份的,最后装下个月开头部分日期。

为什么会在这个onDraw方法里面调用呢,因为如果在构造方法里面执行一次就没法执行了,如果我点击下一个月那数据就不变了,onMeasure和onLayout都执行两遍所以不行。因此只能在onDraw方法绘制一次,计算一下。

往下看,这段代码是绘制星期天的。

// 画用于分隔显示号数的表格框

canvas.drawPath(surface.boxPath, surface.borderPaint);

// 星期计算

float weekTextY = surface.monthHeight + surface.weekHeight * 3 / 4f;

// 绘制星期1.2.3等字体

for (int i = 0; i < surface.weekText.length; i++) {

float weekTextX = i

* surface.cellWidth

+ (surface.cellWidth - surface.weekPaint

.measureText(surface.weekText[i])) / 2f;

canvas.drawText(surface.weekText[i], weekTextX, weekTextY,

surface.weekPaint);

}

动态计算星期1-7的位置然后在所处位置绘制文字。

再下面就是绘制选择格子的背景颜色,默认是当前月的当前号数。

/**

* @param canvas

*/

private void drawDownOrSelectedBg(Canvas canvas) {

// down and not up

if (downDate != null) {

drawCellBg(canvas, downIndex, surface.cellDownBgColor);

}

// selected bg color

if (!selectedEndDate.before(showFirstDate)

&& !selectedStartDate.after(showLastDate)) {

int[] section = new int[]{-1, -1};

calendar.setTime(curDate);

calendar.add(Calendar.MONTH, -1);

findSelectedIndex(0, curStartIndex, calendar, section);

if (section[1] == -1) {

calendar.setTime(curDate);

findSelectedIndex(curStartIndex, curEndIndex, calendar, section);

}

if (section[1] == -1) {

calendar.setTime(curDate);

calendar.add(Calendar.MONTH, 1);

findSelectedIndex(curEndIndex, 42, calendar, section);

}

if (section[0] == -1) {

section[0] = 0;

}

if (section[1] == -1) {

section[1] = 41;

}

for (int i = section[0]; i <= section[1]; i++) {

drawCellBg(canvas, i, surface.cellSelectBgColor);

}

}

}

后面就是开始绘制日期,即将画出来的表格填充数字。

for (int i = 0; i < num; i++) {

// 这个月的字体颜色

int color = surface.textInstantColor;

if (isLastMonth(i)) {

// 上个月字体颜色

color = surface.textOtherColor;

} else if (isNextMonth(i)) {

// 下个月字体颜色

color = surface.textOtherColor;

} else if (todayIndex != -1) {

// 循环为签到的日期加标记

int flagLen = flagData == null ? 0 : flagData.length;

for (int j = 0; j < flagLen; j++) {

if ((date[i] + "").equals(flagData[j]))

drawCellFlag(canvas, i, surface.textFlagBgColor,

surface.textFlagColor);

}

// 如果todayIndex不等于-1且等于今天

if (i == todayIndex) {

// 今天字体颜色

color = surface.textTodayColor;

}

}

drawCellText(canvas, i, date[i] + "", color);

}

在这值得一提的就是这个添加签到标签的方法drawCellFlag。

/**

* 在格子的右上角进行绘制标签

*

* @param canvas  画布

* @param index 下标

* @param bgcolor 背景颜色

* @param textcolor 字体颜色

*/

private void drawCellFlag(Canvas canvas, int index, int bgcolor,

int textcolor) {

int x = getXByIndex(index);

int y = getYByIndex(index);

// 计算一个方格子的上下左右距离组件边框的距离,以此来推出其坐标

float left = surface.cellWidth * (x - 1) + surface.borderWidth;

float top = surface.monthHeight + surface.weekHeight + (y - 1)

* surface.cellHeight - surface.borderWidth;

float right = left + surface.cellWidth + surface.borderWidth;

float botton = top + surface.cellHeight - surface.borderWidth;

surface.cellBgPaint.setColor(bgcolor);

// 通过Path来记录路径,画一个梯形图

Path path = new Path();

path.moveTo(right - surface.cellWidth * 2 / 3, top);

path.lineTo(right - surface.cellWidth / 4, top);

path.lineTo(right, botton - surface.cellHeight * 3 / 4);

path.lineTo(right, botton - surface.cellHeight / 3);

canvas.drawPath(path, surface.cellBgPaint);

// 因为下面的绘制的文字将要进行旋转因此我将以上Canvas绘制的图案进行保存,这样就不会被旋转给影响到了

canvas.save();

// 将字体进行旋转40度,以文字开始绘制的坐标点进行旋转

canvas.rotate((float) 45, right - surface.cellWidth * 3 / 7, botton

- surface.cellHeight * 5 / 6);

surface.cellBgPaint.setColor(textcolor);

// 动态的计算字体大小

float a = surface.cellWidth / 4;

float b = surface.cellHeight / 4;

float c = (float) Math.sqrt(a * a + b * b);

surface.cellBgPaint.setTextSize(c * 3 / 5);

surface.cellBgPaint.setTypeface(Typeface.DEFAULT_BOLD);

// 绘制文字

canvas.drawText(writingFlag, right - surface.cellWidth * 3 / 7, botton

- surface.cellHeight * 5 / 6, surface.cellBgPaint);

// 释放旋转状态,恢复sava时的状态

canvas.restore();

}

这个方法里面能计算出每个表格的left,right,top,botton的位置,即就可以动态计算梯形四个点,这四个点就是

A(right - surface.cellWidth * 2 / 3, top)

B(right - surface.cellWidth / 4, top)

C(right, botton - surface.cellHeight * 3 / 4)

D(right, botton - surface.cellHeight / 3)

通过Path对象记录这四个点串起来的路径然后canvas绘制就ok了。

而这个标签“签到”的位置也是这样给算出来的。

ok,大概流程讲完了。详细的可以去看源码,里面注释多多,你一定能看懂的。(*^__^*)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: jQuery日历签到插件是一种使用jQuery框架开发的日历插件,它可以帮助开发者在网站或应用中快速添加日历签到功能。通常,这类插件可以让用户在日历中点击签到,并在签到后标记已签到的日期。有很多jQuery日历签到插件可供选择,例如:jquery-simple-datetimepicker,jQuery UI Datepicker 等。 ### 回答2: jQuery日历签到插件是一种用于在网页上创建并管理签到功能的插件。它基于最流行的JavaScript库jQuery开发,具有使用方便、功能强大的特点。 使用jQuery日历签到插件,我们可以轻松地在网站或应用中添加一个日历,让用户进行签到。用户可以通过点击日历中的日期来实现签到操作。插件会自动记录用户的签到历史,让用户可以随时查看过去签到的日期。 该插件提供了丰富的定制化选项,可以根据需求设置签到的样式、颜色、日期格式等。我们可以自定义插件的外观,使其与网站的整体风格保持一致。 在签到过程中,插件会根据用户的签到情况进行更新,例如标记已签到的日期、显示签到天数等。用户可以在日历上清晰地看到自己的签到进度,从而激励自己保持连续签到的动力。 除了基本的签到功能外,该插件还提供了一些有用的扩展功能,例如签到排名、分享到社交媒体等。这些功能可以增加用户的参与度和互动性,使签到活动更加有趣和吸引人。 总之,jQuery日历签到插件是一款方便实用的工具,能够轻松地在网页上添加签到功能,增强用户的参与度和互动性。无论是个人博客、在线商城还是社区论坛,都可以通过该插件为用户提供方便快捷的签到体验。 ### 回答3: jQuery日历签到插件是一种基于jQuery库开发的插件,用于在网站或应用程序中实现日历式的签到功能。这个插件可以方便地帮助用户记录自己的签到情况,并且可以显示签到的日期、统计签到次数等信息。 这个插件具有以下特点: 1. 界面美观:插件提供了精美的界面设计,用户可以直观地看到自己的签到情况。 2. 签到操作简单:用户可以通过点击日历来进行签到操作,无需繁琐的操作步骤。 3. 签到状态显示:已签到的日期会有特殊的标记,用户可以一目了然地知道哪些日期已经签到。 4. 签到记录统计:插件提供了签到次数的统计功能,用户可以知道自己的签到情况和坚持程度。 5. 可定制性强:插件提供了丰富的配置选项,用户可以根据自己的需求进行自定义设置,比如自定义签到图标、签到规则等。 使用这个插件,网站或应用程序可以方便地实现签到功能,提升用户的参与度和黏性。无论是健身签到、学习签到还是其他类型的签到,这个插件都能满足用户的需求。同时,由于基于jQuery开发,插件具有良好的兼容性和扩展性,可以与其他jQuery插件或功能进行集成。 综上所述,jQuery日历签到插件是一款功能强大、操作简单、界面美观的插件,适用于各种类型的签到应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值