赞
踩
日历有阳(公)历、阴(农)历之分,咱们从下面两个方面区分一下(当然,他们的区别还是挺多的,有兴趣的可以研究研究)
好了,咱们言归正传。日历翻译成英文就是Calendar,然而咱们平使用的是阳(公)历,翻译成英文就是GregorianCalendar。而咱们要自定义的日历控件主要用到的就是Calendar这个类
Calendar mCalendar = Calendar.getInstance();
Calendar mCalendar = new GregorianCalendar();
这里有人就回问,那这两种初始化有什么不同。其实,这两种初始化方式并没有什么不同,殊途同归。咱们可以通过源码去了解一下就清楚了
首先咱们先看一下Calendar这个类的源码,并且这个类还是一个抽象类。这里我只挑出了相关的代码
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
/**
* Gets a calendar using the default time zone and locale. The
* <code>Calendar</code> returned is based on the current time
* in the default time zone with the default locale.
*
* @return a Calendar.
*/
public static Calendar getInstance(){
Calendar cal = createCalendar(TimeZone.getDefaultRef(),
Locale.getDefault(Locale.Category.FORMAT));
cal.sharedZone = true;
return cal;
}
public static Calendar getInstance(TimeZone zone){
return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
}
public static Calendar getInstance(Locale aLocale){
Calendar cal = createCalendar(TimeZone.getDefaultRef(), aLocale);
cal.sharedZone = true;
return cal;
}
public static Calendar getInstance(TimeZone zone,Locale aLocale){
return createCalendar(zone, aLocale);
}
/**
* 哈哈,看到这里是不是就明白了。
* 他们最终都会去new一个GregorianCalendar实实例给你返回
*/
private static Calendar createCalendar(TimeZone zone,Locale aLocale){
return new GregorianCalendar(zone, aLocale);
}
}
来来来,我们来看一下GregorianCalendar这个类到底是何方神圣
public class GregorianCalendar extends Calendar {
/**
* Constructs a default <code>GregorianCalendar</code> using the current time
* in the default time zone with the default locale.
*/
public GregorianCalendar() {
this(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT));
setZoneShared(true);
}
}
看到这里是不是就明白了,其实这两种初始化是没有区别的。GregorianCalendar这个类也是继承了Calendar的。
获取值
方法 | 结果 |
---|---|
cal.get(Calendar.YEAR) | 年 |
cal.get(Calendar.MONTH) + 1 | 月(必须要+1) |
cal.get(Calendar.DATE) | 日 |
cal.get(Calendar.HOUR_OF_DAY) | 时 |
cal.get(Calendar.MINUTE) | 分 |
cal.get(Calendar.SECOND) | 秒 |
cal.get(Calendar.DAY_OF_WEEK) | 星期(Locale.ENGLISH情况下,周日是1,剩下自己推算) |
设置值
方法 | 结果 |
---|---|
cal.set(2013, 5, 4, 13, 44, 51) | 年月日时分秒(月份0代表1月) |
cal.set(Calendar.YEAR, 2014) | 年 |
cal.set(Calendar.MONTH, 7) | 月(月份0代表1月) |
cal.set(Calendar.DATE, 11) | 日 |
cal.set(Calendar.HOUR_OF_DAY, 15) | 时 |
cal.set(Calendar.MINUTE, 33) | 分 |
cal.set(Calendar.SECOND, 32) | 秒 |
运算值
方法 | 结果 |
---|---|
cal.add(Calendar.YEAR, 1) | 年 |
cal.add(Calendar.MONTH, 1) | 月 |
cal.add(Calendar.DATE, 1) | 日 |
cal.add(Calendar.HOUR_OF_DAY, -1) | 时 |
cal.add(Calendar.MINUTE, 1) | 分 |
cal.add(Calendar.SECOND, 1) | 秒 |
cal.add(Calendar.DATE, 7) | 周 |
首先,先来看一下最终要实现什么样的效果
接下来先对要实现的效果进行分析,整个布局可分为一下三大部分:
结合以前所学的MVC模式,即模型(Model)-视图(View)-控制器(Controller)对整个布局进行分析
Model:CalendarDate
咱们主要关注最底部的当前月份信息,咱们可把最底部的View拆分成一个一个小方块,每一个小方块即代表的是一天。而小方块就是我们要建立的数据模型。
我们把每一个小方块都看成是一个对象,可以根据上面的布局分析得出每个对象的属性:
这样,我们的CalendarDate就出来了
private class CalendarDate {
private int year;
private int month;
private int day;
/**
* 单个日期格子的宽
*/
private int width;
/**
* 单个日期格子的高
*/
private int height;
/**
* 日期的文本
*/
private String text;
/**
* 文本字体的颜色
*/
private int textColor;
/**
* 日期背景的类型 0代表无任何背景,1代表即是当前日期
*/
private int backgroundStyle;
/**
* 字体的大小
*/
private float textSize;
/**
* 背景的大小
*/
private int backgroundSize;
/**
* 字体在第几行
*/
private int location_x;
/**
* 字体在第几列
*/
private int location_y;
/**
* 创建日期对象
*
* @param width 每个日期格子的宽度
* @param height 每个日期格子的高度
*/
private CalendarDate(int width, int height) {
this.width = width;
this.height = height;
}
......
}
而每个模型只有属性是不行的,还得有行为。这里我们让它可以画出自己,调用自己的drawDays()方法即可
private class CalendarDate {
......
/**
* 画自己
*
* @param canvas 要画的画布
* @param paint 画笔
*/
private void drawDays(Canvas canvas, Paint paint) {
//取窄的边框做为边框大小
backgroundSize = width > height ? height : width;
//画背景
drawBackground(canvas, paint);
//画数字
drawText(canvas, paint);
}
/**
* 画数字
*/
private void drawText(Canvas canvas, Paint paint) {
//根据单个框的大小设置字体的大小
textSize = backgroundSize / 3;
paint.setTextSize(textSize);
paint.setColor(textColor);
paint.setStyle(Paint.Style.FILL);
//计算文字的宽度
Rect rect = new Rect();
paint.getTextBounds(text, 0, text.length(), rect);
int w = rect.width();
//计算画文字的位置
float x = location_x * width + (width - w) / 2;
float y = location_y * height + (height + textSize / 2) / 2;
canvas.drawText(text, x, y, paint);
}
/**
* 画背景
*/
private void drawBackground(Canvas canvas, Paint paint) {
//画背景 根据背景状态设置画笔类型
if (backgroundStyle == 0) {
return;
}
paint.setColor(0xFF095CBF);
paint.setStyle(Paint.Style.FILL);
canvas.drawRect(location_x * width, location_y * height, location_x * width + width, location_y * height + height, paint);
}
}
Controller:CalendarManager
这里的的Controller就是要将我们的Model和View连接起来,并且进行相应的管理。可以简化外部使用,即CalendarManager
/**
* 管理日期类
*/
private class CalendarManager {
/**
* 记录当前的时间
*/
private String currentTime;
/**
* 当前的日期
*/
private int current = -1;
/**
* 储存当前的日期
*/
private int tempCurrent = -1;
/**
*
*/
private String[] weeks = {"S", "M", "T", "W", "T", "F", "S"};
private String[] dayArray = {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15",
"16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"};
/**
* 设置当前的时间
*
* @param currentTime 当前的时间
*/
private void setCurrentTime(String currentTime) {
this.currentTime = currentTime;
}
/**
* 获取当前的时间
*/
private String getCurrentTime() {
return currentTime;
}
private void setTempCurrent(int tempCurrent) {
this.tempCurrent = tempCurrent;
}
private int getTempCurrent() {
return tempCurrent;
}
private void setCurrent(int current) {
this.current = current;
}
/**
* 根据日历对象创建日期集合
*
* @param calendar 日历
* @param width 控件的宽度
* @param height 控件的高度
* @return 返回的天数的集合
*/
private List<CalendarDate> createDayByCalendar(Calendar calendar, int width, int height) {
List<CalendarDate> calendarDates = new ArrayList<>();
...
return calendarDates;
}
}
View:CalendarView
最后,最底部的月份布局信息决定得采用继承View来达到最终的效果,所以说继承自View的CalendarView就是我们MVC中的视图层了
/**
* 自定义日期View
*/
private class CalendarView extends View {
List<CalendarDate> mCalendarDateList;
/**
* 画笔
*/
private Paint paint;
/**
* 重绘,并更改当前状态,由于绘图是在calendar基础上进行绘制的,所以改变calendar就可以改变图片
*/
public void upDateDraw() {
if ((mCalendar.get(Calendar.MONTH) + "" + mCalendar.get(Calendar.YEAR)).equals(mCalendarManager.getCurrentTime())) {
mCalendarManager.setCurrent(mCalendarManager.getTempCurrent());
} else {
mCalendarManager.setCurrent(-1);
}
invalidate();
}
public CalendarView(Context context) {
super(context);
//初始化控件
initView();
}
public CalendarView(Context context, AttributeSet attrs) {
super(context, attrs);
//初始化控件
initView();
}
public CalendarView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//初始化控件
initView();
}
/***
* 初始化控件
*/
private void initView() {
paint = new Paint();
paint.setAntiAlias(true);
mCalendar = Calendar.getInstance();
mCalendarManager.setCurrent(mCalendar.get((Calendar.DAY_OF_MONTH)));
mCalendarManager.setTempCurrent(mCalendar.get(Calendar.DAY_OF_MONTH
));
mCalendarManager.setCurrentTime(mCalendar.get(Calendar.MONTH) + "" + mCalendar.get(Calendar.YEAR));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//获取day集合并绘制
mCalendarDateList = mCalendarManager.createDayByCalendar(mCalendar, getMeasuredWidth(), getMeasuredHeight());
for (CalendarDate calendarDate : mCalendarDateList) {
calendarDate.drawDays(canvas, paint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (MotionEvent.ACTION_DOWN == event.getAction()) {
//判断点击的是哪个日期
float x = event.getX();
float y = event.getY();
//计算点击的是哪个日期
int locationX = (int) (x * 7 / getMeasuredWidth());
int locationY = (int) (y * 7 / getMeasuredHeight());
if (locationY == 0) {
return super.onTouchEvent(event);
}
CalendarDate calendarDate = findDate(locationX, locationY);
if (null != mOnSelectChangeListener && null != calendarDate) {
mOnSelectChangeListener.getDate(calendarDate.year, calendarDate.month, calendarDate.day);
hide();
}
}
return super.onTouchEvent(event);
}
private CalendarDate findDate(int locationX, int locationY) {
for (CalendarDate calendarDate : mCalendarDateList) {
if (calendarDate.location_x == locationX && calendarDate.location_y == locationY)
return calendarDate;
}
return null;
}
}
到这里,整个自定义日历控件的MVC就已经全部说完了。这里我将这三个部分全部封装到一个LinearLayout里面了,方便导入和使用,但是内部还是以MVC为架构进行模块区分。由于文件代码过长,这里就不贴了,想要的小伙伴可以去这里下载源码下载
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。