0%

周日历与月日历的实现


最近公司的项目有个这样的需求,在页面的顶部显示周日历,可左右水平滑动切换当前显示的周,默认选择日为今日,也可以点击选择查看日,查看当日的行程列表,显示在界面下方。周日历的左侧有个按钮可点击进入月日历,自动定位到当前选择日,月日历是可垂直滑动查看的,两个日历的可查看范围为当前月的前3个月和后6个月,都需要在有行程安排的日期下绘制行字。大概整理一下思路,周日历可采用自定义viewpager来实现,上部分的星期几标识可使用gridview来实现。月日历可采用自定义的recyclerview来实现,每个月为一个item,每个item为一个自定义的view,采用canvas来绘制。


最终效果图gif:
image

weekCalendar的自定义实现

自定义LinearLayout

顶部的星期几采用gridview来实现,然后动态添加到自定义的LinearLayout,代码如下:

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
private GridView getDaysNames() {

daysName = new GridView(getContext());
daysName.setSelector(new StateListDrawable());
daysName.setNumColumns(7);

daysName.setAdapter(new BaseAdapter() {
private String[] days = {"日","一","二","三","四","五","六"};

public int getCount() {
return days.length;
}

@Override
public String getItem(int position) {
return days[position];
}

@Override
public long getItemId(int position) {
return 0;
}

@SuppressLint("InflateParams")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.week_day_grid_item, null);
}
TextView day = (TextView) convertView.findViewById(R.id.daytext);
day.setText(days[position]);
if (typedArray != null) {
day.setTextColor(typedArray.getColor(R.styleable.WeekCalendar_weekTextColor,
Color.WHITE));
day.setTextSize(TypedValue.COMPLEX_UNIT_PX, typedArray.getDimension(R.styleable
.WeekCalendar_weekTextSize, day.getTextSize()));
}
return convertView;
}
});
return daysName;
}

在控件初始化时调用,代码如下:

1
2
3
daysName = getDaysNames();
daysName.setGravity(Gravity.CENTER);
addView(daysName);

还需要暴露一些其它的接口,以及自定义的属性设置

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
 private void init(AttributeSet attrs) {
if (attrs != null) {
typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.WeekCalendar);
int selectedBgColor = typedArray.getColor(R.styleable
.WeekCalendar_selectedBgColor_week, ContextCompat.getColor(getContext(), R.color
.colorAccent));
int dayTextColorPre = typedArray.getColor(R.styleable
.WeekCalendar_previousTextColor_week, Color.WHITE);
int dayTextColorNormal = typedArray.getColor(R.styleable.WeekCalendar_normalTextColor_week,ContextCompat.getColor(getContext(),R.color.default_light_gray));
float daysTextSize = typedArray.getDimension(R.styleable
.WeekCalendar_dayTextSize_week, -1);
int todayDateTextColor = typedArray.getColor(R.styleable
.WeekCalendar_todayTextColor_week, ContextCompat.getColor(getContext(), R.color.default_blue));
int selectedTextColor = typedArray.getColor(R.styleable
.WeekCalendar_selectedTextColor_week, ContextCompat.getColor(getContext(), R.color.white));
int flagPreBgColor=typedArray.getColor(R.styleable.WeekCalendar_flagPreBgColor_week,ContextCompat.getColor(getContext(), R.color.default_light_gray));
int flagNormalBgColor=typedArray.getColor(R.styleable.WeekCalendar_flagNormalBgColor_week,ContextCompat.getColor(getContext(), R.color.default_orange));
int flagTextColor=typedArray.getColor(R.styleable.WeekCalendar_flagTextColor_week,ContextCompat.getColor(getContext(), R.color.white));
String flagTextStr=typedArray.getString(R.styleable.WeekCalendar_flagTextStr_week);
if(TextUtils.isEmpty(flagTextStr)){
flagTextStr="行";
}
boolean drawRoundRect = typedArray.getBoolean(R.styleable.WeekCalendar_isRoundRect_weekview,false);
setDayDecorator(new DefaultDayDecorator(getContext(),
selectedBgColor,
selectedTextColor,
todayDateTextColor,
dayTextColorPre,
dayTextColorNormal,
flagPreBgColor,
flagNormalBgColor,
flagTextColor,
flagTextStr,
daysTextSize,
drawRoundRect));
}
setOrientation(VERTICAL);

if (!typedArray.getBoolean(R.styleable.WeekCalendar_hideWeekNum, false)) {
daysName = getDaysNames();
daysName.setGravity(Gravity.CENTER);
addView(daysName);
}
RelativeLayout.LayoutParams lpWeek = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,DensityUtil.dip2px(getContext(),92)- DensityUtil.dip2px(getContext(),92)/3);
lpWeek.addRule(RelativeLayout.CENTER_IN_PARENT);
WeekPager weekPager = new WeekPager(getContext(), attrs);
addView(weekPager,lpWeek);
}

@Subscribe
public void onDateClick(Event.OnDateClickEvent event) {
if (listener != null)
listener.onDateClick(event.getDateTime());
}

@Subscribe
public void onDayDecorate(Event.OnDayDecorateEvent event) {
if (dayDecorator != null) {
dayDecorator.decorate(event.getView(), event.getDayTextView(), event.getDateTime(),
event.getFirstDay(), event.getSelectedDateTime());
dayDecorator.drawFlag(event.getFlagText(),event.getDateTime(),flagDates);
}
}

@Subscribe
public void onWeekChange(Event.OnWeekChange event) {
if (onWeekChangeListener != null) {
onWeekChangeListener.onWeekChange(event.getFirstDayOfTheWeek(), event.isForward());
}
}

public void setOnDateClickListener(OnDateClickListener listener) {
this.listener = listener;
}

public void setDayDecorator(DayDecorator decorator) {
this.dayDecorator = decorator;
}

public void setOnWeekChangeListener(OnWeekChangeListener onWeekChangeListener) {
this.onWeekChangeListener = onWeekChangeListener;
}

这里的DayDecorator接口是管理每一个item的绘制,包括标签绘制和日期绘制,代码如下:

1
2
3
4
public interface DayDecorator {
void decorate(View view, TextView dayTextView, DateTime dateTime, DateTime firstDayOfTheWeek, DateTime selectedDateTime);
void drawFlag(TextView flagText, DateTime dateTime, List<String> flagDates);
}

