前言:
本文实现的是,自定义的日历控件,可以添加日程安排。
本篇是基于网上源码做的相应修改,本文只列出修改的内容,需要看源码博文的请到:
http://blog.csdn.net/h7870181/article/details/8960478
一、效果展示
二、日历控件的修改
1、星期显示中文
此处,原控件里显示的是英文,本处改成中文,这个改动十分简单,只要能看懂代码的人,都会修改,我就在此处顺便练习了一下枚举类型的使用。
该处修改,是在CalendarView的一个内部类Surface里进行的,该类主要的目的是初始化一些参数设置,比如画笔颜色、日期框宽度、日期数组等等。以下是修改的部分代码:
<span style="font-size:12px;">public String[] weekText = {DateEnum.SUN.getValue(), DateEnum.MON.getValue(), DateEnum.TUE.getValue(),
DateEnum.WED.getValue(), DateEnum.THU.getValue(), DateEnum.FRI.getValue(), DateEnum.SAT.getValue()};</span>
其中,DateEnum是我定义的周枚举类型,此处比较简单,不再多叙述,详情可参考源码。
2、改变周末的显示颜色
可以看到截图中,周六和周日字体颜色显示为粉色,这里也比较简单,在源码中找到绘制这些字的位置,然后加一个判断是否为周末的控制语句即可完成。
首先,绘制是在onDraw中进行的,
for (int i = 0; i < 42; i++) {
int color = surface.textColor;
if (isWeekEnd(i)) {
color = surface.weekDayColor;
}
if (isLastMonth(i)) {
color = surface.borderColor;
} else if (isNextMonth(i)) {
color = surface.borderColor;
}
if (todayIndex != -1 && i == todayIndex) {
color = surface.todayNumberColor;
}
drawCellText(canvas, i, date[i] + "", color);
}
在代码中,我们可以看出,绘制原理其实就是绘制42个格子,并在格子里面画上相应的日期,在一些特殊情况下,改变字体的颜色,比如当日显示为红色,还有我加入的周末显示为粉色。判断是否是周末,我用了一个比较笨的方式,将42个格子中,所有周末的下标记录在同一个数组里面,再进行判断,
<span style="font-size:12px;">private boolean isWeekEnd(int date) {
//6*7的方格中,以下位置代表的是周末
int[] weekEnd = new int[]{6, 7, 13, 14, 20, 21, 27, 28, 34, 35};
boolean isWeekEnd = false;
int i = 0;
while (i < weekEnd.length) {
if (date == weekEnd[i]) {
isWeekEnd = true;
break;
}
i++;
}
return isWeekEnd;
}</span>
这里还要考虑到一个问题,就是代表上一个月和下一个月日期,即截图中灰色日期部分,是不能够显示为粉色的,所以if (isWeekEnd(i))的判断,一定要在if (isLastMonth(i))这个判断的前面。
3、日程红点的加入
首先需要在CalendarView中,定义一个ArrayList,用于记录需要绘制点的下标
<span style="font-size:12px;">private List<Integer> spotList = new ArrayList<Integer>();//需要加点的位置数组\</span>
其次在onDraw方法中,添加一个绘制圆点方法的调用
<span style="font-size:12px;">drawSpot(canvas, spotList);</span>
然后我们来看一下这个方法里面做了什么,
<span style="font-size:12px;">private void drawSpot(Canvas canvas, List<Integer> spotList) {
for (int i : spotList) { //循环遍历spotList画点
drawSpotDetail(canvas, i);
}
}</span>
循环遍历,画点方法:
<span style="font-size:12px;">private void drawSpotDetail(Canvas canvas, int index) {
int x = getXByIndex(index);
int y = getYByIndex(index);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(15);
float cellY = surface.monthHeight + surface.weekHeight + (y - 1)
* surface.cellHeight + surface.cellHeight * 1 / 4f;
float cellX = (surface.cellWidth * (x - 1))
+ (surface.cellWidth)
/ 2f;
canvas.drawPoint(cellX, cellY, paint);
}</span>
这方法是首先根据索引计算点的位置,然后画点。
接下来就是从外部把需要画点的list传入到控件中,然后再调用一次invalidate();方法,就可以重新执行onDraw方法,实现点的重绘,此处提供了对外开放的方法
<span style="font-size:12px;">public void change(List<Integer> spotList) {
this.spotList.clear();
this.spotList.addAll(spotList);
invalidate();
}</span>
对于日历控件的修改,大致上就是这些,当然还有一些小的修改就不足为提了,下面介绍添加日程的部分。
二、在日历控件下面,添加日程模块
这个功能我是用ExpandableListView来实现的,将ExpandableListView放到日历控件的下面,再为ExpandableListView和CalendarView添加相应的交互即可。
1、布局文件
<span style="font-size:12px;"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/layout_calendar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="visible"
android:background="@color/calendar_top_bg">
<TextView
android:id="@+id/calendarCenter"
style="@style/main_bar_text_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_margin="8dp" />
<ImageButton
android:id="@+id/calendarLeft"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:background="@null"
android:contentDescription="@null"
android:padding="8dp"
android:src="@drawable/calendar_month_left" />
<ImageButton
android:id="@+id/calendarRight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:background="@null"
android:contentDescription="@null"
android:padding="8dp"
android:src="@drawable/calendar_month_right" />
<com.example.calendarviewdemo.CalendarView
android:id="@+id/calendar"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_below="@+id/calendarCenter" />
</RelativeLayout>
<ExpandableListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:childDivider="@android:color/transparent"
android:background="@color/white"
android:groupIndicator="@null"
android:layout_weight="1"
android:drawSelectorOnTop="false" />
</LinearLayout></span>
ExpandableListView的学习资料也很多,以下就简述重点及遇到的问题了,有兴趣的可以下载源码查看全部代码。
2、ExpandableListView数据没有更改之前,setSelectedGroup()方法调用效果一切正常;而填充数据更改notifyDataSetChanged之后,同样的代码片段却莫名其妙无效了,该问题我用以下方法得以解决,
<span style="font-size:12px;">mListView.post(new Runnable() { </span>
<span style="font-size:12px;"> @Override
public void run() { //因此此处放在线程里
mListView.setSelectedGroup(calendar.getNowDate() - 1); //设置初始listview的显示位置,为本日
}
});</span>
3、CalendarView的点击事件
<span style="font-size:12px;">calendar.setOnItemClickListener(new OnItemClickListener() {
@Override
public void OnItemClick(java.util.Date selectedStartDate,
java.util.Date selectedEndDate, java.util.Date downDate) {
setHeadDate(format.format(downDate)); //头上的日期显示更换
String[] ya = getStrings(format.format(downDate)); //获取按下的日期,目的是获取“日”,与mListView的下标对比,得到要显示的位置
String[] nowDate = getStrings(calendar.getYearAndmonthAndDate());
if(ya[1].equals(nowDate[1])){ //用于限制本页显示灰色的上一月和下一月,点击后不执行以下代码
mListView.setSelectedGroup(Integer.valueOf(ya[2]) - 1); //日期比下标大1,因此这里减1,可定位到正确位置
}
}
});</span>
这里要实现两个目的,第一点击后标题上的日期显示更改,第二下面的ExpandableListView显示的Group的Item也要做相应的修改,注意这里有一个问题,为灰色的日期也是可以设置点击时间,但是显然我们不希望点了这些灰色日期后,ExpandableListView位置发生变化,因此做了一个是否是本月的判断,只有在本月,点了以后ExpandableListView的位置才能发生变化。
3、点击ExpandableListView的child的最后一项时,(最后一项显示的内容为“新建日程”),调用CalendarView绘制红点的方法
<span style="font-size:12px;">mListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView expandableListView, View view, int groupPosition, int childPosition, long l) {
if(child.get(groupPosition).size() == childPosition + 1){
spotList.add(Integer.valueOf(group.get(groupPosition)) - 1 + calendar.getcurStartIndex());
calendar.change(spotList);
Toast.makeText(MainActivity.this, "添加了一个日程" , Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(MainActivity.this, "hah" + childPosition , Toast.LENGTH_SHORT).show();
}
return false;
}
});</span>
注意这里,ExpandableListView要使用setOnChildClickListener这个监听时,在其adapter中重写的isChildSelectable,返回值必须为true,否则监听无效。
<span style="font-size:12px;">@Override
public boolean isChildSelectable(int i, int i2) {
return true;
}</span>
4.ExpandableListView的一些设置
mListView.expandGroup(i);
}
终于写好了~ 内容不是什么高大尚的东西,希望对新手有些帮助!
欢迎提出宝贵意见!
源码下载:源码下载