1.calendar.xml文件
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.jykenan.CalendarWidget
android:id="@+id/calenaract_calendar_widget"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
2.日历view
public class CalendarWidget extends View implements OnTouchListener {
private Context context;
private Paint paint = new Paint();
private RectF borderRect;
private Rect canlendarRect;
private Rect dayCellContextRect;
private CalendarHeader header;
private DayCell[] dayCells = new DayCell[42];
private Calendar calendar = Calendar.getInstance();
private Calendar todayCalendar = Calendar.getInstance();

private DayCell curDayCell;
private int moveX = 0;// X方向上在onScroll中的移动像素
private GestureDetector detector;// 手势运动委托器

private CalendarAction gestureAction;

private OnCalendarSelectedListenter calendarSelectedListenter;

private SimpleDateFormat chineseDateFormat = new SimpleDateFormat(
"yyyy年MM月dd日");
private int lunaryear;
private int lunarmonth;
private int lunarday;
private boolean leap;
private String chineseNumber[] = {"一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"};
private final long[] lunarInfo = new long[] { 0x04bd8, 0x04ae0, 0x0a570,
0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0,
0x0ada2, 0x095b0, 0x14977, 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50,
0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, 0x06566,
0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0,
0x1c8d7, 0x0c950, 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4,
0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, 0x06ca0, 0x0b550,
0x15355, 0x04da0, 0x0a5d0, 0x14573, 0x052d0, 0x0a9a8, 0x0e950,
0x06aa0, 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260,
0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0, 0x04dd5, 0x04ad0,
0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6,
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40,
0x0af46, 0x0ab60, 0x09570, 0x04af5, 0x04970, 0x064b0, 0x074a3,
0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0, 0x0c960,
0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0,
0x092d0, 0x0cab5, 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9,
0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, 0x07954, 0x06aa0,
0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65,
0x0d530, 0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0,
0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0, 0x056d0, 0x055b2,
0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0 };

public CalendarWidget(Context context) {
super(context, null);
}

public CalendarWidget(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

private void init(Context context) {
this.context = context;
gestureAction = CalendarAction.CALENDAR_DEFAULT;

setOnTouchListener(this);
detector = new GestureDetector(context, new MyGestureListener());
setLongClickable(true);// 这样才能启动Gesture [dʒestʃə]

header = new CalendarHeader(this.context);
for (int i = 0; i < 42; i++) {
dayCells[i] = new DayCell();
}
setCalendar();
}

private void setCalendar() {
// 先清除状态
for (int i = 0; i < 42; i++) {
dayCells[i].clearState();
}

// 根据日期设置DayCell
// 设置日历是本月1号
calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
1);

// 获得本月有多少天
int maxday = calendar.getActualMaximum(Calendar.DATE);
// 第一天是星期几
int dayofweek = calendar.get(Calendar.DAY_OF_WEEK) - 1;

for (int i = dayofweek; i < maxday + dayofweek; i++) {
setLunar(calendar.getTime().getTime());
int day = calendar.get(Calendar.DATE);
dayCells[i].setText("" + day);
if(lunarday==1){
dayCells[i].setLunarText(""+lunarmonth+"月");
}else{
dayCells[i].setLunarText(""+getChinaDayString(lunarday));
}
// 今天 (年月日三者相等 小时毫秒这些不比较)
if (todayCalendar.get(Calendar.YEAR) == calendar.get(Calendar.YEAR)
&& todayCalendar.get(Calendar.MONTH) == calendar
.get(Calendar.MONTH)
&& todayCalendar.get(Calendar.DATE) == calendar
.get(Calendar.DATE)) {
dayCells[i].setToday(true);
}
// 周六
if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
dayCells[i].setFontColor(context.getResources().getColor(
R.color.canlendar_week_6));
}
// 周日
if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
dayCells[i].setFontColor(context.getResources().getColor(
R.color.canlendar_week_7));
}
// 设置值
dayCells[i].setYear(calendar.get(Calendar.YEAR));
dayCells[i].setMonth(calendar.get(Calendar.MONTH));
dayCells[i].setDay(calendar.get(Calendar.DATE));

dayCells[i].setLunarYear(lunaryear);
dayCells[i].setLunarMonth(lunarmonth);
dayCells[i].setLunarDay(lunarday);
// +1天,注意运行到最后,就是第二个月的第一天了。
calendar.add(Calendar.DATE, 1);
}
// 最后要重新设置为本月的第一天,这样为了滑动作准备
calendar.add(Calendar.MONTH, -1);
}
private String getChinaDayString(int day) {
String chineseTen[] = {"初", "十", "廿", "三"};
int n = day % 10 == 0 ? 9 : day % 10 - 1;
if (day > 30)
return "";
if (day == 10)
return "初十";
else
return chineseTen[day / 10] + chineseNumber[n];
}
private void setLunar(long currentStamp) {
@SuppressWarnings("unused")
int yearCyl, monCyl, dayCyl;
int leapMonth = 0;
Date baseDate = null;
try {
baseDate = chineseDateFormat.parse("1900年1月31日");
// } catch (ParseException e) {
// e.printStackTrace(); //To change body of catch statement use
// Options | File Templates.
} catch (java.text.ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

// 求出和1900年1月31日相差的天数
int offset = (int) ((currentStamp - baseDate.getTime()) / 86400000L);
dayCyl = offset + 40;
monCyl = 14;

// 用offset减去每农历年的天数
// 计算当天是农历第几天
// i最终结果是农历的年份
// offset是当年的第几天
int iYear, daysOfYear = 0;
for (iYear = 1900; iYear < 2050 && offset > 0; iYear++) {
daysOfYear = yearDays(iYear);
offset -= daysOfYear;
monCyl += 12;
}
if (offset < 0) {
offset += daysOfYear;
iYear--;
monCyl -= 12;
}
// 农历年份
lunaryear = iYear;

yearCyl = iYear - 1864;
leapMonth = leapMonth(iYear); // 闰哪个月,1-12
leap = false;

// 用当年的天数offset,逐个减去每月(农历)的天数,求出当天是本月的第几天
int iMonth, daysOfMonth = 0;
for (iMonth = 1; iMonth < 13 && offset > 0; iMonth++) {
// 闰月
if (leapMonth > 0 && iMonth == (leapMonth + 1) && !leap) {
--iMonth;
leap = true;
daysOfMonth = leapDays(lunaryear);
} else
daysOfMonth = monthDays(lunaryear, iMonth);

offset -= daysOfMonth;
// 解除闰月
if (leap && iMonth == (leapMonth + 1))
leap = false;
if (!leap)
monCyl++;
}
// offset为0时,并且刚才计算的月份是闰月,要校正
if (offset == 0 && leapMonth > 0 && iMonth == leapMonth + 1) {
if (leap) {
leap = false;
} else {
leap = true;
--iMonth;
--monCyl;
}
}
// offset小于0时,也要校正
if (offset < 0) {
offset += daysOfMonth;
--iMonth;
--monCyl;
}
lunarmonth = iMonth;
lunarday = offset + 1;
}

// ====== 传回农历 y年的总天数
final private int yearDays(int y) {
int i, sum = 348;
for (i = 0x8000; i > 0x8; i >>= 1) {
if ((lunarInfo[y - 1900] & i) != 0)
sum += 1;
}
return (sum + leapDays(y));
}

// ====== 传回农历 y年闰月的天数
final private int leapDays(int y) {
if (leapMonth(y) != 0) {
if ((lunarInfo[y - 1900] & 0x10000) != 0)
return 30;
else
return 29;
} else
return 0;
}

// ====== 传回农历 y年闰哪个月 1-12 , 没闰传回 0
final private int leapMonth(int y) {
return (int) (lunarInfo[y - 1900] & 0xf);
}

// ====== 传回农历 y年m月的总天数
final private int monthDays(int y, int m) {
if ((lunarInfo[y - 1900] & (0x10000 >> m)) == 0)
return 29;
else
return 30;
}

@Override
public boolean onTouch(View arg0, MotionEvent e) {
detector.onTouchEvent(e);
if (e.getAction() == MotionEvent.ACTION_UP) {
if (gestureAction == CalendarAction.CALENDAR_RIGHT) {
moveX = 0;
calendar.add(Calendar.MONTH, -1);
setCalendar();
anycInvalidate();
}
if (gestureAction == CalendarAction.CALENDAR_LEFT) {
moveX = 0;
calendar.add(Calendar.MONTH, 1);
setCalendar();
anycInvalidate();
}
if (gestureAction == CalendarAction.CALENDAR_NONE) {
moveX = 0;
setCalendar();
anycInvalidate();
}
// 转发事件
if (gestureAction == CalendarAction.CALENDAR_TOUCH) {
if (calendarSelectedListenter != null) {
calendarSelectedListenter.onSelected(curDayCell.getYear(),
curDayCell.getMonth(), curDayCell.getDay());
}
}
gestureAction = CalendarAction.CALENDAR_DEFAULT;// 清除状态
}

return false;
}

private void anycInvalidate() {
new Thread(new Runnable() {

@Override
public void run() {
postInvalidate();
}

}).start();
}

@Override
public void onDraw(Canvas canvas) {
compute();
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG
| Paint.FILTER_BITMAP_FLAG));
drawBackGround(canvas);
// drawBorder(canvas);
drawTitle(canvas);
header.drawWeekHeader(canvas, canlendarRect);
drawDayCells(canvas);
}