具体实现代码如下:

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
public class DefaultDayDecorator implements DayDecorator {

private Context context;
private final int selectedBgColor;
private final int selectedTextColor;
private int todayDateTextColor;
private int textColorPre;
private int textColorNormal;
private float textSize;
private boolean drawRoundRect;
private String flagTextStr;
private int flagPreBgColor;
private int flagNormalBgColor;
private int flagTextColor;

public DefaultDayDecorator(Context context,
@ColorInt int selectedBgColor,
@ColorInt int selectedTextColor,
@ColorInt int todayDateTextColor,
@ColorInt int textColorPre,
@ColorInt int textColorNormal,
@ColorInt int flagPreBgColor,
@ColorInt int flagNormalBgColor,
@ColorInt int flagTextColor,
String flagTextStr,
float textSize,
boolean drawRoundRect) {
this.context = context;
this.selectedBgColor = selectedBgColor;
this.selectedTextColor = selectedTextColor;
this.todayDateTextColor = todayDateTextColor;
this.textColorPre = textColorPre;
this.textColorNormal = textColorNormal;
this.textSize = textSize;
this.drawRoundRect = drawRoundRect;
this.flagPreBgColor=flagPreBgColor;
this.flagNormalBgColor=flagNormalBgColor;
this.flagTextColor=flagTextColor;
this.flagTextStr=flagTextStr;
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void decorate(View view, TextView dayTextView,
DateTime dateTime, DateTime firstDayOfTheWeek, DateTime selectedDateTime) {
//DateTime dt = new DateTime();
Drawable todayShape,selectShape;
if(drawRoundRect){
todayShape = ContextCompat.getDrawable(context, R.drawable.today_rect);
selectShape = ContextCompat.getDrawable(context, R.drawable.select_rect);
}else{
todayShape = ContextCompat.getDrawable(context, R.drawable.today_circle);
selectShape = ContextCompat.getDrawable(context, R.drawable.select_circle);
}
selectShape.setColorFilter(selectedBgColor, PorterDuff.Mode.SRC_ATOP);
todayShape.setColorFilter(todayDateTextColor, PorterDuff.Mode.SRC_ATOP);

// solidCircle.mutate().setAlpha(200);
//holoCircle.mutate().setAlpha(200);

DateTime calendarStartDate = DateTime.now();
if (selectedDateTime != null && !selectedDateTime.toLocalDate().equals(dateTime.toLocalDate())) { //当前时间未选中
if (dateTime.toLocalDate().isBefore(calendarStartDate.toLocalDate())) {//在今天之前
dayTextView.setTextColor(textColorPre);
dayTextView.setBackground(null);
} else if (dateTime.toLocalDate().isAfter(calendarStartDate.toLocalDate())) {//今天之后
dayTextView.setTextColor(textColorNormal);
dayTextView.setBackground(null);
} else {//就是今天
dayTextView.setBackground(todayShape);
dayTextView.setTextColor(todayDateTextColor);
}
} else if (selectedDateTime != null && selectedDateTime.toLocalDate().equals(dateTime.toLocalDate())) {//当前时间选中
dayTextView.setBackground(selectShape);
dayTextView.setTextColor(selectedTextColor);
}
float size = textSize;
if (size == -1)
size = dayTextView.getTextSize();
Log.v("jessie", dayTextView.getText().toString());
dayTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
}

@Override
public void drawFlag(TextView flagText, DateTime dateTime, List<String> flagDates) {
if (flagDates != null) {
Iterator<String> it = flagDates.iterator();
DateTime calendarStartDate = DateTime.now();
while (it.hasNext()) {
String date = it.next();
String curDate = OtherUtils.formatDate(dateTime.toDate(), "yyyy-MM-dd");
if (curDate.equals(date)) {//当前为目标日期
flagText.setText(flagTextStr);
flagText.setTextColor(flagTextColor);
if (dateTime.toLocalDate().isBefore(calendarStartDate.toLocalDate())) {//已经过了
flagText.setBackgroundColor(flagPreBgColor);
} else {//还没有过
flagText.setBackgroundColor(flagNormalBgColor);
}
flagText.setVisibility(View.VISIBLE);

}
}
}
}
}

自定义viewpager

左右水平滑动切换当前显示的周的效果可用自定义的viewpager来实现,暴露一些设置当前页,选择日期,重置到今天的接口

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
public class WeekPager extends ViewPager {
private PagerAdapter adapter;
private int pos;
private boolean check;
public static int NUM_OF_PAGES;
private TypedArray typedArray;


public WeekPager(Context context) {
super(context);
initialize(null);
}

public WeekPager(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(attrs);
}

@Override
public void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
post(new Runnable() {
@Override
public void run() {
//Force rerendering so the week is drawn again when you return to the view after
// back button press.
if (adapter != null) {
adapter.notifyDataSetChanged();
}
}
});
}

private void initialize(AttributeSet attrs) {
if (attrs != null) {
typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.WeekCalendar);
// NUM_OF_PAGES = typedArray.getInt(R.styleable.WeekCalendar_numOfPages, 100);
NUM_OF_PAGES= DateUtil.countPageNum();
}
setId(idCheck());
if (!isInEditMode()) {
initPager(new DateTime());
BusProvider.getInstance().register(this);
}
}


private void initPager(DateTime dateTime) {
// pos = NUM_OF_PAGES / 2;
pos = DateUtil.countCurPage();
adapter = new PagerAdapter(((FragmentActivity) getContext())
.getSupportFragmentManager(), dateTime);
setAdapter(adapter);
addOnPageChangeListener(new ViewPager
.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
if (!check)
if (position < pos){
adapter.swipeBack();
}
else if (position > pos){
adapter.swipeForward();
}

pos = position;
check = false;

}

});
setOverScrollMode(OVER_SCROLL_NEVER);
setCurrentItem(pos);
if (WeekFragment.selectedDateTime == null)
WeekFragment.selectedDateTime = new DateTime();
}

@Subscribe
public void setCurrentPage(Event.SetCurrentPageEvent event) {
check = true;
if (event.getDirection() == 1)
adapter.swipeForward();
else
adapter.swipeBack();
setCurrentItem(getCurrentItem() + event.getDirection());

}

@Subscribe
public void reset(Event.ResetEvent event) {
WeekFragment.selectedDateTime = DateTime.now();
//WeekFragment.CalendarStartDate = new DateTime();
initPager(DateTime.now());
}

