前言:
本文实现的是,自定义的日历控件,可以添加日程安排。
本篇是基于网上源码做的相应修改,本文只列出修改的内容,需要看源码博文的请到:
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);
}
终于写好了~ 内容不是什么高大尚的东西,希望对新手有些帮助!
欢迎提出宝贵意见!
源码下载:源码下载