private void drawDayCells(Canvas canvas) {
for (DayCell cell : dayCells) {
cell.drawDay(canvas);
}
}
private void drawBackGround(Canvas canvas){

Bitmap bmp = ((BitmapDrawable)getResources ().getDrawable( R.drawable.summer)).getBitmap( ) ;
Bitmap newbmp=Bitmap.createScaledBitmap(bmp,getWidth(),getHeight(),true);
canvas.drawBitmap( newbmp, 0, 0, null ) ;
}
private void drawBorder(Canvas canvas) {
paint.setColor(context.getResources()
.getColor(R.color.canlendar_border));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
canvas.drawRoundRect(borderRect, 10, 10, paint);
}

private void compute() {
// Stroke宽度为3,留白1,这样就空4出来
borderRect = new RectF(4, 4, getWidth() - 4, getHeight() - 4);
canlendarRect = new Rect(moveX + 14, 35, getWidth() + moveX - 14,
getHeight() - 14);
header.setHeaderRect(canlendarRect);
dayCellContextRect = header.computeDayCellRect();
// 计算每个Cell的高和宽
int cellWidth = dayCellContextRect.width() / 7;
int cellHeight = dayCellContextRect.height() / 6;// 最多6行
int startX = dayCellContextRect.left;
int startY = dayCellContextRect.top;
int colIndex = 1;
for (int i = 0; i < 42; i++) {
Rect rect = new Rect(startX, startY, startX + cellWidth, startY
+ cellHeight);
// 7个一行
if (colIndex % 7 != 0) {
startX = startX + cellWidth;
}
// 换行,Y下移一个cell的高度,X回到起点
else {
startX = dayCellContextRect.left;
startY = startY + cellHeight;
}
colIndex++;
DayCell dayCell = dayCells[i];
dayCell.setRect(rect);
}
}