@Subscribe
public void setSelectedDate(Event.SetSelectedDateEvent event) {
WeekFragment.selectedDateTime = event.getSelectedDate();
initPager(event.getSelectedDate());
}

@Subscribe
public void setStartDate(Event.SetStartDateEvent event) {
WeekFragment.selectedDateTime = event.getStartDate();
initPager(event.getStartDate());
}

private int idCheck() {
int id = 0;
while (true) {
if (findViewById(++id) == null) break;
}
return id;
}
}

自定义FragmentStatePagerAdapter

这里的viewpager需要一个自定的FragmentStatePagerAdapter来实现,每个item是一个自定义的fragment,滑动的时候通过evenbus调用页面翻转的接口

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
public class PagerAdapter extends FragmentStatePagerAdapter {
private static final String TAG = "PagerAdapter";
// private int currentPage = NUM_OF_PAGES / 2;
private int currentPage= DateUtil.countCurPage();
private DateTime date;
public PagerAdapter(FragmentManager fm, DateTime date) {
super(fm);
this.date = date;
}

@Override
public Fragment getItem(int position) {
WeekFragment fragment = new WeekFragment();
Bundle bundle = new Bundle();

if (position < currentPage)
bundle.putSerializable(DATE_KEY, getPerviousDate());
else if (position > currentPage)
bundle.putSerializable(DATE_KEY, getNextDate());
else
bundle.putSerializable(DATE_KEY, getTodaysDate());

fragment.setArguments(bundle);
return fragment;
}

@Override
public int getCount() {
return NUM_OF_PAGES;
}

private DateTime getTodaysDate() {
return date;
}

private DateTime getPerviousDate() {
return date.plusDays(-7);
}

private DateTime getNextDate() {
return date.plusDays(7);
}

@Override
public int getItemPosition(Object object) {
//Force rerendering so the week is drawn again when you return to the view after
// back button press.
return POSITION_NONE;
}

public void swipeBack() {
date = date.plusDays(-7);
currentPage--;
currentPage = currentPage <= 1 ? NUM_OF_PAGES - 1 : currentPage;
BusProvider.getInstance().post(
new Event.OnWeekChange(date.withDayOfWeek(DateTimeConstants.MONDAY), false));
}

public void swipeForward() {
date = date.plusDays(7);
currentPage++;
currentPage = currentPage >= NUM_OF_PAGES - 1 ? 1 : currentPage;
BusProvider.getInstance().post(
new Event.OnWeekChange(date.withDayOfWeek(DateTimeConstants.MONDAY), true));

}

}

内嵌fragment与对应的adapter

内嵌的fragment,是一个gridview的布局, 对应的adapter代码如下,绘制的时候将控件通过evenbus传到我们自定义的layout里面做处理:

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
public class WeekFragment extends Fragment {
public static String DATE_KEY = "date_key";
private GridView gridView;
private WeekAdapter weekAdapter;
public static DateTime selectedDateTime = new DateTime();
private DateTime startDate;
private DateTime endDate;
private boolean isVisible;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_week, container, false);
gridView = (GridView) rootView.findViewById(R.id.gridView);
init();
return rootView;
}

private void init() {
ArrayList<DateTime> days = new ArrayList<>();
DateTime midDate = (DateTime) getArguments().getSerializable(DATE_KEY);
if (midDate != null) {
midDate = midDate.withDayOfWeek(DateTimeConstants.THURSDAY);
}
//Getting all seven days

for (int i = -4; i <= 2; i++){
DateTime dateTime=midDate.plusDays(i);
days.add(midDate != null ?dateTime : null);

}



startDate = days.get(0);
endDate = days.get(days.size() - 1);

weekAdapter = new WeekAdapter(getActivity(), days);
gridView.setAdapter(weekAdapter);

gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
BusProvider.getInstance().post(new Event.OnDateClickEvent(weekAdapter.getItem
(position)));
selectedDateTime = weekAdapter.getItem(position);
BusProvider.getInstance().post(new Event.InvalidateEvent());
}
});
}

@Subscribe
public void updateSelectedDate(Event.UpdateSelectedDateEvent event) {
if (isVisible) {
selectedDateTime = selectedDateTime.plusDays(event.getDirection());
if (selectedDateTime.toLocalDate().equals(endDate.plusDays(1).toLocalDate())
|| selectedDateTime.toLocalDate().equals(startDate.plusDays(-1).toLocalDate())) {
if (!(selectedDateTime.toLocalDate().equals(startDate.plusDays(-1).toLocalDate()) &&
event.getDirection() == 1)
&& !(selectedDateTime.toLocalDate().equals(endDate.plusDays(1)
.toLocalDate()) && event.getDirection() == -1))
BusProvider.getInstance().post(new Event.SetCurrentPageEvent(event.getDirection()));
}
BusProvider.getInstance().post(new Event.InvalidateEvent());
}
}


@Subscribe
public void invalidate(Event.InvalidateEvent event) {
gridView.invalidateViews();
}

@Subscribe
public void updateUi(Event.OnUpdateUi event) {
weekAdapter.notifyDataSetChanged();
}

@Override
public void onStart() {
BusProvider.getInstance().register(this);
super.onStart();
}

@Override
public void onStop() {
BusProvider.getInstance().unregister(this);
super.onStop();
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
isVisible = isVisibleToUser;
super.setUserVisibleHint(isVisibleToUser);
}

public class WeekAdapter extends BaseAdapter {
private ArrayList<DateTime> days;
private Context context;
private DateTime firstDay;

WeekAdapter(Context context, ArrayList<DateTime> days) {
this.days = days;
this.context = context;
}

@Override
public int getCount() {
return days.size();
}

@Override
public DateTime getItem(int position) {
return days.get(position);
}

@Override
public long getItemId(int position) {
return 0;
}

@SuppressLint("InflateParams")
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public View getView(int position, View convertView, ViewGroup parent) {

if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(R.layout.grid_item, null);
firstDay = getItem(0);
}

DateTime dateTime = getItem(position).withMillisOfDay(0);

TextView dayTextView = (TextView) convertView.findViewById(R.id.daytext);
dayTextView.setText(String.valueOf(dateTime.getDayOfMonth()));
TextView textflag= (TextView) convertView.findViewById(R.id.textflag);
Log.v("jessie","传过去的"+dateTime.getDayOfMonth() );
BusProvider.getInstance().post(new Event.OnDayDecorateEvent(convertView, dayTextView,
dateTime, firstDay, WeekFragment.selectedDateTime,textflag));
return convertView;
}
}

