前言:最近一直做项目,遇到许多问题,其中令我很不爽的是:项目需求要求的日历控件一定要仿苹果!!!安卓本身提供的TimePicker是年月日分开滚动的,这不行呀,一定得一起滚,这让我挺大地感慨当前安卓开发很难真正做出android的风格出来,不是技术问题而是需求问题。还好,第二个项目对日历控件的要求没那么严格了,但是我百度了一下,开源的日历控件很少有符合心意的(可能是我没找到,有的话请留言我~),所以就根据之前的日历控件再做改进,回头看去,之前的我还是太年轻了。
总结之前的缺陷:
- 没有用好Canvas和Paint,绘制的位置计算不对
- 功能不足,比如设置可已选择的日期
于是,第二季来了
这其实是两个部分的结合,上部分显示年月和按钮的是一个LinearLayout,下面的日历才是真正的主角。
考虑到之前绘制了年月和切换上下月的按钮,感觉不太灵活,于是单独将日历页拿出来,这样就可以运用到很多地方了。只要结合多其他的控件,即可以做出想要的效果。
思路部分:
- 将高度划分为八份,第一份绘制周数,第二份至第八份绘制日期。
- 将宽度划分为七份,分别绘制星期一至星期日。
- 获取一个月最大天数和第一天的星期则可以得到这个月的所有日期,用以绘制。
- 最重要的,关于时间判断处理的逻辑是最头疼的,在网上找了许多关于时间的算法,总结成了一个CalendarUtils。
方法部分:
-
public void setYear(int year);
设置年 -
public void setMonth(int month);
设置月 -
public void setToday(int today);
设置今天 -
public void setCalendarBg(int calendarBg);
设置日历背景颜色 -
public void setAllDays(int maxnum);
设置一个月的天数 -
public void setmNormalTextColor(int mNormalTextColor);
设置普通文字颜色 -
public void setmBeSelectedTextColor(int mBeSelectedTextColor);
设置被选择文字颜色 -
public void setmUnBeSelectedTextColor(int mUnBeSelectedTextColor);
设置不可选择文字颜色 -
public void setmUnBeSelectedTextBgColor(int mUnBeSelectedTextBgColor);
设置不可选择文字背景颜色 -
public void setmBeSelectedTextBgColor(int mBeSelectedTextBgColor);
设置被选择文字背景颜色 -
public void setmNormalTextBgColor(int mNormalTextBgColor);
设置普通文字背景颜色 -
public void setmBgRadius(int mBgRadius);
设置背景半径 -
public void setmWeekTextColor(int mWeekTextColor);
设置周字体颜色 - ``
代码部分:
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
Log.d(TAG, "mViewWidth=" + mViewWidth);
Log.d(TAG, "mViewHeight=" + mViewHeight);
canvas.drawColor(calendarBg);
int hang_num = 8;
int lie_num = 7;
xInterval = mViewWidth / lie_num;
yInterval = mViewHeight / hang_num;
//背景圆的半径
mBgRadius = (int) (Math.min(xInterval, yInterval) / 2);
mWeekTextSize = Math.min(mViewHeight, mViewWidth) / 15;
mDayTextsize = Math.min(mViewHeight, mViewWidth) / 15;
float x = 0;
float y = 0;
float offset = 0;
mPaint.reset();
drawWeekText(canvas);
drawDay(canvas);
}
/**
* 绘制周
*
* @param canvas
*/
private void drawWeekText(Canvas canvas) {
float x, y;
float offset;
//注意,这里要先设置TextSize,后面用Paint测量String长度才会准,这样绘制文字才会准
mPaint.setTextSize(mWeekTextSize);
// 绘制一周
for (int i = 0; i < weekName.length; i++) {
x = i * xInterval + xInterval / 2;
// 因为绘制在第一行
y = 1 * yInterval;
offset = mPaint.measureText(weekName[i]);
if (i == 0 || i == weekName.length - 1) {
mPaint.setColor(mWeekTextColor);
mPaint.setAntiAlias(true);
canvas.drawText(weekName[i], x - offset / 2, y, mPaint);
} else {
mPaint.setColor(mWeekTextColor);
mPaint.setAntiAlias(true);
canvas.drawText(weekName[i], x - offset / 2, y, mPaint);
}
}
mPaint.reset();
}
绘制的方法也做了修改,让字体的绘制更加精准
/**
* 绘制天
*
* @param canvas
*/
private void drawDay(Canvas canvas) {
mPaint.setTextSize(mDayTextsize);
mPaint.setAntiAlias(true);
int day = 0;
int theday;
boolean istoday = false;
boolean isCheckDay = false;
float offset = 0;
float x, y;
// 绘制的日期
String str;
for (int i = 2; i < 8; i++) {
for (int j = 0; j < 7; j++) {
if (i == 2 && j == 0) {
j = weekOfFirstDay;
}
if (day > allDays.length - 1) {
theday = -1;
} else {
theday = allDays[day];
}
if (theday < 10) {
str = "0" + theday;
} else {
str = "" + theday;
}
if (theday == -1) {
str = "";
}
offset = mPaint.measureText(str);
x = j * xInterval + xInterval / 2;
// 因为绘制在第一行
y = i * yInterval;
drawDayText(x, y, str, mNormalTextColor, isToday(theday), isTheday(theday, month, year),
isOverdue(theday, month, year), canvas, offset);
day++;
}
}
}
很多代码跟之前的是重复的,不贴太多。
主要一个贴出CalendarUtils这个工具类,帮助更多人
package tools;
import java.util.Calendar;
import android.util.Log;
public class CalendarUtils {
private static Calendar calendar = Calendar.getInstance();
private static int theDay = calendar.get(Calendar.DAY_OF_MONTH);
private static int theMonth = calendar.get(Calendar.MONTH) + 1;
private static int theYear = calendar.get(Calendar.YEAR);
public static void reset() {
calendar.set(theYear, theMonth - 1, theDay);
printCalendar();
}
public static Calendar getCanlendar(){
return calendar;
}
public static int getToday() {
return theDay;
}
public static int getTomonth() {
return theMonth;
}
public static int getToyear() {
return theYear;
}
public static int getCurrentYear() {
return calendar.get(Calendar.YEAR);
}
public static int getCurrentMonth() {
return calendar.get(Calendar.MONTH) + 1;
}
public static int getCurrentDate() {
return calendar.get(Calendar.DATE);
}
public static int getCurrentMaxNumOfMonth() {
return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
}
public static int getCurrentFirstWeekdayOfMoth() {
int today = getCurrentDate();
calendar.set(Calendar.DATE, 1);
int weekday = calendar.get(Calendar.DAY_OF_WEEK) - 1;
calendar.set(Calendar.DATE, today);
return weekday;
}
public static void nextMonth() {
calendar.add(Calendar.MONTH, 1);
}
public static void preMonth() {
calendar.add(Calendar.MONTH, -1);
}
public static void printCalendar() {
Log.d("CalendarUtils", getCurrentYear() + "年" + getCurrentMonth() + "月" + getCurrentDate() + "日");
// System.out.println(getCurrentYear() + "年" + getCurrentMonth() + "月" +
// getCurrentDate() + "日");
// System.out.println("总共有" + getCurrentMaxNumOfMonth() + "天" + "第一天是星期"
// + getCurrentFirstWeekdayOfMoth());
}
public static String getNextDay(int year, int month, int day) {
calendar.set(year, month - 1, day + 1);
int currentYear = getCurrentYear();
int currentMonth = getCurrentMonth();
int currentDate = getCurrentDate();
reset();
return currentYear + "-" + currentMonth + "-" + currentDate;
}
public static String getPreDay(int year, int month, int day) {
calendar.set(year, month - 1, day - 1);
int currentYear = getCurrentYear();
int currentMonth = getCurrentMonth();
int currentDate = getCurrentDate();
reset();
return currentYear + "-" + currentMonth + "-" + currentDate;
}
/**
* O 日 距离 T 日 的天数
* @param yearo
* @param montho
* @param dayo
* @param yeart
* @param montht
* @param dayt
* @return
*/
public static int getGapCount(int yearo, int montho, int dayo, int yeart, int montht, int dayt) {
int oldYear = getCurrentYear();
int oldMonth = getCurrentMonth();
int oldDay = getCurrentDate();
calendar.set(yearo, montho, dayo, 0, 0, 0);
long time1 = calendar.getTime().getTime();
calendar.set(yeart, montht, dayt, 0, 0, 0);
long time2 = calendar.getTime().getTime();
calendar.set(oldYear, oldMonth-1 , oldDay);
return (int) ((time1-time2) / (1000 * 60 * 60 * 24));
}
public static boolean isPreDay( int year, int month, int day){
if(getGapCount(year,month,day,theYear,theMonth,theDay)<0){
return true;
}else{
return false;
}
}
public static void set(int year, int month, int day) {
// TODO Auto-generated method stub
calendar.set(year, month, day);
}
}
其实,回头一看,这个日历控件好像不怎么高大上了。。。