private void drawTitle(Canvas canvas) {
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(15);
paint.setFakeBoldText(true);// 伪粗体,中文使用
paint.setTextAlign(Align.CENTER);
paint.setColor(context.getResources()
.getColor(R.color.canlendar_header));
int month = calendar.get(Calendar.MONTH) + 1;
canvas.drawText(calendar.get(Calendar.YEAR) + "年" + month + "月",
canlendarRect.left + canlendarRect.width()/2,
canlendarRect.top - 4, paint);
}

class MyGestureListener extends SimpleOnGestureListener {
// 用户按下触摸屏,尚未松开或拖动,由一个MotionEvent.ACTION_DOWN触发.
// 注意和onDown的区别,强调的是没有松开或者拖动的状态
@Override
public void onShowPress(MotionEvent arg0) {
super.onShowPress(arg0);
}

// 用户按下触摸屏,由一个MotionEvent.ACTION_DOWN触发
@Override
public boolean onDown(MotionEvent e) {
// 只增加背景,不触发处理
for (final DayCell cell : dayCells) {
if (cell.isClicked((int) e.getX(), (int) e.getY())
&& cell.getText() != null) {
if (curDayCell != null) {

curDayCell.setSelected(false);
// 更新局部,提高性能
CalendarWidget.this.invalidate(
curDayCell.getRect().left,
curDayCell.getRect().top,
curDayCell.getRect().right,
curDayCell.getRect().bottom);
}
cell.setSelected(true);
CalendarWidget.this.invalidate(cell.getRect().left,
cell.getRect().top, cell.getRect().right,
cell.getRect().bottom);
curDayCell = cell;
gestureAction = CalendarAction.CALENDAR_TOUCH;
}
}
return false;
}

// 用户(用户按下触摸屏后)松开,由MotionEvent.ACTION_UP触发
// 如果在onDown和onFling之中2选1,那么使用这个事件是可以区分的
@Override
public boolean onSingleTapUp(MotionEvent arg0) {
return false;
}

// 长按 多个ACTION_DOWN触发
@Override
public void onLongPress(MotionEvent arg0) {
super.onLongPress(arg0);
}

// 用户按下触摸屏,快速移动后松开,由一个MotionEvent.ACTION_DOWN,多个ACTION_MOVE,一个ACTION_UP触发.
// velocityX和velocityY分别表示在X和Y方向上移动的速度
// e1 第一个Down,e2最后一个move
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
// Toast.makeText(context, "飞行了", Toast.LENGTH_SHORT).show();
// 向左,速度每秒>200个像素
if ((e1.getX() - e2.getX() > getWidth() / 2)
&& (Math.abs(velocityX)) > 200) {
gestureAction = CalendarAction.CALENDAR_LEFT;
}
// 向右,速度每秒>200个像素
else if ((e2.getX() - e1.getX() > getWidth() / 2)
&& (Math.abs(velocityX)) > 200) {
gestureAction = CalendarAction.CALENDAR_RIGHT;
}

return false;
}