item的布局grid_item,很简单,代码如下:

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">

<TextView
android:gravity="center"
android:id="@+id/daytext"
android:layout_width="@dimen/day_text_wh"
android:layout_height="@dimen/day_text_wh"
android:text="20"
android:textColor="@color/default_black"
android:textSize="17sp"/>

<TextView
android:visibility="gone"
android:id="@+id/textflag"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginTop="4dp"
android:gravity="center"
android:background="@color/default_orange"
android:text="行"
android:textColor="@color/white"
android:textSize="12sp"
/>
</LinearLayout>

自定义MonthCalendar的实现

自定义RecyclerView

将自定义的属性值传到adapter中对SimpleMonthView做自定义,这里的SimpleMonthView就是我们自定义的日历item,绘制每个月日历。

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
public class MonthCalendarView extends RecyclerView {
protected Context mContext;
protected MonthAdapter mAdapter;
private MonthCalendarController mController;
protected int mCurrentScrollState = 0;
protected long mPreviousScrollPosition;
protected int mPreviousScrollState = 0;
private TypedArray typedArray;
private OnScrollListener onScrollListener;
private LinearLayoutManager linearLayoutManager;
public MonthCalendarView(Context context) {
this(context, null);
}

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

public MonthCalendarView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (!isInEditMode()) {
typedArray = context.obtainStyledAttributes(attrs, R.styleable.MonthCalendarView);
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
init(context);
}
}

public void setController(MonthCalendarController mController) {
this.mController = mController;
setUpAdapter();
setAdapter(mAdapter);
}


public int getStartYear(){
return DateUtil.getStartYear();
}

public LinearLayoutManager getLinearLayoutManager(){
return linearLayoutManager;
}

public void init(Context paramContext) {
linearLayoutManager=new LinearLayoutManager(paramContext);
setLayoutManager(linearLayoutManager);
mContext = paramContext;
setUpListView();

onScrollListener = new OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
final MonthView child = (MonthView) recyclerView.getChildAt(0);
if (child == null) {
return;
}

mPreviousScrollPosition = dy;
mPreviousScrollState = mCurrentScrollState;
}
};
}

public void setFlagDates(List<String> dates){
mAdapter.setFlagDates(dates);
}

public void setSelected(CalendarDay calendarDay){
mAdapter.setSelectedDay(calendarDay);
mAdapter.notifyDataSetChanged();
}

protected void setUpAdapter() {
if (mAdapter == null) {
mAdapter = new MonthAdapter(getContext(), mController, typedArray);
}
mAdapter.notifyDataSetChanged();
}

protected void setUpListView() {
setVerticalScrollBarEnabled(false);
setOnScrollListener(onScrollListener);
setFadingEdgeLength(0);
}

}

自定义recyclerview的adapter

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
public class SimpleMonthAdapter extends RecyclerView.Adapter<SimpleMonthAdapter.ViewHolder> implements SimpleMonthView.OnDayClickListener {
protected static final int MONTHS_IN_YEAR = 12;
private final TypedArray typedArray;
private final Context mContext;
private final DatePickerController mController;
private final Calendar calendar;
private CalendarDay selectedDay;
private final Integer firstMonth;
private final Integer lastMonth;
private List<String> dates;
private int count= DateUtil.getPreMonthNum()+DateUtil.getNextMonthNum()+1;


public SimpleMonthAdapter(Context context, DatePickerController datePickerController, TypedArray typedArray) {
this.typedArray = typedArray;
calendar = Calendar.getInstance();
firstMonth = getFirstMonth();
lastMonth = getLastMonth();
mContext = context;
mController = datePickerController;
init();
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
final SimpleMonthView simpleMonthView = new SimpleMonthView(mContext, typedArray);
return new ViewHolder(simpleMonthView, this);
}

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
final SimpleMonthView v = viewHolder.simpleMonthView;
final HashMap<String, Integer> drawingParams = new HashMap<String, Integer>();
int month;
int year;

month = (firstMonth + (position % MONTHS_IN_YEAR)) % MONTHS_IN_YEAR;
year = position / MONTHS_IN_YEAR + getStartYear() + ((firstMonth + (position % MONTHS_IN_YEAR)) / MONTHS_IN_YEAR);

int selectedFirstDay = (selectedDay==null?-1:selectedDay.day);
int selectedFirstMonth = (selectedDay==null?-1:selectedDay.month);
int selectedFirstYear = (selectedDay==null?-1:selectedDay.year);

v.reuse();
drawingParams.put(SimpleMonthView.VIEW_PARAMS_SELECTED_BEGIN_YEAR, selectedFirstYear);
drawingParams.put(SimpleMonthView.VIEW_PARAMS_SELECTED_BEGIN_MONTH, selectedFirstMonth);
drawingParams.put(SimpleMonthView.VIEW_PARAMS_SELECTED_BEGIN_DAY, selectedFirstDay);

drawingParams.put(SimpleMonthView.VIEW_PARAMS_YEAR, year);
drawingParams.put(SimpleMonthView.VIEW_PARAMS_MONTH, month);
drawingParams.put(SimpleMonthView.VIEW_PARAMS_WEEK_START, calendar.getFirstDayOfWeek());
v.setMonthParams(drawingParams);
if(dates!=null){
v.setFlagDates(dates);
}
v.invalidate();
}

public long getItemId(int position) {
return position;
}

@Override
public int getItemCount() {
return count;
}


public static class ViewHolder extends RecyclerView.ViewHolder {
final SimpleMonthView simpleMonthView;

public ViewHolder(View itemView, SimpleMonthView.OnDayClickListener onDayClickListener) {
super(itemView);
simpleMonthView = (SimpleMonthView) itemView;
simpleMonthView.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
simpleMonthView.setClickable(true);
simpleMonthView.setOnDayClickListener(onDayClickListener);
}
}

protected void init() {
onDayTapped(new CalendarDay(System.currentTimeMillis()));
}

public void onDayClick(SimpleMonthView simpleMonthView, CalendarDay calendarDay) {
if (calendarDay != null) {
onDayTapped(calendarDay);
}
}

