本篇博客笔者给大家分享一个日历控件,这里有个需求:要求显示当前月的日期,左右可以切换月份来查看日期。
我们想一想会如何去实现这样的一个控件,有开源的,但可能不太满足我们的特定的需求,这里笔者自定义了一个,读者可以根据自己的需求来修改代码。下面来说一下实现的思路:
首先我们要显示当前月份,自然我们要计算出当前的日期,并且把每一天对应到具体的星期,我们会有以下效果:
我们先想一下这样的效果用什么控件可以实现?很自然可以想到用网格视图GridView,但这里笔者使用的不是GridView, 因为使用GridView可能无法实现那个红色的圈圈,所以笔者决定自定义View,通过绘制来达到这样的效果。
这里我们定于一个日历卡,每一个月代表一个日历卡,我们通过计算每个月的日期,然后根据计算出来的位置绘制我们的数字。
我们知道,一个星期有七天,分别为星期日、星期一、星期二、星期三、星期四、星期五、星期六,这里有7列,一个月至少有28天,最多31天,所以至少应该有6行。组成6*7的方格图。
直接上代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
|
package
com.xiaowu.calendar;
import
android.content.Context;
import
android.graphics.Canvas;
import
android.graphics.Color;
import
android.graphics.Paint;
import
android.util.AttributeSet;
import
android.view.MotionEvent;
import
android.view.View;
import
android.view.ViewConfiguration;
/**
* 自定义日历卡
*
* @author wuwenjie
*
*/
public
class
CalendarCard
extends
View {
private
static
final
int
TOTAL_COL =
7
;
// 7列
private
static
final
int
TOTAL_ROW =
6
;
// 6行
private
Paint mCirclePaint;
// 绘制圆形的画笔
private
Paint mTextPaint;
// 绘制文本的画笔
private
int
mViewWidth;
// 视图的宽度
private
int
mViewHeight;
// 视图的高度
private
int
mCellSpace;
// 单元格间距
private
Row rows[] =
new
Row[TOTAL_ROW];
// 行数组,每个元素代表一行
private
static
CustomDate mShowDate;
// 自定义的日期,包括year,month,day
private
OnCellClickListener mCellClickListener;
// 单元格点击回调事件
private
int
touchSlop;
//
private
boolean
callBackCellSpace;
private
Cell mClickCell;
private
float
mDownX;
private
float
mDownY;
/**
* 单元格点击的回调接口
*
* @author wuwenjie
*
*/
public
interface
OnCellClickListener {
void
clickDate(CustomDate date);
// 回调点击的日期
void
changeDate(CustomDate date);
// 回调滑动ViewPager改变的日期
}
public
CalendarCard(Context context, AttributeSet attrs,
int
defStyleAttr) {
super
(context, attrs, defStyleAttr);
init(context);
}
public
CalendarCard(Context context, AttributeSet attrs) {
super
(context, attrs);
init(context);
}
public
CalendarCard(Context context) {
super
(context);
init(context);
}
public
CalendarCard(Context context, OnCellClickListener listener) {
super
(context);
this
.mCellClickListener = listener;
init(context);
}
private
void
init(Context context) {
mTextPaint =
new
Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaint =
new
Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaint.setStyle(Paint.Style.FILL);
mCirclePaint.setColor(Color.parseColor(#F24949));
// 红色圆形
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
initDate();
}
private
void
initDate() {
mShowDate =
new
CustomDate();
fillDate();
//
}
private
void
fillDate() {
int
monthDay = DateUtil.getCurrentMonthDay();
// 今天
int
lastMonthDays = DateUtil.getMonthDays(mShowDate.year,
mShowDate.month -
1
);
// 上个月的天数
int
currentMonthDays = DateUtil.getMonthDays(mShowDate.year,
mShowDate.month);
// 当前月的天数
int
firstDayWeek = DateUtil.getWeekDayFromDate(mShowDate.year,
mShowDate.month);
boolean
isCurrentMonth =
false
;
if
(DateUtil.isCurrentMonth(mShowDate)) {
isCurrentMonth =
true
;
}
int
day =
0
;
for
(
int
j =
0
; j < TOTAL_ROW; j++) {
rows[j] =
new
Row(j);
for
(
int
i =
0
; i < TOTAL_COL; i++) {
int
position = i + j * TOTAL_COL;
// 单元格位置
// 这个月的
if
(position >= firstDayWeek
&& position < firstDayWeek + currentMonthDays) {
day++;
rows[j].cells[i] =
new
Cell(CustomDate.modifiDayForObject(
mShowDate, day), State.CURRENT_MONTH_DAY, i, j);
// 今天
if
(isCurrentMonth && day == monthDay ) {
CustomDate date = CustomDate.modifiDayForObject(mShowDate, day);
rows[j].cells[i] =
new
Cell(date, State.TODAY, i, j);
}
if
(isCurrentMonth && day > monthDay) {
// 如果比这个月的今天要大,表示还没到
rows[j].cells[i] =
new
Cell(
CustomDate.modifiDayForObject(mShowDate, day),
State.UNREACH_DAY, i, j);
}
// 过去一个月
}
else
if
(position < firstDayWeek) {
rows[j].cells[i] =
new
Cell(
new
CustomDate(mShowDate.year,
mShowDate.month -
1
, lastMonthDays
- (firstDayWeek - position -
1
)),
State.PAST_MONTH_DAY, i, j);
// 下个月
}
else
if
(position >= firstDayWeek + currentMonthDays) {
rows[j].cells[i] =
new
Cell((
new
CustomDate(mShowDate.year,
mShowDate.month +
1
, position - firstDayWeek
- currentMonthDays +
1
)),
State.NEXT_MONTH_DAY, i, j);
}
}
}
mCellClickListener.changeDate(mShowDate);
}
@Override
protected
void
onDraw(Canvas canvas) {
super
.onDraw(canvas);
for
(
int
i =
0
; i < TOTAL_ROW; i++) {
if
(rows[i] !=
null
) {
rows[i].drawCells(canvas);
}
}
}
@Override
protected
void
onSizeChanged(
int
w,
int
h,
int
oldw,
int
oldh) {
super
.onSizeChanged(w, h, oldw, oldh);
mViewWidth = w;
mViewHeight = h;
mCellSpace = Math.min(mViewHeight / TOTAL_ROW, mViewWidth / TOTAL_COL);
if
(!callBackCellSpace) {
callBackCellSpace =
true
;
}
mTextPaint.setTextSize(mCellSpace /
3
);
}
@Override
public
boolean
onTouchEvent(MotionEvent event) {
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
mDownX = event.getX();
mDownY = event.getY();
break
;
case
MotionEvent.ACTION_UP:
float
disX = event.getX() - mDownX;
float
disY = event.getY() - mDownY;
if
(Math.abs(disX) < touchSlop && Math.abs(disY) < touchSlop) {
int
col = (
int
) (mDownX / mCellSpace);
int
row = (
int
) (mDownY / mCellSpace);
measureClickCell(col, row);
}
break
;
default
:
break
;
}
return
true
;
}
/**
* 计算点击的单元格
* @param col
* @param row
*/
private
void
measureClickCell(
int
col,
int
row) {
if
(col >= TOTAL_COL || row >= TOTAL_ROW)
return
;
if
(mClickCell !=
null
) {
rows[mClickCell.j].cells[mClickCell.i] = mClickCell;
}
if
(rows[row] !=
null
) {
mClickCell =
new
Cell(rows[row].cells[col].date,
rows[row].cells[col].state, rows[row].cells[col].i,
rows[row].cells[col].j);
CustomDate date = rows[row].cells[col].date;
date.week = col;
mCellClickListener.clickDate(date);
// 刷新界面
update();
}
}
/**
* 组元素
*
* @author wuwenjie
*
*/
class
Row {
public
int
j;
Row(
int
j) {
this
.j = j;
}
public
Cell[] cells =
new
Cell[TOTAL_COL];
// 绘制单元格
public
void
drawCells(Canvas canvas) {
for
(
int
i =
0
; i < cells.length; i++) {
if
(cells[i] !=
null
) {
cells[i].drawSelf(canvas);
}
}
}
}
/**
* 单元格元素
*
* @author wuwenjie
*
*/
class
Cell {
public
CustomDate date;
public
State state;
public
int
i;
public
int
j;
public
Cell(CustomDate date, State state,
int
i,
int
j) {
super
();
this
.date = date;
this
.state = state;
this
.i = i;
this
.j = j;
}
public
void
drawSelf(Canvas canvas) {
switch
(state) {
case
TODAY:
// 今天
mTextPaint.setColor(Color.parseColor(#fffffe));
canvas.drawCircle((
float
) (mCellSpace * (i +
0.5
)),
(
float
) ((j +
0.5
) * mCellSpace), mCellSpace /
3
,
mCirclePaint);
break
;
case
CURRENT_MONTH_DAY:
// 当前月日期
mTextPaint.setColor(Color.BLACK);
break
;
case
PAST_MONTH_DAY:
// 过去一个月
case
NEXT_MONTH_DAY:
// 下一个月
mTextPaint.setColor(Color.parseColor(#fffffe));
break
;
case
UNREACH_DAY:
// 还未到的天
mTextPaint.setColor(Color.GRAY);
break
;
default
:
break
;
}
// 绘制文字
String content = date.day + ;
canvas.drawText(content,
(
float
) ((i +
0.5
) * mCellSpace - mTextPaint
.measureText(content) /
2
), (
float
) ((j +
0.7
)
* mCellSpace - mTextPaint
.measureText(content,
0
,
1
) /
2
), mTextPaint);
}
}
/**
*
* @author wuwenjie 单元格的状态 当前月日期,过去的月的日期,下个月的日期
*/
enum
State {
TODAY,CURRENT_MONTH_DAY, PAST_MONTH_DAY, NEXT_MONTH_DAY, UNREACH_DAY;
}
// 从左往右划,上一个月
public
void
leftSlide() {
if
(mShowDate.month ==
1
) {
mShowDate.month =
12
;
mShowDate.year -=
1
;
}
else
{
mShowDate.month -=
1
;
}
update();
}
// 从右往左划,下一个月
public
void
rightSlide() {
if
(mShowDate.month ==
12
) {
mShowDate.month =
1
;
mShowDate.year +=
1
;
}
else
{
mShowDate.month +=
1
;
}
update();
}
public
void
update() {
fillDate();
invalidate();
}
}
|
/CustomCalendarView/src/com/xiaowu/calendar/DateUtil.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
package
com.xiaowu.calendar;
import
android.annotation.SuppressLint;
import
android.util.Log;
import
java.text.ParseException;
import
java.text.SimpleDateFormat;
import
java.util.Calendar;
import
java.util.Date;
import
java.util.GregorianCalendar;
public
class
DateUtil {
public
static
String[] weekName = { 周日, 周一, 周二, 周三, 周四, 周五,周六 };
public
static
int
getMonthDays(
int
year,
int
month) {
if
(month >
12
) {
month =
1
;
year +=
1
;
}
else
if
(month <
1
) {
month =
12
;
year -=
1
;
}
int
[] arr = {
31
,
28
,
31
,
30
,
31
,
30
,
31
,
31
,
30
,
31
,
30
,
31
};
int
days =
0
;
if
((year %
4
==
0
&& year %
100
!=
0
) || year %
400
==
0
) {
arr[
1
] =
29
;
// 闰年2月29天
}
try
{
days = arr[month -
1
];
}
catch
(Exception e) {
e.getStackTrace();
}
return
days;
}
public
static
int
getYear() {
return
Calendar.getInstance().get(Calendar.YEAR);
}
public
static
int
getMonth() {
return
Calendar.getInstance().get(Calendar.MONTH) +
1
;
}
public
static
int
getCurrentMonthDay() {
return
Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
}
public
static
int
getWeekDay() {
return
Calendar.getInstance().get(Calendar.DAY_OF_WEEK);
}
public
static
int
getHour() {
return
Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
}
public
static
int
getMinute() {
return
Calendar.getInstance().get(Calendar.MINUTE);
}
public
static
CustomDate getNextSunday() {
Calendar c = Calendar.getInstance();
c.add(Calendar.DATE,
7
- getWeekDay()+
1
);
CustomDate date =
new
CustomDate(c.get(Calendar.YEAR),
c.get(Calendar.MONTH)+
1
, c.get(Calendar.DAY_OF_MONTH));
return
date;
}
public
static
int
[] getWeekSunday(
int
year,
int
month,
int
day,
int
pervious) {
int
[] time =
new
int
[
3
];
Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, month);
c.set(Calendar.DAY_OF_MONTH, day);
c.add(Calendar.DAY_OF_MONTH, pervious);
time[
0
] = c.get(Calendar.YEAR);
time[
1
] = c.get(Calendar.MONTH )+
1
;
time[
2
] = c.get(Calendar.DAY_OF_MONTH);
return
time;
}
public
static
int
getWeekDayFromDate(
int
year,
int
month) {
Calendar cal = Calendar.getInstance();
cal.setTime(getDateFromString(year, month));
int
week_index = cal.get(Calendar.DAY_OF_WEEK) -
1
;
if
(week_index <
0
) {
week_index =
0
;
}
return
week_index;
}
@SuppressLint
(SimpleDateFormat)
public
static
Date getDateFromString(
int
year,
int
month) {
String dateString = year + - + (month >
9
? month : (
0
+ month))
+ -
01
;
Date date =
null
;
try
{
SimpleDateFormat sdf =
new
SimpleDateFormat(yyyy-MM-dd);
date = sdf.parse(dateString);
}
catch
(ParseException e) {
System.out.println(e.getMessage());
}
return
date;
}
public
static
boolean
isToday(CustomDate date){
return
(date.year == DateUtil.getYear() &&
date.month == DateUtil.getMonth()
&& date.day == DateUtil.getCurrentMonthDay());
}
public
static
boolean
isCurrentMonth(CustomDate date){
return
(date.year == DateUtil.getYear() &&
date.month == DateUtil.getMonth());
}
}
|
/CustomCalendarView/src/com/xiaowu/calendar/CustomDate.java、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
package
com.xiaowu.calendar;
import
java.io.Serializable;
public
class
CustomDate
implements
Serializable{
private
static
final
long
serialVersionUID = 1L;
public
int
year;
public
int
month;
public
int
day;
public
int
week;
public
CustomDate(
int
year,
int
month,
int
day){
if
(month >
12
){
month =
1
;
year++;
}
else
if
(month <
1
){
month =
12
;
year--;
}
this
.year = year;
this
.month = month;
this
.day = day;
}
public
CustomDate(){
this
.year = DateUtil.getYear();
this
.month = DateUtil.getMonth();
this
.day = DateUtil.getCurrentMonthDay();
}
public
static
CustomDate modifiDayForObject(CustomDate date,
int
day){
CustomDate modifiDate =
new
CustomDate(date.year,date.month,day);
return
modifiDate;
}
@Override
public
String toString() {
return
year+-+month+-+day;
}
public
int
getYear() {
return
year;
}
public
void
setYear(
int
year) {
this
.year = year;
}
public
int
getMonth() {
return
month;
}
public
void
setMonth(
int
month) {
this
.month = month;
}
public
int
getDay() {
return
day;
}
public
void
setDay(
int
day) {
this
.day = day;
}
public
int
getWeek() {
return
week;
}
public
void
setWeek(
int
week) {
this
.week = week;
}
}
|