// 按下屏幕,并拖动,一个DOWM,多个MOVE触发
// e1 第一个Down,e2当前移动的point
// distance表示Scroll过程中的移动的像素。distanceX >0 表示向左,distanceY >0
// 表示向上,两个都大就是左上
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
moveX -= distanceX;
new Thread(new Runnable() {

@Override
public void run() {
CalendarWidget.this.postInvalidate();
}

}).start();
// 向右
if (e2.getX() - e1.getX() > getWidth() / 2) {
gestureAction = CalendarAction.CALENDAR_RIGHT;
}
// 向左
else if (e1.getX() - e2.getX() > getWidth() / 2) {
gestureAction = CalendarAction.CALENDAR_LEFT;
} else {
gestureAction = CalendarAction.CALENDAR_NONE;
}
return false;
}
}

public void setCalendarSelectedListenter(
OnCalendarSelectedListenter calendarSelectedListenter) {
this.calendarSelectedListenter = calendarSelectedListenter;
}
}

enum CalendarAction {
CALENDAR_DEFAULT, CALENDAR_NONE, CALENDAR_TOUCH, CALENDAR_LEFT, CALENDAR_RIGHT,
}
3.日历标题
public class CalendarHeader
{
private Context context;
private Rect headerRect;
private static final String[] week = new String[]{"日","一","二","三","四","五","六"};
public Rect getHeaderRect() {
return headerRect;
}

public void setHeaderRect(Rect headerRect) {
this.headerRect = headerRect;
}

public CalendarHeader(Context context)
{
this.context = context;
}

public void drawWeekHeader(Canvas canvas,Rect canlendarRect)
{
int cellWidth = canlendarRect.width()/7;
Paint paint = new Paint();
//这里可以实现移动 ,以后需要提取
int start = canlendarRect.left;
paint.setTextAlign(Align.CENTER);
paint.setTextSize(15);
for(int i=0;i<7;i++)
{
paint.setColor(Color.BLACK);
if(week[i].equals("六"))
{
paint.setColor(context.getResources().getColor(R.color.canlendar_week_6));
}
if(week[i].equals("日"))
{
paint.setColor(context.getResources().getColor(R.color.canlendar_week_7));
}
canvas.drawText(week[i], start+cellWidth/2, canlendarRect.top+20, paint);
start+=cellWidth;
}
}
public Rect computeDayCellRect()
{
return new Rect(headerRect.left,headerRect.top+40,headerRect.right,headerRect.bottom);
}
}
4.填写每天的内容
class DayCell
{
private Rect rect;
private String text;
private String lunarText;
private boolean selected;
private int fontColor = Color.BLACK;
private int year;
private int month;
private int day;
private int lunarYear;
private int lunarMonth;
private int lunarDay;
private boolean today;

public boolean isClicked(int mouseX,int mouseY)
{
if(rect.contains(mouseX, mouseY))
{
return true;
}
return false;
}
public void drawDay(Canvas canvas)
{
Paint paint = new Paint();
//选中状态,增加背景色
if(selected && !today)
{
Shader bgShader = new LinearGradient(rect.left, rect.top, rect.right, rect.bottom, Color.parseColor("#43682f"), Color.parseColor("#5cc35c"), Shader.TileMode.MIRROR);
paint.setShader(bgShader);
canvas.drawRoundRect(new RectF(rect), 3, 3, paint);
}
//是今天,增加背景标识
if(today)
{
Shader bgShader = new LinearGradient(rect.left, rect.top, rect.right, rect.bottom, Color.parseColor("#ff8200"), Color.parseColor("#ffcea8"), Shader.TileMode.MIRROR);
paint.setShader(bgShader);
canvas.drawRoundRect(new RectF(rect), 3, 3, paint);
}
paint.setShader(null);
paint.setColor(fontColor);
paint.setTextAlign(Align.CENTER);
//rect的开始+宽度/2,这样得到rect的X中点坐标
//计算字体高度,计算出居中的定位
FontMetrics fm = paint.getFontMetrics();
int fontHeight = (int) Math.ceil(fm.descent - fm.top)-6;
if(text != null)
{
canvas.drawText(text, rect.left+rect.width()/2, rect.bottom-(rect.height()-fontHeight)/2-7, paint);
canvas.drawText(lunarText, rect.left+rect.width()/2, rect.bottom-(rect.height()-fontHeight)/4, paint);
}
}
public void clearState()
{
setText(null);
setSelected(false);
setDay(0);
setMonth(0);
setYear(0);
setLunarText(null);
setLunarYear(0);
setLunarMonth(0);
setLunarDay(0);
setToday(false);
}
public Rect getRect() {
return rect;
}
public void setRect(Rect rect) {
this.rect = rect;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getLunarText() {
return lunarText;
}
public void setLunarText(String lunarText) {
this.lunarText = lunarText;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getLunarYear(){
return lunarYear;
}
public void setLunarYear(int lunarYear){
this.lunarYear=lunarYear;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getLunarMonth() {
return lunarMonth;
}
public void setLunarMonth(int lunarmonth) {
this.lunarMonth = lunarmonth;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public int getLunarDay() {
return lunarDay;
}
public void setLunarDay(int lunarday) {
this.lunarDay = lunarday;
}
public int getFontColor() {
return fontColor;
}
public void setFontColor(int fontColor) {
this.fontColor = fontColor;
}
public boolean isToday()
{
return today;
}
public void setToday(boolean today)
{
this.today = today;
}

}
(4)接口实现
public interface OnCalendarSelectedListenter
{
public void onSelected(int year,int month,int day);
}

更多相关文章

  1. 【Android】Android控件之Seekbar拖动条的使用
  2. Android(安卓)wifi触发回连ap的过程
  3. android:broadcast_01
  4. Android之各种事件触发方案
  5. android GestureDetector应用
  6. Android:控件样式触发
  7. Android(安卓)Gesture Detector
  8. Android上用TextView实现跑马灯
  9. Android: Android中各种onTouch事件

随机推荐

  1. Android 2D绘图总结
  2. android WebView java与js相互调用
  3. EditText对行光标默认第一行问题
  4. Android 中的 R.class,减小 Apk 包大小
  5. Android支持的媒体格式
  6. android仿京东商城例子
  7. 使用ContentProvider时出现SecurityExcep
  8. android快捷卸载第三方应用
  9. android socket客户端app
  10. Android——ScrollView