//设置flag标记日期
public void setFlagDates(List<String> flagDates){
this.dates=flagDates;
notifyDataSetChanged();
}


protected void onDayTapped(CalendarDay calendarDay) {
mController.onDayOfMonthSelected(calendarDay.year, calendarDay.month, calendarDay.day);
setSelectedDay(calendarDay);
}

public void setSelectedDay(CalendarDay calendarDay) {
this.selectedDay=calendarDay;
notifyDataSetChanged();
}

}

对日期对象CalendarDay做了简单的封装

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
public class CalendarDay {
private static final long serialVersionUID = -5456695978688356202L;
private Calendar calendar;

int day;
int month;
int year;

public CalendarDay() {
setTime(System.currentTimeMillis());
}

public CalendarDay(int year, int month, int day) {
setDay(year, month, day);
}

public CalendarDay(long timeInMillis) {
setTime(timeInMillis);
}

public CalendarDay(Calendar calendar) {
year = calendar.get(Calendar.YEAR);
month = calendar.get(Calendar.MONTH);
day = calendar.get(Calendar.DAY_OF_MONTH);
}

private void setTime(long timeInMillis) {
if (calendar == null) {
calendar = Calendar.getInstance();
}
calendar.setTimeInMillis(timeInMillis);
month = this.calendar.get(Calendar.MONTH);
year = this.calendar.get(Calendar.YEAR);
day = this.calendar.get(Calendar.DAY_OF_MONTH);
}

public void set(CalendarDay calendarDay) {
year = calendarDay.year;
month = calendarDay.month;
day = calendarDay.day;
}

public void setDay(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}

public Date getDate() {
if (calendar == null) {
calendar = Calendar.getInstance();
}
calendar.set(year, month, day);
return calendar.getTime();
}

@Override
public String toString() {
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("{ year: ");
stringBuilder.append(year);
stringBuilder.append(", month: ");
stringBuilder.append(month);
stringBuilder.append(", day: ");
stringBuilder.append(day);
stringBuilder.append(" }");

return stringBuilder.toString();
}
}

自定义月日历itemview

