id="@+id/tvMsg3"android:
layout_width="fill_parent"
android:
layout_height="wrap_content"
android:
textColor="@color/text_color"/>
下面在Main类的Oncreate方法中编写代码装载main.xml:
publicvoidonCreate(BundlesavedInstanceState)
{
super.onCreate(savedInstanceState);
LinearLayoutmainLayout=(LinearLayout)getLayoutInflater().
inflate(R.layout.main,null);
setContentView(mainLayout);
//需要向mainLayout中加入日历网络
}
在绘制日历之前,要先介绍一下日历绘制的方法。
虽然从底层技术来看,绘制日历是在onDraw方法中完成的。
但在本系统中将要绘制的部分分成了很多块。
而这些要绘制的块都需要放在一个叫CalendarView的类中,代码如下:
publicclassCalendarViewextendsView
{
privateActivityactivity;
@Override
protectedvoidonDraw(Canvascanvas)
{}
publicCalendarView(Activityactivity)
{
super(activity);
this.activity=activity;
}
}
在编写完CalendarView类后,需要在Main类中定义该类的变量,并在onCreate方法中创建类的对象实例,代码如下:
privateCalendarViewcalendarView;
calendarView=newCalendarView(this);
mainLayout.addView(calendarView);
在前面已经介绍过,在本系统中会将要绘制的日历分成若干块,而每一块都需要有同样的接口,以便统一绘制它们。
因此,这些块都要实现一个CalendarElement接口。
这些要绘制的块包括日历边框、网络、日历头等,而在CalendarElement接口中有一个draw方法。
在绘制日历元素时只需调用draw方法即可。
在后面的实现中会看到更多实现CalendarElement接口的类,下面先来编写CalendarElement接口。
packageproject.calendar.interfaces;
importandroid.graphics.Canvas;
publicinterfaceCalendarElement
{
publicvoiddraw(Canvascanvas);
}
packageproject.calendar.interfaces;
importandroid.graphics.Canvas;
publicinterfaceCalendarElement
{
publicvoiddraw(Canvascanvas);
}
现在需要一个总的类来绘制上述的这些块。
这个功能由Calendar类来完成。
Calendar是一个总的日历元素类,在该类的draw方法中绘制了所有的日历元素。
Calendar是第一个实现CalendarElement接口的类,代码如下:
publicclassCalendarextendsCalendarParent
{
//elements用于保存多功能日历中所有的日历元素
privateArrayListelements=new
ArrayList();
publicCalendar(Activityactivity,Viewview)
{
super(activity,view);
}
@Override
publicvoiddraw(Canvascanvas)
{
//在draw方法中通过扫描elements变量来获得所
//有日历元素的对象,并调用draw方法绘制这些日历元素
for(CalendarElementce:
elements)
ce.draw(canvas);
}
}
在CalendarView类中需要调用Calendar类来绘制日历,因此,需要在CalendarView类中创建Calendar类的对象实例,并调用draw进行绘制,代码如下:
publicCalendarce;
@Override
protectedvoidonDraw(Canvascanvas)
{
ce.draw(canvas);
}
publicCalendarView(Activityactivity)
{
ce=newCalendar(activity,this);
}
下面编写第一个绘制元素类:
Border。
Border类用于绘制日历的边框,该类是日历元素类,需要实现CalendarElement接口,不过该类只要继承刚实现的CalendarParent类即可。
publicclassBorderextendsCalendarParent
{
publicBorder(Activityactivity,Viewview)
{
super(activity,view);
//注意,一定要4个字节的颜色值,包括一个透明色
paint.setColor(0xFFFFFFFF);
}
@Override
publicvoiddraw(Canvascanvas)
{
floatleft=borderMargin;
floattop=borderMargin;
floatright=view.getMeasuredWidth()-left;
floatbottom=view.getMeasuredHeight()-top;
canvas.drawLine(left,top,right,top,paint);
canvas.drawLine(right,top,right,bottom,paint);
canvas.drawLine(right,bottom,left,bottom,paint);
canvas.drawLine(left,bottom,left,top,paint);
Log.d("draw",String.valueOf(right));
}
}
Grid类用于绘制日历的网格,该类是日历元素类,需要继承CalendarParent类。
publicclassGridextendsCalendarParent
{
privatefloattop,left;
@Override
publicvoiddraw(Canvascanvas)
{
left=borderMargin;
top=borderMargin+weekNameSize+weekNameMargin*
2+4;
floatcalendarWidth=view.getMeasuredWidth()-left*2;
floatcalendarHeight=view.getMeasuredHeight()-top-
borderMargin;
floatcellWidth=calendarWidth/7;
floatcellHeight=calendarHeight/6;
paint.setColor(0xFFFFFFFF);
canvas.drawLine(left,top,left+view.getMeasuredWidth()
-borderMargin*2,top,paint);
paint.setColor(0xFF666666);
//画横线
for(inti=1;i<6;i++)
{
canvas.drawLine(left,top+(cellHeight)*i,left+calendar-
Width,
top+(cellHeight)*i,paint);
}
//画竖线
for(inti=1;i<7;i++)
{
canvas.drawLine(left+cellWidth*i,top,left+cellWidth*i,
view.getMeasuredHeight()-borderMargin,paint);
}}
publicGrid(Activityactivity,Viewview)
{
super(activity,view);
//TODOAuto-generatedconstructorstub
}}
Week类用于显示日历网格上方的星期文本:
publicclassWeekextendsCalendarParent
{
privateString[]weekNames=newString[]
{"日","一","二","三","四","五","六"};
privateintweekNameColor;
publicWeek(Activityactivity,Viewview)
{
super(activity,view);
weekNameColor=Color.WHITE;
paint.setTextSize(weekNameSize);
}
@Override
publicvoiddraw(Canvascanvas)
{
floatleft=borderMargin;
floattop=borderMargin;
floateveryWeekWidth=(view.getMeasuredWidth()-borderMargin
*2)/7;
paint.setFakeBoldText(true);
for(inti=0;i{
if(i==0||i==weekNames.length-1)
paint.setColor(sundaySaturdayColor);
else
paint.setColor(weekNameColor);
left=borderMargin+everyWeekWidth*i
+(everyWeekWidth-paint.measureText(weekNames[i]))/
2;
canvas.drawText(weekNames[i],left,top+paint.getTextSize
()+
weekNameMargin,paint);
}}}
3.3核心技术
前面介绍了绘制日历的基本方法,现在将逐步接触到日历系统的核心技术。
首先来看一下日历中如何表示日期。
在Grid类中的days数组保存了42个数字。
这42个数字就是日历主界面中的6*7个方格中的数字。
这些数字分为两部分,中间的部分就是当前月中的天数。
这些天数最小是28天,最大是31天。
在这组数字的前后可以包含了上月的部分月末天数和下月部分月初天数。
当单击或触摸上月或下月的天数时,日历会跳到上月或下月。
这里内容在后面将详细介绍。
只介绍如何将这些数字绘制到这42个方格中。
为了区别这两类数字。
将在上月和下月的相应天数中加星号(*),代码如下:
privateString[]days=newString[42];
publicintcurrentYear,currentMonth;
publicintcurrentDay=-1,currentDay1=-1,currentDayIndex
=-1;
privatejava.util.Calendarcalendar=java.util.Calendar.
getInstance();
在Grid类的构造方法中初始化变量的代码如下:
currentYear=calendar.get(calendar.YEAR);
currentMonth=calendar.get(calendar.MONTH);
下面来编写一个getMonthDays方法,该方法用来获得指定月份的天数。
这个方法也是绘制指定月份的日历的基础,代码如下:
privateintgetMonthDays(intyear,intmonth)
{
month++;
switch(month)
{
case1:
case3:
case5:
case7:
case8:
case10:
case12:
{
return31;
}
case4:
case6:
case9:
case11:
{
return30;
}
case2:
{
if(((year%4==0)&&(year%100!
=0))||(year%
400==0))
return29;
else
return28;
}}
return0;
}
下面到了关键时刻,需要基础出当前月的天数,以及上一月和下一月落在本月初和本月末的天数。
并将其放在days数组中。
代码如下:
privatevoidcalculateDays()
{
//将当前日历设为指定月份的第一天
calendar.set(currentYear,currentMonth,1);
//获得指定月份的第1天是当前周的第几天
intweek=calendar.get(calendar.DAY_OF_WEEK);
intmonthDays=0;
intprevMonthDays=0;
//获得当前月有多少天
monthDays=getMonthDays(currentYear,currentMonth);
//如果当前月是一年中的第一个月,则获得上一年最后一月
//(也就是12月)的天数
if(currentMonth==0)
prevMonthDays=getMonthDays(currentYear-1,11);
//否则,获得指定月上一月的天数
else
prevMonthDays=getMonthDays(currentYear,current-
Month-1);
for(inti=week,day=prevMonthDays;i>1;i--,day--)
{
days[i-2]="*"+String.valueOf(day);
}
//设置指定月(在这里是当前月)的天数
for(intday=1,i=week-1;day<=monthDays;day++,
i++)
{
days[i]=String.valueOf(day);
if(day==currentDay)
{
//获得当前日在days数组中的索引
currentDayIndex=i;
}}
//设置下一月显示在本月日历后面的天数
for(inti=week+monthDay