这里根据自定义的属性读取值,对画笔初始化后用canvas进行绘制,计算当前绘制的日期坐标,对是否已过,是否为今日,是否选择分情况进行绘制。

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
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
class MonthView extends View {

public static final String VIEW_PARAMS_HEIGHT = "height";
public static final String VIEW_PARAMS_MONTH = "month";
public static final String VIEW_PARAMS_YEAR = "year";
public static final String VIEW_PARAMS_SELECTED_BEGIN_DAY = "selected_begin_day";
// public static final String VIEW_PARAMS_SELECTED_LAST_DAY = "selected_last_day";
public static final String VIEW_PARAMS_SELECTED_BEGIN_MONTH = "selected_begin_month";
// public static final String VIEW_PARAMS_SELECTED_LAST_MONTH = "selected_last_month";
public static final String VIEW_PARAMS_SELECTED_BEGIN_YEAR = "selected_begin_year";
// public static final String VIEW_PARAMS_SELECTED_LAST_YEAR = "selected_last_year";
public static final String VIEW_PARAMS_WEEK_START = "week_start";

protected int DEFAULT_HEIGHT = DensityUtil.dip2px(getContext(),16);
protected static final int DEFAULT_NUM_ROWS = 6;
protected static int DAY_SELECTED_CIRCLE_SIZE;
protected int DAY_SEPARATOR_WIDTH = DensityUtil.dip2px(getContext(),33);
protected static int MINI_DAY_NUMBER_TEXT_SIZE;
protected int MIN_HEIGHT = DensityUtil.dip2px(getContext(),3);
protected static int MONTH_LINE_SIZE;
protected static int MONTH_HEADER_SIZE;
protected static int MONTH_TITLE_TEXT_SIZE;

protected int mPadding = dip2px(getContext(),10);

private String mMonthTitleTypeface;//月份标题风格
protected Paint flagTextPaint;//标记字画笔
protected Paint flagPreBgPaint;//已过标记背景画笔
protected Paint flagNormalBgPaint;//标记背景画笔
protected Paint mMonthLinePaint;//月份标题线画笔
protected Paint mMonthNumPaint;//日期画笔
protected Paint mMonthTitlePaint;//月份标题画笔
protected Paint todayCiclePaint;//今天圆圈画笔
protected Paint mSelectedCirclePaint;//选择圆圈画笔

protected int mMonthTitleColor;//月份标题颜色
protected int mMonthLineColor;//月份标题线颜色
protected int mNormalDayColor;//日期颜色
protected int mPreviousDayColor;//已过日期颜色
protected int mSelectedBgColor;//选择背景颜色
protected int mSelectedTextColor;//选择文字颜色
protected int todayTextColor;//今日日期颜色
protected int flagPreBgColor;//已过标记背景色
protected int flagNormalBgColor;//标记背景色
protected int flagTextColor;//标记文字色
protected String flag; //标记文本
private final StringBuilder mStringBuilder;

protected boolean mHasToday = false;
protected boolean mIsPrev = false;
protected int mSelectedBeginDay = -1;
protected int mSelectedBeginMonth = -1;
protected int mSelectedBeginYear = -1;
private List<String> dates;
protected int mToday = -1;
protected int mWeekStart = 1;
protected int mNumDays = 7;
protected int mNumCells = mNumDays;
private int mDayOfWeekStart = 0;
protected int mMonth;
protected Boolean mDrawRect;
protected int mRowHeight = DEFAULT_HEIGHT;
protected int mWidth;
protected int mYear;
final Time today;

private final Calendar mCalendar;

private int mNumRows = DEFAULT_NUM_ROWS;


private OnDayClickListener mOnDayClickListener;

public MonthView(Context context, TypedArray typedArray) {
super(context);

Resources resources = context.getResources();
mCalendar = Calendar.getInstance();
today = new Time(Time.getCurrentTimezone());
today.setToNow();
mMonthTitleTypeface = resources.getString(R.string.sans_serif);
mMonthTitleColor = typedArray.getColor(R.styleable.MonthCalendarView_monthTitleColor, ContextCompat.getColor(context,R.color.normal_day));
mMonthLineColor = typedArray.getColor(R.styleable.MonthCalendarView_monthLineColor, ContextCompat.getColor(context,R.color.normal_day));
mNormalDayColor = typedArray.getColor(R.styleable.MonthCalendarView_normalDayTextColor_month, ContextCompat.getColor(context,R.color.normal_day));
mPreviousDayColor = typedArray.getColor(R.styleable.MonthCalendarView_previousDayTextColor_month, ContextCompat.getColor(context,R.color.normal_day));
mSelectedBgColor = typedArray.getColor(R.styleable.MonthCalendarView_selectedBgColor_month, ContextCompat.getColor(context,R.color.selected_day_background));
mSelectedTextColor = typedArray.getColor(R.styleable.MonthCalendarView_selectedTextColor_month, ContextCompat.getColor(context,R.color.white));
todayTextColor = typedArray.getColor(R.styleable.MonthCalendarView_todayTextColor_month,ContextCompat.getColor(context,R.color.default_blue));
flagTextColor= typedArray.getColor(R.styleable.MonthCalendarView_flagTextColor_month,ContextCompat.getColor(context,R.color.white));
flagPreBgColor=typedArray.getColor(R.styleable.MonthCalendarView_flagPreBgColor_month,ContextCompat.getColor(context,R.color.white));
flagNormalBgColor=typedArray.getColor(R.styleable.MonthCalendarView_flagNormalBgColor_month,ContextCompat.getColor(context,R.color.default_orange));
mDrawRect = typedArray.getBoolean(R.styleable.MonthCalendarView_isRoundRect_month, false);
int preMonthNum = typedArray.getInteger(R.styleable.MonthCalendarView_preMonthNum_month,2);
DateUtil.setPreMonthNum(preMonthNum);
int nextMonthNum = typedArray.getInteger(R.styleable.MonthCalendarView_nextMonthNum_month,2);
DateUtil.setNextMonthNum(nextMonthNum);

flag = typedArray.getString(R.styleable.MonthCalendarView_flagTextStr_month);
if(TextUtils.isEmpty(flag)){
flag="行";
}
mStringBuilder = new StringBuilder(50);

MINI_DAY_NUMBER_TEXT_SIZE = typedArray.getDimensionPixelSize(R.styleable.MonthCalendarView_dayTextSize_month, resources.getDimensionPixelSize(R.dimen.text_size_day));
MONTH_TITLE_TEXT_SIZE = typedArray.getDimensionPixelSize(R.styleable.MonthCalendarView_monthTitleTextSize, resources.getDimensionPixelSize(R.dimen.text_size_month));
MONTH_LINE_SIZE = typedArray.getDimensionPixelSize(R.styleable.MonthCalendarView_monthLineTextSize, resources.getDimensionPixelSize(R.dimen.text_size_day_name));
MONTH_HEADER_SIZE = typedArray.getDimensionPixelOffset(R.styleable.MonthCalendarView_headerMonthHeight, resources.getDimensionPixelOffset(R.dimen.header_month_height));
DAY_SELECTED_CIRCLE_SIZE = typedArray.getDimensionPixelSize(R.styleable.MonthCalendarView_selectedDayBgRadius, resources.getDimensionPixelOffset(R.dimen.selected_day_radius));

mRowHeight = ((typedArray.getDimensionPixelSize(R.styleable.MonthCalendarView_calendarHeight, resources.getDimensionPixelOffset(R.dimen.calendar_height)) - MONTH_HEADER_SIZE) / 6);

initView();

}

//设置flag标记日期
public void setFlagDates(List<String> flagDates){
this.dates=flagDates;
}

private int calculateNumRows() {
int offset = findDayOffset();
int dividend = (offset + mNumCells) / mNumDays;
int remainder = (offset + mNumCells) % mNumDays;
return (dividend + (remainder > 0 ? 1 : 0));
}


//绘制每月标题
private void drawMonthTitle(Canvas canvas) {
StringBuilder stringBuilder = new StringBuilder(getMonthAndYearString().toLowerCase());
stringBuilder.setCharAt(0, Character.toUpperCase(stringBuilder.charAt(0)));
Rect rect=new Rect();
mMonthTitlePaint.getTextBounds(String.valueOf(stringBuilder),0,stringBuilder.length(),rect);
int x = mWidth / mNumDays -mPadding;
int y = (MONTH_HEADER_SIZE - MONTH_LINE_SIZE) / 2 ;
canvas.drawLine(0, y-rect.height()-MIN_HEIGHT, mWidth, y-rect.height()-MIN_HEIGHT, mMonthLinePaint);
canvas.drawText(stringBuilder.toString(), x, y, mMonthTitlePaint);
canvas.drawLine(0, y+MIN_HEIGHT, mWidth, y+MIN_HEIGHT, mMonthLinePaint);
}

private int findDayOffset() {
return (mDayOfWeekStart < mWeekStart ? (mDayOfWeekStart + mNumDays) : mDayOfWeekStart)
- mWeekStart;
}

private String getMonthAndYearString() {
int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_NO_MONTH_DAY;
mStringBuilder.setLength(0);
long millis = mCalendar.getTimeInMillis();
return DateUtils.formatDateRange(getContext(), millis, millis, flags);
}

private void onDayClick(CalendarDay calendarDay) {
if (mOnDayClickListener != null) {
mOnDayClickListener.onDayClick(this, calendarDay);
}
}

private boolean sameDay(int monthDay, Time time) {
return (mYear == time.year) && (mMonth == time.month) && (monthDay == time.monthDay);
}

private boolean prevDay(int monthDay, Time time) {
return ((mYear < time.year)) || (mYear == time.year && mMonth < time.month) || (mMonth == time.month && monthDay < time.monthDay);
}

//绘制日期
protected void drawMonthNums(Canvas canvas) {

int y = (mRowHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2 - DAY_SEPARATOR_WIDTH + MONTH_HEADER_SIZE;
int paddingDay = (mWidth - 2 * mPadding) / (2 * mNumDays);
int dayOffset = findDayOffset();
int day = 1;

while (day <= mNumCells) {
int x = paddingDay * (1 + dayOffset * 2) + mPadding;

//天数
String dayStr= String.format("%d", day);
//测量字符串天数
Rect dayRect=new Rect();
mMonthTitlePaint.getTextBounds(dayStr,0,dayStr.length(),dayRect);
//测量字符串标记
Rect flagRect=new Rect();
mMonthTitlePaint.getTextBounds(flag,0,flag.length(),flagRect);

//已经过了的日期
if (prevDay(day, today)) {
mMonthNumPaint.setColor(mPreviousDayColor);
if(isFlag(mYear,mMonth,day)){
//绘制方形背景
canvas.drawRect(x-flagRect.width()/2-MIN_HEIGHT/2,y+DEFAULT_HEIGHT-MIN_HEIGHT/2,x+flagRect.width()/2+MIN_HEIGHT/2,y+flagRect.height()+DEFAULT_HEIGHT+MIN_HEIGHT/2,flagPreBgPaint);
//绘制字体
canvas.drawText(flag, x-flagRect.width()/2-MIN_HEIGHT/2, y+flagRect.height()+DEFAULT_HEIGHT, flagTextPaint);
}
}else{
//当前日期是今天
if (mHasToday && (mToday == day)) {
mMonthNumPaint.setColor(getResources().getColor(R.color.default_blue));
mMonthNumPaint.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL));
if (mDrawRect) {
RectF rectF = new RectF(x - DAY_SELECTED_CIRCLE_SIZE, (y - MINI_DAY_NUMBER_TEXT_SIZE / 3) - DAY_SELECTED_CIRCLE_SIZE, x + DAY_SELECTED_CIRCLE_SIZE, (y - MINI_DAY_NUMBER_TEXT_SIZE / 3) + DAY_SELECTED_CIRCLE_SIZE);
canvas.drawRoundRect(rectF, 10.0f, 10.0f, todayCiclePaint);
}else{
canvas.drawCircle(x, y - MINI_DAY_NUMBER_TEXT_SIZE / 3, DAY_SELECTED_CIRCLE_SIZE, todayCiclePaint);
}
} else { //不是今天
mMonthNumPaint.setColor(mNormalDayColor);
mMonthNumPaint.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL));
}
if(isFlag(mYear,mMonth,day)){
//绘制方形背景
canvas.drawRect(x-flagRect.width()/2-MIN_HEIGHT/2,y+DEFAULT_HEIGHT-MIN_HEIGHT/2,x+flagRect.width()/2+MIN_HEIGHT/2,y+flagRect.height()+DEFAULT_HEIGHT+MIN_HEIGHT/2,flagNormalBgPaint);
//绘制字体
canvas.drawText(flag, x-flagRect.width()/2-MIN_HEIGHT/2, y+flagRect.height()+DEFAULT_HEIGHT, flagTextPaint);
}

}
//选择的日期
if ((mMonth == mSelectedBeginMonth && mSelectedBeginDay == day && mSelectedBeginYear == mYear) ) {
mMonthNumPaint.setColor(mSelectedTextColor);
if (mDrawRect) {
RectF rectF = new RectF(x - DAY_SELECTED_CIRCLE_SIZE, (y - MINI_DAY_NUMBER_TEXT_SIZE / 3) - DAY_SELECTED_CIRCLE_SIZE, x + DAY_SELECTED_CIRCLE_SIZE, (y - MINI_DAY_NUMBER_TEXT_SIZE / 3) + DAY_SELECTED_CIRCLE_SIZE);
canvas.drawRoundRect(rectF, 10.0f, 10.0f, mSelectedCirclePaint);
} else{
canvas.drawCircle(x, y - MINI_DAY_NUMBER_TEXT_SIZE / 3, DAY_SELECTED_CIRCLE_SIZE, mSelectedCirclePaint);
}
}

canvas.drawText(dayStr, x, y, mMonthNumPaint);
dayOffset++;
if (dayOffset == mNumDays) {
dayOffset = 0;
y += mRowHeight;
}
day++;
}
}

//是否需要绘制标记
private boolean isFlag(int year,int month,int day){
if(dates!=null){
//遍历当前天是否需要绘制
Calendar c = Calendar.getInstance();//获取一个日历实例
c.set(year, month, day);//设定日历的日期
Date curDay=c.getTime();
Iterator<String> it=dates.iterator();
while(it.hasNext()){
String date=it.next();
String curDayStr=OtherUtils.formatDate(curDay,"yyyy-MM-dd");
if(date.equals(curDayStr)){
return true;
}
}
return false;
}else{
return false;
}

}

public CalendarDay getDayFromLocation(float x, float y) {
int padding = mPadding;
if ((x < padding) || (x > mWidth - mPadding)) {
return null;
}
// (mRowHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2 - DAY_SEPARATOR_WIDTH + MONTH_HEADER_SIZE;
int yDay = (int) (y - MONTH_HEADER_SIZE + DAY_SEPARATOR_WIDTH) / mRowHeight;
int day = 1 + ((int) ((x - padding) * mNumDays / (mWidth - padding - mPadding)) - findDayOffset()) + yDay * mNumDays;

if (mMonth > 11 || mMonth < 0 || CalendarUtils.getDaysInMonth(mMonth, mYear) < day || day < 1)
return null;

return new CalendarDay(mYear, mMonth, day);
}

protected void initView() {
mMonthTitlePaint = new Paint();
mMonthTitlePaint.setAntiAlias(true);
mMonthTitlePaint.setTextSize(MONTH_TITLE_TEXT_SIZE);
mMonthTitlePaint.setTypeface(Typeface.create(mMonthTitleTypeface, Typeface.BOLD));
mMonthTitlePaint.setColor(mMonthTitleColor);
mMonthTitlePaint.setTextAlign(Align.CENTER);
mMonthTitlePaint.setStyle(Style.FILL);

mSelectedCirclePaint = new Paint();
mSelectedCirclePaint.setFakeBoldText(true);
mSelectedCirclePaint.setAntiAlias(true);
mSelectedCirclePaint.setColor(mSelectedBgColor);
mSelectedCirclePaint.setTextAlign(Align.CENTER);
mSelectedCirclePaint.setStyle(Style.FILL);

mMonthLinePaint = new Paint();
mMonthLinePaint.setAntiAlias(true);
mMonthLinePaint.setTextSize(MONTH_LINE_SIZE);
mMonthLinePaint.setColor(mMonthLineColor);
mMonthLinePaint.setStyle(Style.FILL);
mMonthLinePaint.setTextAlign(Align.CENTER);
mMonthNumPaint = new Paint();
mMonthNumPaint.setAntiAlias(true);
mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE);
mMonthNumPaint.setStyle(Style.FILL);
mMonthNumPaint.setTextAlign(Align.CENTER);
mMonthNumPaint.setFakeBoldText(false);

flagTextPaint=new Paint();
flagTextPaint.setAntiAlias(true);
flagTextPaint.setColor(flagTextColor);
flagTextPaint.setTextSize(MONTH_LINE_SIZE);
flagNormalBgPaint=new Paint();
flagNormalBgPaint.setAntiAlias(true);
flagNormalBgPaint.setColor(flagNormalBgColor);

flagPreBgPaint=new Paint();
flagPreBgPaint.setAntiAlias(true);
flagPreBgPaint.setColor(flagPreBgColor);

todayCiclePaint=new Paint();
todayCiclePaint.setAntiAlias(true);
todayCiclePaint.setStrokeWidth(4);
todayCiclePaint.setColor(todayTextColor);
todayCiclePaint.setStyle(Style.STROKE);
}

protected void onDraw(Canvas canvas) {
drawMonthTitle(canvas);
// drawMonthDayLabels(canvas);
drawMonthNums(canvas);
}

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mRowHeight * mNumRows + MONTH_HEADER_SIZE);
}

protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mWidth = w;
}

public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
CalendarDay calendarDay = getDayFromLocation(event.getX(), event.getY());
if (calendarDay != null) {
onDayClick(calendarDay);
}
}
return true;
}

public void reuse() {
mNumRows = DEFAULT_NUM_ROWS;
requestLayout();
}

public void setMonthParams(HashMap<String, Integer> params) {
if (!params.containsKey(VIEW_PARAMS_MONTH) && !params.containsKey(VIEW_PARAMS_YEAR)) {
throw new InvalidParameterException("You must specify month and year for this view");
}
setTag(params);

if (params.containsKey(VIEW_PARAMS_HEIGHT)) {
mRowHeight = params.get(VIEW_PARAMS_HEIGHT);
if (mRowHeight < MIN_HEIGHT) {
mRowHeight = MIN_HEIGHT;
}
}
if (params.containsKey(VIEW_PARAMS_SELECTED_BEGIN_DAY)) {
mSelectedBeginDay = params.get(VIEW_PARAMS_SELECTED_BEGIN_DAY);
}
// if (params.containsKey(VIEW_PARAMS_SELECTED_LAST_DAY)) {
// mSelectedLastDay = params.get(VIEW_PARAMS_SELECTED_LAST_DAY);
// }
if (params.containsKey(VIEW_PARAMS_SELECTED_BEGIN_MONTH)) {
mSelectedBeginMonth = params.get(VIEW_PARAMS_SELECTED_BEGIN_MONTH);
}
// if (params.containsKey(VIEW_PARAMS_SELECTED_LAST_MONTH)) {
// mSelectedLastMonth = params.get(VIEW_PARAMS_SELECTED_LAST_MONTH);
// }
if (params.containsKey(VIEW_PARAMS_SELECTED_BEGIN_YEAR)) {
mSelectedBeginYear = params.get(VIEW_PARAMS_SELECTED_BEGIN_YEAR);
}
// if (params.containsKey(VIEW_PARAMS_SELECTED_LAST_YEAR)) {
// mSelectedLastYear = params.get(VIEW_PARAMS_SELECTED_LAST_YEAR);
// }

mMonth = params.get(VIEW_PARAMS_MONTH);
mYear = params.get(VIEW_PARAMS_YEAR);

mHasToday = false;
mToday = -1;

mCalendar.set(Calendar.MONTH, mMonth);
mCalendar.set(Calendar.YEAR, mYear);
mCalendar.set(Calendar.DAY_OF_MONTH, 1);
mDayOfWeekStart = mCalendar.get(Calendar.DAY_OF_WEEK);

if (params.containsKey(VIEW_PARAMS_WEEK_START)) {
mWeekStart = params.get(VIEW_PARAMS_WEEK_START);
} else {
mWeekStart = mCalendar.getFirstDayOfWeek();
}

mNumCells = CalendarUtils.getDaysInMonth(mMonth, mYear);
for (int i = 0; i < mNumCells; i++) {
final int day = i + 1;
if (sameDay(day, today)) {
mHasToday = true;
mToday = day;
}

mIsPrev = prevDay(day, today);
}

mNumRows = calculateNumRows();
}

public void setOnDayClickListener(OnDayClickListener onDayClickListener) {
mOnDayClickListener = onDayClickListener;
}

public interface OnDayClickListener {
void onDayClick(MonthView monthView, CalendarDay calendarDay);
}

/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
int scale = (int) context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}

}

调用lib库

WeekCalendar的调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 weekCalendar.setOnDateClickListener(new OnDateClickListener() {
@Override
public void onDateClick(DateTime dateTime) {
clickDateTime=dateTime;
journey_month.setText(dateTime.getMonthOfYear() + "");
journey_data_title.setText(OtherUtils.formatDate(dateTime.toDate()));
journeyListAdapter.setData(setCurJourneyList(dateTime.toDate()));//设置当天的行程数据
}

});
weekCalendar.setOnWeekChangeListener(new OnWeekChangeListener() {
@Override
public void onWeekChange(DateTime firstDayOfTheWeek, boolean forward) {
journey_month.setText(firstDayOfTheWeek.getMonthOfYear() + "");
Toast.makeText(context, "Week changed: " + firstDayOfTheWeek +
" Forward: " + forward, Toast.LENGTH_SHORT).show();
}
});

MonthCalendar的调用

这里通过嵌套在popwindow进行调用,当然也可以放在其它控件中

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
  LayoutInflater inflater = LayoutInflater.from(context);
vPop = inflater.inflate(R.layout.view_popwindow_calendar_select, null);
pop = new PopupWindow(vPop, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
// pop.setBackgroundDrawable(new ColorDrawable(0));
pop.setFocusable(false);
pop.setOutsideTouchable(false);

dayPickerView = (DayPickerView) vPop.findViewById(R.id.pop_pickerView);

dayPickerView.setController(new DatePickerController() {
@Override
public int getMaxYear() {
return DateUtil.getEndYear();
}

@Override
public void onDayOfMonthSelected(int year, int month, int day) {
Log.e("kosmos", "onDayOfMonthSelected:" + day + " / " + month + " / " + year);
if (listener != null) {
listener.onCalendarSelect(year, month, day);
}
if (pop != null && pop.isShowing()) {
// pop.dismiss();
}
}

});

想查看完整代码的可以点击传送门