Android自定义日历控件,自带农历节假日,已经开源,即取即用~
来源:互联网 发布:北京java工程师待遇 编辑:程序博客网 时间:2024/06/08 10:24
关注本人的更多博客:http://www.cnblogs.com/liushilin/
该自定义日历控件已经开源:github地址
可能不少的小伙伴都有看楼主昨天发的自定义日历控件,虽然实现功能不多,但也还算将就吧。
没有看的小伙伴如果有兴趣的话可以去看看:http://www.cnblogs.com/liushilin/p/5759750.html
但是看了的小伙伴就很心急了,说楼主上传到gitHub的东西有问题,楼主下载来看了看,基本都没问题吧,没弄好的小伙伴自己再去我的github首页提取哦~因为我还上传了一个新的。
有的小伙伴又不满意了,楼主,你发的东西代码量那么大也搞得很麻烦,小生纵有远大理想,可思前虑后,还是不得正果,倘若楼主能指点一二便好了。
有的小伙伴说,楼主,你的东西就不能封装一下吗?封装一下,方便你我他~
有的小伙伴说,楼主,你的东西是挺不错,但是界面也略丑了吧~这里gank到22楼住的小伙伴说,中间分隔太低,信息太密集,建议添加行间距和留白,希望能做的漂亮些~
......
小主已成功gank到各位的中肯建议,但是不要慌,大家一个一个来。
大家的建议小主都非常地认可,所以楼主觉得把整个东西该封装的算法封装起来,也UI也弄得漂亮一些。
照例先给个运行图吧~
下面给大家讲解一下我们这个自定义日历的简单思路,肯定还是使用GridView的。
由于昨天是纯定义画的UI,所以大家看起来可能有所困难,所以今天楼主简化了UI,把UI放在了xml文件中,供大家食用。
楼主不是废话多的人,具体看代码,代码中我也做了更多的注释。
1)先准备好我们的算法,我们现在把它完全分离。
闰年月的算法应该大家都懂吧。SpecialCalendar.java
三个方法,判断闰年,这个可能只要学程序前面肯定就学了的。
对于判断月份,貌似也不用多说,就一个switch语句,闰年的时候2月是29天,平年是28天。
还有一个判断某一天是星期几的,这个在Calendar中已经很明显了,但是要注意的是Month在calendar中是0-11
1 package com.example.nanchen.calendarviewdemo.view; 2 3 import java.util.Calendar; 4 5 /** 6 * 闰年月算法 7 * 8 * @author nanchen 9 * @date 2016-08-11 08:54:4410 *11 */12 public class SpecialCalendar {13 14 private int daysOfMonth = 0; // 某月的天数15 private int dayOfWeek = 0; // 具体某一天是星期几16 17 // 判断是否为闰年18 public boolean isLeapYear(int year) {19 if (year % 100 == 0 && year % 400 == 0) {20 return true;21 } else if (year % 100 != 0 && year % 4 == 0) {22 return true;23 }24 return false;25 }26 27 // 得到某月有多少天数28 public int getDaysOfMonth(boolean isLeapYear, int month) {29 switch (month) {30 case 1:31 case 3:32 case 5:33 case 7:34 case 8:35 case 10:36 case 12:37 daysOfMonth = 31;38 break;39 case 4:40 case 6:41 case 9:42 case 11:43 daysOfMonth = 30;44 break;45 case 2:46 if (isLeapYear) {47 daysOfMonth = 29;48 } else {49 daysOfMonth = 28;50 }51 52 }53 return daysOfMonth;54 }55 56 /**57 * 指定某年中的某月的第一天是星期几58 */59 public int getWeekdayOfMonth(int year, int month) {60 Calendar cal = Calendar.getInstance();61 cal.set(year, month - 1, 1);62 dayOfWeek = cal.get(Calendar.DAY_OF_WEEK) - 1;63 return dayOfWeek;64 }65 66 }
2)然后带来的是算农历的算法。LunarCalendar.java
就算法本身而言,楼主参考了网上留下的算法。基本就是定义好这些要用的数组,然后考虑完所有的情况。
这里需要心细严谨一些,毕竟你或许错一个参数,一个值就会让你一直搞不出原因,所以写出算法不妨先在Eclipse中看看控制台运行的结果是否正确吧。
高配置随便带Android studio的童鞋当我没说。
这里需要注意的是,我们要做农历,肯定必须想到年份,闰月,闰月是哪个月,这个月有多少天,这些都是需要考虑的。
楼主这简单添加几个节日,节日大家可以随便添加哦。只是一个显示问题。
1 package com.example.nanchen.calendarviewdemo.view; 2 3 import java.text.ParseException; 4 import java.text.SimpleDateFormat; 5 import java.util.Date; 6 import java.util.Locale; 7 8 /** 9 * 农历算法 10 * 11 * @author nanchen 12 * @date 2016-08-11 08-57:24 13 */ 14 public class LunarCalendar { 15 private int year; // 农历的年份 16 private int month; 17 private int day; 18 private String lunarMonth; // 农历的月份 19 public int leapMonth = 0; // 闰的是哪个月 20 21 final static String chineseNumber[] = {"一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"}; 22 static SimpleDateFormat chineseDateFormat = new SimpleDateFormat("yyyy年MM月dd日", Locale.CHINA); 23 final static long[] lunarInfo = new long[]{ // 24 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, // 25 0x055d2, 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, // 26 0x095b0, 0x14977, 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, // 27 0x09570, 0x052f2, 0x04970, 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, // 28 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, // 29 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, 0x06ca0, 0x0b550, 0x15355, 0x04da0, // 30 0x0a5d0, 0x14573, 0x052d0, 0x0a9a8, 0x0e950, 0x06aa0, 0x0aea6, 0x0ab50, 0x04b60, // 31 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0, 0x04dd5, // 32 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6, 0x095b0, // 33 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 34 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, // 35 0x092e0, 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, // 36 0x092d0, 0x0cab5, 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, // 37 0x15176, 0x052b0, 0x0a930, 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, // 38 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, 0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, // 39 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, // 40 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0}; 41 42 // 农历部分假日 43 final static String[] lunarHoliday = new String[]{"0101 春节", "0115 元宵", "0505 端午", "0707 情人", "0715 中元", "0815 中秋", "0909 重阳", "1208 腊八", "1224 小年", "0100 除夕"}; 44 45 // 公历部分节假日 46 final static String[] solarHoliday = new String[]{ // 47 "0101 元旦", "0214 情人", "0308 妇女", "0312 植树", "0315 消费者权益日", "0401 愚人", "0501 劳动", "0504 青年", // 48 "0512 护士", "0601 儿童", "0701 建党", "0801 建军", "0808 父亲", "0910 教师", "0928 孔子诞辰",// 49 "1001 国庆", "1006 老人", "1024 联合国日", "1112 孙中山诞辰纪念", "1220 澳门回归纪念", "1225 圣诞"}; 50 51 /** 52 * 传回农历 y年的总天数 53 * 54 * @param y 年份 55 * @return 该年的总天数 56 */ 57 private static int yearDays(int y) { 58 int i, sum = 348; 59 for (i = 0x8000; i > 0x8; i >>= 1) { 60 if ((lunarInfo[y - 1900] & i) != 0) 61 sum += 1; 62 } 63 return (sum + leapDays(y)); 64 } 65 66 /** 67 * 传回农历 y年闰月的天数 68 * 69 * @param y 年份 70 * @return 改年闰月天数 71 */ 72 private static int leapDays(int y) { 73 if (leapMonth(y) != 0) { 74 if ((lunarInfo[y - 1900] & 0x10000) != 0) 75 return 30; 76 else 77 return 29; 78 } else 79 return 0; 80 } 81 82 /** 83 * 传回农历 y年闰哪个月 1-12 , 没闰传回 0 84 * 85 * @param y 年份 86 * @return 改年闰月的月数 87 */ 88 private static int leapMonth(int y) { 89 return (int) (lunarInfo[y - 1900] & 0b1111); 90 } 91 92 /** 93 * 传回农历 y年m月的总天数 94 * 95 * @param y 年份 96 * @param m 月份 97 * @return 该月份的总天数 98 */ 99 private static int monthDays(int y, int m) {100 if ((lunarInfo[y - 1900] & (0x10000 >> m)) == 0)101 return 29;102 else103 return 30;104 }105 106 /**107 * 传回农历 y年的生肖108 *109 * @param year 年份110 * @return 生肖111 */112 public String animalsYear(int year) {113 final String[] Animals = new String[]{"鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"};114 return Animals[(year - 4) % 12];115 }116 117 /**118 * 传入 月日的offset 传回干支, 0=甲子119 */120 private static String cyclicalm(int num) {121 final String[] Gan = new String[]{"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"};122 final String[] Zhi = new String[]{"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"};123 return (Gan[num % 10] + Zhi[num % 12]);124 }125 126 /**127 * 传入 offset 传回干支, 0=甲子128 */129 public String cyclical(int year) {130 int num = year - 1900 + 36;131 return (cyclicalm(num));132 }133 134 public static String getChinaDayString(int day) {135 String chineseTen[] = {"初", "十", "廿", "卅"};136 int n = day % 10 == 0 ? 9 : day % 10 - 1;137 if (day > 30)138 return "";139 if (day == 10)140 return "初十";141 else142 return chineseTen[day / 10] + chineseNumber[n];143 }144 145 /**146 * 传出y年m月d日对应的农历. yearCyl3:农历年与1864的相差数 ? monCyl4:从1900年1月31日以来,闰月数147 * dayCyl5:与1900年1月31日相差的天数,再加40 ?148 * <p/>149 * isday: 这个参数为false---日期为节假日时,阴历日期就返回节假日 ,true---不管日期是否为节假日依然返回这天对应的阴历日期150 *151 * @return152 */153 public String getLunarDate(int year_log, int month_log, int day_log,154 boolean isday) {155 // @SuppressWarnings("unused")156 int yearCyl, monCyl, dayCyl;157 // int leapMonth = 0;158 String nowadays;159 Date baseDate = null;160 Date nowaday = null;161 try {162 baseDate = chineseDateFormat.parse("1900年1月31日");163 } catch (ParseException e) {164 e.printStackTrace(); // To change body of catch statement use165 // Options | File Templates.166 }167 168 nowadays = year_log + "年" + month_log + "月" + day_log + "日";169 try {170 nowaday = chineseDateFormat.parse(nowadays);171 } catch (ParseException e) {172 e.printStackTrace(); // To change body of catch statement use173 // Options | File Templates.174 }175 176 // 求出和1900年1月31日相差的天数177 int offset = (int) ((nowaday.getTime() - baseDate.getTime()) / 86400000L);178 dayCyl = offset + 40;179 monCyl = 14;180 181 // 用offset减去每农历年的天数182 // 计算当天是农历第几天183 // i最终结果是农历的年份184 // offset是当年的第几天185 int iYear, daysOfYear = 0;186 for (iYear = 1900; iYear < 10000 && offset > 0; iYear++) {187 daysOfYear = yearDays(iYear);188 offset -= daysOfYear;189 monCyl += 12;190 }191 if (offset < 0) {192 offset += daysOfYear;193 iYear--;194 monCyl -= 12;195 }196 // 农历年份197 year = iYear;198 setYear(year); // 设置公历对应的农历年份199 200 yearCyl = iYear - 1864;201 leapMonth = leapMonth(iYear); // 闰哪个月,1-12202 boolean leap = false;203 204 // 用当年的天数offset,逐个减去每月(农历)的天数,求出当天是本月的第几天205 int iMonth, daysOfMonth = 0;206 for (iMonth = 1; iMonth < 13 && offset > 0; iMonth++) {207 // 闰月208 if (leapMonth > 0 && iMonth == (leapMonth + 1) && !leap) {209 --iMonth;210 leap = true;211 daysOfMonth = leapDays(year);212 } else213 daysOfMonth = monthDays(year, iMonth);214 215 offset -= daysOfMonth;216 // 解除闰月217 if (leap && iMonth == (leapMonth + 1))218 leap = false;219 if (!leap)220 monCyl++;221 }222 // offset为0时,并且刚才计算的月份是闰月,要校正223 if (offset == 0 && leapMonth > 0 && iMonth == leapMonth + 1) {224 if (leap) {225 leap = false;226 } else {227 leap = true;228 --iMonth;229 --monCyl;230 }231 }232 // offset小于0时,也要校正233 if (offset < 0) {234 offset += daysOfMonth;235 --iMonth;236 --monCyl;237 }238 month = iMonth;239 setLunarMonth(chineseNumber[month - 1] + "月"); // 设置对应的阴历月份240 day = offset + 1;241 242 if (!isday) {243 // 如果日期为节假日则阴历日期则返回节假日244 // setLeapMonth(leapMonth);245 for (int i = 0; i < solarHoliday.length; i++) {246 // 返回公历节假日名称247 String sd = solarHoliday[i].split(" ")[0]; // 节假日的日期248 String sdv = solarHoliday[i].split(" ")[1]; // 节假日的名称249 String smonth_v = month_log + "";250 String sday_v = day_log + "";251 String smd = "";252 if (month_log < 10) {253 smonth_v = "0" + month_log;254 }255 if (day_log < 10) {256 sday_v = "0" + day_log;257 }258 smd = smonth_v + sday_v;259 if (sd.trim().equals(smd.trim())) {260 return sdv;261 }262 }263 264 for (int i = 0; i < lunarHoliday.length; i++) {265 // 返回农历节假日名称266 String ld = lunarHoliday[i].split(" ")[0]; // 节假日的日期267 String ldv = lunarHoliday[i].split(" ")[1]; // 节假日的名称268 String lmonth_v = month + "";269 String lday_v = day + "";270 String lmd = "";271 if (month < 10) {272 lmonth_v = "0" + month;273 }274 if (day < 10) {275 lday_v = "0" + day;276 }277 lmd = lmonth_v + lday_v;278 if (ld.trim().equals(lmd.trim())) {279 return ldv;280 }281 }282 }283 if (day == 1)284 return chineseNumber[month - 1] + "月";285 else286 return getChinaDayString(day);287 288 }289 290 public String toString() {291 if (chineseNumber[month - 1] == "一" && getChinaDayString(day) == "初一")292 return "农历" + year + "年";293 else if (getChinaDayString(day) == "初一")294 return chineseNumber[month - 1] + "月";295 else296 return getChinaDayString(day);297 // return year + "年" + (leap ? "闰" : "") + chineseNumber[month - 1] +298 // "月" + getChinaDayString(day);299 }300 301 //302 // public static void main(String[] args) {303 // System.out.println(new304 // LunarCalendar().getLunarDate(2012, 1, 23));305 // }306 307 308 public int getLeapMonth() {309 return leapMonth;310 }311 312 public void setLeapMonth(int leapMonth) {313 this.leapMonth = leapMonth;314 }315 316 /**317 * 得到当前日期对应的阴历月份318 *319 * @return320 */321 public String getLunarMonth() {322 return lunarMonth;323 }324 325 public void setLunarMonth(String lunarMonth) {326 this.lunarMonth = lunarMonth;327 }328 329 /**330 * 得到当前年对应的农历年份331 *332 * @return333 */334 public int getYear() {335 return year;336 }337 338 public void setYear(int year) {339 this.year = year;340 }341 }
3)为了设置点击事件,这里就分离一个接口出来吧。
ClickDataListener.java
1 package com.example.nanchen.calendarviewdemo.view; 2 3 /** 4 * 选中日期的监听事件 5 * 6 * @author nanchen 7 * @date 2016-08-11 09:14:21 8 */ 9 public interface ClickDataListener {10 /**11 * 点击日期day的点击事件监听12 * @param year 年13 * @param month 月14 * @param day 日15 */16 void onClickData(int year, int month, int day);17 }
这里暂且只有一个点击事件,这个东西大家可以自行创新。
4)这里定义一个格式,可能大家格式都不一样,自己弄呗。
FormatDate.java
1 package com.example.nanchen.calendarviewdemo.view;2 3 /**4 * @author nanchen5 * @date 16-8-12 上午8:156 */7 public class FormatDate {8 public static String DATE_FORMAT = "yyyy-MM-dd";9 }
5)先来看看Adapter的代码,毕竟只有Adapter做好了,才能好好显示。
这里大家需要注意的是设置点击左右和滑动的时候都要显示上月或者下月,这个其实之前楼主写了一个TimeUtils的工具类。
这个可以通过Calendar里面的set方法来解决的,里面有两个参数。
这里先给大家看看我写的两个方法,与本例无关哦。
/** * 获得指定日期的前一月 * @param specifiedDay 指定的字符串日期 格式:yyyy-MM * @return 指定的前一月 */ public static String getSpecifiedMonthBefore(String specifiedDay){ Calendar c = Calendar.getInstance(); Date date = null; try { date = new SimpleDateFormat("yyyy-MM").parse(specifiedDay); } catch (ParseException e) { e.printStackTrace(); } c.setTime(date); int month = c.get(Calendar.MONTH); c.set(Calendar.MONTH,month-1); return new SimpleDateFormat("yyyy-MM").format(c.getTime()); } /** * 获得指定日期的后一月 * @param specifiedDay 指定的字符串日期 格式:yyyy-MM * @return 指定的后一月 */ public static String getSpecifiedMonthAfter(String specifiedDay){ Calendar c = Calendar.getInstance(); Date date=null; try { date = new SimpleDateFormat("yyyy-MM").parse(specifiedDay); } catch (ParseException e) { e.printStackTrace(); } c.setTime(date); int month = c.get(Calendar.MONTH); c.set(Calendar.MONTH,month+1); return new SimpleDateFormat("yyyy-MM").format(c.getTime()); }
上面只是教大家如何达到上月下月的效果,其实同样你可以通过Calendar和format方式对day和year等其他做同样的功能实现,大家可能以后还是有用的。
下面是Adapter,我想大家应该都很明白吧?就用new Date获取当前日期,然后字符串split分离成小数组,然后获取年月日,获取年月日方式不限,很多的。
要说到显示还是得看我们的getView,毕竟Adapter的精髓就在这个方法里,它可以觉得你到底是用怎么显示,这里楼主抛弃了以前画的方法,直接搞一个xml,把大小限制了,这样就不会像昨天那样布局密集,而丑了。
布局很简单啦。
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:background="#FFFFFF" > 6 7 <TextView 8 android:id="@+id/tvtext" 9 android:layout_width="45dp"10 android:layout_height="55dp"11 android:gravity="center" />12 13 </LinearLayout>
我们在getView中设置了颜色等属性。并把非当前月设置为暗色,不可点击。
然后对于week做一些处理,好的,Adapter就OK了。
1 package com.example.nanchen.calendarviewdemo.view; 2 3 import android.content.Context; 4 import android.content.res.Resources; 5 import android.graphics.Color; 6 import android.text.SpannableString; 7 import android.text.Spanned; 8 import android.text.TextUtils; 9 import android.text.style.RelativeSizeSpan; 10 import android.text.style.StyleSpan; 11 import android.util.Log; 12 import android.view.LayoutInflater; 13 import android.view.View; 14 import android.view.ViewGroup; 15 import android.widget.BaseAdapter; 16 import android.widget.TextView; 17 18 import com.example.nanchen.calendarviewdemo.R; 19 20 import java.text.SimpleDateFormat; 21 import java.util.Date; 22 import java.util.Locale; 23 24 /** 25 * 日历GridView中的每一个item显示的文本 26 * 27 * @author nanchen 28 * @date 2016-08-11 10:14:52 29 */ 30 public class CalendarAdapter extends BaseAdapter { 31 private boolean isLeapYear = false; // 是否为闰年 32 private int daysOfMonth = 0; // 某月的天数 33 private int dayOfWeek = 0; // 具体某一天是星期几 34 private int lastDaysOfMonth = 0; // 上一个月的总天数 35 private Context context; 36 private String[] dayNumber = new String[42]; // 一个GridView中的日期存入此数组中,因为一般日历都是6行7列 37 private SpecialCalendar sc = null; 38 private LunarCalendar lc = null; 39 private Resources res = null; 40 private int currentYear = 0; 41 private int currentMonth = 0; 42 private int currentDay = 0; 43 44 private SimpleDateFormat sdf = new SimpleDateFormat(FormatDate.DATE_FORMAT, Locale.CHINA);//格式化日期格式 45 private String showYear = ""; // 用于在头部显示的年份 46 private String showMonth = ""; // 用于在头部显示的月份 47 private String animalsYear = ""; 48 private String leapMonth = ""; // 闰哪一个月 49 private String cyclical = ""; // 天干地支 50 // 系统当前时间 51 private String sysDate = ""; 52 private String sys_year = ""; 53 private String sys_month = ""; 54 private String sys_day = ""; 55 private int colorDataPosition = -1; 56 private boolean isClickData = false; 57 58 public CalendarAdapter() { 59 Date date = new Date(); 60 sysDate = sdf.format(date); // 当期日期 61 sys_year = sysDate.split("-")[0]; 62 sys_month = sysDate.split("-")[1]; 63 sys_day = sysDate.split("-")[2]; 64 65 } 66 67 public CalendarAdapter(Context context, Resources rs, int jumpMonth, 68 int jumpYear, int year_c, int month_c, int day_c) { 69 this(); 70 this.context = context; 71 sc = new SpecialCalendar(); 72 lc = new LunarCalendar(); 73 this.res = rs; 74 75 int stepYear = year_c + jumpYear; 76 int stepMonth = month_c + jumpMonth; 77 if (stepMonth > 0) { 78 // 往下一个月滑动 79 if (stepMonth % 12 == 0) { 80 stepYear = year_c + stepMonth / 12 - 1; 81 stepMonth = 12; 82 } else { 83 stepYear = year_c + stepMonth / 12; 84 stepMonth = stepMonth % 12; 85 } 86 } else { 87 // 往上一个月滑动 88 stepYear = year_c - 1 + stepMonth / 12; 89 stepMonth = stepMonth % 12 + 12; 90 if (stepMonth % 12 == 0) { 91 92 } 93 } 94 95 currentYear = stepYear; // 得到当前的年份 96 currentMonth = stepMonth; // 得到本月 97 // (jumpMonth为滑动的次数,每滑动一次就增加一月或减一月) 98 currentDay = day_c; // 得到当前日期是哪天 99 100 getCalendar(currentYear, currentMonth);101 102 }103 104 public CalendarAdapter(Context context, Resources rs, int year, int month,105 int day) {106 this();107 this.context = context;108 sc = new SpecialCalendar();109 lc = new LunarCalendar();110 this.res = rs;111 currentYear = year;// 得到跳转到的年份112 currentMonth = month; // 得到跳转到的月份113 currentDay = day; // 得到跳转到的天114 getCalendar(currentYear, currentMonth);115 }116 117 @Override118 public int getCount() {119 return dayNumber.length;120 }121 122 @Override123 public Object getItem(int position) {124 return dayNumber[position];125 }126 127 @Override128 public long getItemId(int position) {129 return position;130 }131 132 @Override133 public View getView(int position, View convertView, ViewGroup parent) {134 135 if (convertView == null) {136 convertView = LayoutInflater.from(context).inflate(R.layout.calen_calendar_item, parent,false);137 }138 TextView textView = (TextView) convertView.findViewById(R.id.tvtext);139 String d = dayNumber[position].split("\\.")[0];140 String dv = dayNumber[position].split("\\.")[1];141 142 SpannableString sp = new SpannableString(d + "\n" + dv);143 sp.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0,144 d.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);145 sp.setSpan(new RelativeSizeSpan(1.2f), 0, d.length(),146 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);147 if (dv != null || !TextUtils.isEmpty(dv)) {148 sp.setSpan(new RelativeSizeSpan(0.75f), d.length() + 1,149 dayNumber[position].length(),150 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);151 }152 textView.setText(sp);153 textView.setTextColor(Color.GRAY);154 155 if (position < daysOfMonth + dayOfWeek && position >= dayOfWeek) {156 // 当前月信息显示157 textView.setTextColor(Color.BLACK);// 当月字体设黑158 // drawable = new ColorDrawable(Color.rgb(23, 126, 214));159 if (position % 7 == 0 || position % 7 == 6) {160 // 当前月信息显示161 textView.setTextColor(Color.rgb(23, 126, 214));// 当月字体设黑162 }163 }164 165 if (colorDataPosition == position) {166 // 设置当天的背景167 textView.setTextColor(Color.WHITE);168 169 textView.setBackgroundResource(R.drawable.bg_circle);170 } else {171 textView.setBackgroundColor(res.getColor(android.R.color.transparent));172 }173 return convertView;174 }175 176 // 得到某年的某月的天数且这月的第一天是星期几177 private void getCalendar(int year, int month) {178 isLeapYear = sc.isLeapYear(year); // 是否为闰年179 daysOfMonth = sc.getDaysOfMonth(isLeapYear, month); // 某月的总天数180 dayOfWeek = sc.getWeekdayOfMonth(year, month); // 某月第一天为星期几181 lastDaysOfMonth = sc.getDaysOfMonth(isLeapYear, month - 1); // 上一个月的总天数182 getweek(year, month);183 }184 185 // 将一个月中的每一天的值添加入数组dayNuber中186 private void getweek(int year, int month) {187 int j = 1;188 String lunarDay = "";189 // 得到当前月的所有日程日期(这些日期需要标记)190 for (int i = 0; i < dayNumber.length; i++) {191 if (i < dayOfWeek) { // 前一个月192 int temp = lastDaysOfMonth - dayOfWeek + 1;193 lunarDay = lc.getLunarDate(year, month - 1, temp + i, false);194 dayNumber[i] = (temp + i) + "." + lunarDay;195 196 } else if (i < daysOfMonth + dayOfWeek) { // 本月197 String day = String.valueOf(i - dayOfWeek + 1); // 得到的日期198 lunarDay = lc.getLunarDate(year, month, i - dayOfWeek + 1,199 false);200 dayNumber[i] = i - dayOfWeek + 1 + "." + lunarDay;201 // 对于当前月才去标记当前日期202 if (sys_year.equals(String.valueOf(year))203 && sys_month.equals(String.valueOf(month))204 && sys_day.equals(day)) {205 // 标记当前日期206 colorDataPosition = i;207 }208 setShowYear(String.valueOf(year));209 setShowMonth(String.valueOf(month));210 setAnimalsYear(lc.animalsYear(year));211 setLeapMonth(lc.leapMonth == 0 ? "" : String212 .valueOf(lc.leapMonth));213 setCyclical(lc.cyclical(year));214 } else { // 下一个月215 lunarDay = lc.getLunarDate(year, month + 1, j, false);216 dayNumber[i] = j + "." + lunarDay;217 j++;218 }219 }220 221 String abc = "";222 for (int i = 0; i < dayNumber.length; i++) {223 abc = abc + dayNumber[i] + ":";224 }225 Log.d("DAYNUMBER", abc);226 227 }228 229 /**230 * 点击每一个item时返回item中的日期231 * 232 * @param position233 * @return234 */235 public String getDateByClickItem(int position) {236 return dayNumber[position];237 }238 239 /**240 * 在点击gridView时,得到这个月中第一天的位置241 * 242 * @return243 */244 public int getStartPositon() {245 return dayOfWeek + 7;246 }247 248 /**249 * 在点击gridView时,得到这个月中最后一天的位置250 * 251 * @return252 */253 public int getEndPosition() {254 return (dayOfWeek + daysOfMonth + 7) - 1;255 }256 257 public String getShowYear() {258 return showYear;259 }260 261 public void setShowYear(String showYear) {262 this.showYear = showYear;263 }264 265 public String getShowMonth() {266 return showMonth;267 }268 269 public void setShowMonth(String showMonth) {270 this.showMonth = showMonth;271 }272 273 public String getAnimalsYear() {274 return animalsYear;275 }276 277 public void setAnimalsYear(String animalsYear) {278 this.animalsYear = animalsYear;279 }280 281 public String getLeapMonth() {282 return leapMonth;283 }284 285 public void setLeapMonth(String leapMonth) {286 this.leapMonth = leapMonth;287 }288 289 public String getCyclical() {290 return cyclical;291 }292 293 public void setCyclical(String cyclical) {294 this.cyclical = cyclical;295 }296 297 /**298 * @param position299 * 设置点击的日期的颜色位置300 */301 public void setColorDataPosition(int position) {302 if (position >= dayOfWeek && position < daysOfMonth + dayOfWeek) {303 colorDataPosition = position;304 notifyDataSetChanged();305 }306 }307 }
6)重头戏还是这个MyCalendarView.java,毕竟,你其他的东西都是打辅助的,到后面,大家要用的还不是这个类。
MyCalendarView.java
1 package com.example.nanchen.calendarviewdemo.view; 2 3 import android.app.Activity; 4 import android.content.Context; 5 import android.graphics.Color; 6 import android.graphics.drawable.ColorDrawable; 7 import android.util.AttributeSet; 8 import android.util.Log; 9 import android.view.Display; 10 import android.view.GestureDetector; 11 import android.view.GestureDetector.SimpleOnGestureListener; 12 import android.view.Gravity; 13 import android.view.MotionEvent; 14 import android.view.View; 15 import android.view.View.OnClickListener; 16 import android.view.WindowManager; 17 import android.view.animation.AnimationUtils; 18 import android.widget.AdapterView; 19 import android.widget.AdapterView.OnItemClickListener; 20 import android.widget.GridView; 21 import android.widget.ImageView; 22 import android.widget.LinearLayout; 23 import android.widget.TextView; 24 import android.widget.ViewFlipper; 25 26 import com.example.nanchen.calendarviewdemo.R; 27 28 import java.text.SimpleDateFormat; 29 import java.util.Date; 30 import java.util.Locale; 31 32 33 /** 34 * 自定义的日历控件 35 * 36 * @author nanchen 37 * @date 2016-08-11 09:44:27 38 */ 39 public class MyCalendarView extends LinearLayout implements OnClickListener { 40 41 private final String TAG = MyCalendarView.class.getSimpleName(); 42 private int year_c = 0;// 今天的年份 43 private int month_c = 0;// 今天的月份 44 private int day_c = 0;// 今天的日期 45 private String currentDate = "";//当前日期 46 private Context mContext; 47 private TextView currentMonth;// 显示日期 48 private ImageView prevMonth;// 去上一个月 49 private ImageView nextMonth;// 去下一个月 50 private int gvFlag = 0; 51 private GestureDetector gestureDetector = null;//用户手势检测 52 private CalendarAdapter adapter = null;//适配器 53 private ViewFlipper flipper = null; 54 private GridView gridView = null; 55 private static int jumpMonth = 0; // 每次滑动,增加或减去一个月,默认为0(即显示当前月) 56 private static int jumpYear = 0; // 滑动跨越一年,则增加或者减去一年,默认为0(即当前年) 57 private ClickDataListener clickDataListener; 58 59 public MyCalendarView(Context context) { 60 this(context, null); 61 } 62 63 public MyCalendarView(Context context, AttributeSet attrs) { 64 super(context, attrs); 65 mContext = context; 66 initView(); 67 } 68 69 /** 70 * 初始化布局 71 */ 72 private void initView() { 73 //加载布局,一个头布局,一个横向的星期布局,下面一个ViewFlipper用于滑动 74 View view = View.inflate(mContext, R.layout.calen_calendar, this); 75 currentMonth = (TextView) view.findViewById(R.id.currentMonth); 76 prevMonth = (ImageView) view.findViewById(R.id.prevMonth); 77 nextMonth = (ImageView) view.findViewById(R.id.nextMonth); 78 setListener(); 79 setCurrentDay(); 80 gestureDetector = new GestureDetector(mContext, new MyGestureListener()); 81 flipper = (ViewFlipper) findViewById(R.id.flipper); 82 flipper.removeAllViews(); 83 adapter = new CalendarAdapter(mContext, getResources(), jumpMonth, 84 jumpYear, year_c, month_c, day_c); 85 addGridView(); 86 gridView.setAdapter(adapter); 87 flipper.addView(gridView, 0); 88 addTextToTopTextView(currentMonth); 89 } 90 91 private void setListener() { 92 prevMonth.setOnClickListener(this); 93 nextMonth.setOnClickListener(this); 94 95 } 96 97 public void setClickDataListener(ClickDataListener clickDataListener) { 98 this.clickDataListener = clickDataListener; 99 }100 101 private void setCurrentDay() {102 Date date = new Date();103 SimpleDateFormat sdf = new SimpleDateFormat(FormatDate.DATE_FORMAT, Locale.CHINA);104 currentDate = sdf.format(date); // 当期日期105 year_c = Integer.parseInt(currentDate.split("-")[0]);106 month_c = Integer.parseInt(currentDate.split("-")[1]);107 day_c = Integer.parseInt(currentDate.split("-")[2]);108 }109 110 /**111 * 移动到下一个月112 * 113 * @param gvFlag114 */115 private void enterNextMonth(int gvFlag) {116 addGridView(); // 添加一个gridView117 jumpMonth++; // 下一个月118 adapter = new CalendarAdapter(mContext, this.getResources(), jumpMonth,119 jumpYear, year_c, month_c, day_c);120 gridView.setAdapter(adapter);121 addTextToTopTextView(currentMonth); // 移动到下一月后,将当月显示在头标题中122 gvFlag++;123 flipper.addView(gridView, gvFlag);124 flipper.setInAnimation(AnimationUtils.loadAnimation(mContext,125 R.anim.push_left_in));126 flipper.setOutAnimation(AnimationUtils.loadAnimation(mContext,127 R.anim.push_left_out));128 flipper.showNext();129 flipper.removeViewAt(0);130 }131 132 /**133 * 移动到上一个月134 * 135 * @param gvFlag136 */137 private void enterPrevMonth(int gvFlag) {138 addGridView(); // 添加一个gridView139 jumpMonth--; // 上一个月140 141 adapter = new CalendarAdapter(mContext, this.getResources(), jumpMonth,142 jumpYear, year_c, month_c, day_c);143 gridView.setAdapter(adapter);144 gvFlag++;145 addTextToTopTextView(currentMonth); // 移动到上一月后,将当月显示在头标题中146 flipper.addView(gridView, gvFlag);147 148 flipper.setInAnimation(AnimationUtils.loadAnimation(mContext,149 R.anim.push_right_in));150 flipper.setOutAnimation(AnimationUtils.loadAnimation(mContext,151 R.anim.push_right_out));152 flipper.showPrevious();153 flipper.removeViewAt(0);154 }155 156 /**157 * 添加头部的年份 闰哪月等信息158 * 159 * @param view160 */161 private void addTextToTopTextView(TextView view) {162 StringBuffer textDate = new StringBuffer();163 // draw = getResources().getDrawable(R.drawable.top_day);164 // view.setBackgroundDrawable(draw);165 textDate.append(adapter.getShowYear()).append("年")166 .append(adapter.getShowMonth()).append("月").append("\t");167 view.setText(textDate);168 }169 170 private void addGridView() {171 LayoutParams params = new LayoutParams(172 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);173 // 取得屏幕的宽度和高度174 WindowManager windowManager = ((Activity) mContext).getWindowManager();175 Display display = windowManager.getDefaultDisplay();176 int Width = display.getWidth();177 int Height = display.getHeight();178 179 gridView = new GridView(mContext);180 gridView.setNumColumns(7);181 gridView.setColumnWidth(40);182 // gridView.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);183 if (Width == 720 && Height == 1280) {184 gridView.setColumnWidth(40);185 }186 gridView.setGravity(Gravity.CENTER_VERTICAL);187 gridView.setSelector(new ColorDrawable(Color.TRANSPARENT));188 // 去除gridView边框189 gridView.setVerticalSpacing(0);190 gridView.setHorizontalSpacing(0);191 gridView.setOnTouchListener(new OnTouchListener() {192 // 将gridview中的触摸事件回传给gestureDetector193 194 public boolean onTouch(View v, MotionEvent event) {195 // TODO Auto-generated method stub196 return gestureDetector.onTouchEvent(event);197 }198 });199 200 gridView.setOnItemClickListener(new OnItemClickListener() {201 202 @Override203 public void onItemClick(AdapterView<?> arg0, View arg1,204 int position, long arg3) {205 // TODO Auto-generated method stub206 // 点击任何一个item,得到这个item的日期(排除点击的是周日到周六(点击不响应))207 int startPosition = adapter.getStartPositon();208 int endPosition = adapter.getEndPosition();209 if (startPosition <= position + 7210 && position <= endPosition - 7) {211 int scheduleDay = Integer.parseInt(adapter.getDateByClickItem(position)212 .split("\\.")[0]); // 这一天的阳历213 int scheduleYear = Integer.parseInt(adapter.getShowYear());214 int scheduleMonth = Integer.parseInt(adapter.getShowMonth());215 ((CalendarAdapter) arg0.getAdapter())216 .setColorDataPosition(position);217 if (clickDataListener != null) {218 clickDataListener.onClickData(scheduleYear,219 scheduleMonth, scheduleDay);220 }221 }222 }223 });224 gridView.setLayoutParams(params);225 }226 227 @Override228 public void onClick(View v) {229 switch (v.getId()) {230 case R.id.nextMonth: // 下一个月231 enterNextMonth(gvFlag);232 Log.d(TAG, "gvFlag=" + gvFlag);233 break;234 case R.id.prevMonth: // 上一个月235 enterPrevMonth(gvFlag);236 break;237 238 }239 240 }241 242 private static class MyGestureListener extends SimpleOnGestureListener {243 private MyCalendarView calendarView;244 245 @Override246 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,247 float velocityY) {248 int gvFlag = 0; // 每次添加gridview到viewflipper中时给的标记249 if (e1.getX() - e2.getX() > 120) {250 // 像左滑动251 calendarView.enterNextMonth(gvFlag);252 return true;253 } else if (e1.getX() - e2.getX() < -120) {254 // 向右滑动255 calendarView.enterPrevMonth(gvFlag);256 return true;257 }258 return false;259 }260 }261 }
这个,这个类没啥难的吧?看不懂?就跟一般的Activity处理一样,绑定布局,设置监听,做一些交互。
额,对,昨天写这个的时候搞得太复杂太复杂了。今天楼主全部放到xml文件中去了啦啦啦。
而且上面的布局和周几都不用刷新的,这不,直接放在Xml中定死就可以了嘛。
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 7 <LinearLayout 8 android:layout_width="match_parent" 9 android:layout_height="wrap_content"10 android:orientation="horizontal" >11 12 <ImageView13 android:id="@+id/prevMonth"14 android:layout_width="0dp"15 android:layout_height="wrap_content"16 android:layout_gravity="center"17 android:layout_weight="1"18 android:src="@drawable/pre_month_selector" />19 20 <TextView21 android:id="@+id/currentMonth"22 android:layout_width="0dp"23 android:layout_height="35dp"24 android:layout_weight="3"25 android:gravity="center"26 android:text="2016年8月"27 android:textColor="#177ed6"28 android:textSize="18dp" />29 30 <ImageView31 android:id="@+id/nextMonth"32 android:layout_width="0dp"33 android:layout_height="wrap_content"34 android:layout_gravity="center"35 android:layout_weight="1"36 android:src="@drawable/next_month_selector" />37 </LinearLayout>38 39 40 <!--周几的布局-->41 <LinearLayout42 android:layout_width="match_parent"43 android:layout_height="20dp"44 android:background="#ffffffff" >45 46 <TextView47 style="@style/weekName"48 android:text="日"49 android:textColor="#177ed6" />50 51 <TextView52 style="@style/weekName"53 android:text="一" />54 55 <TextView56 style="@style/weekName"57 android:text="二" />58 59 <TextView60 style="@style/weekName"61 android:text="三" />62 63 <TextView64 style="@style/weekName"65 android:text="四" />66 67 <TextView68 style="@style/weekName"69 android:text="五" />70 71 <TextView72 style="@style/weekName"73 android:text="六"74 android:textColor="#177ed6" />75 </LinearLayout>76 77 <View78 android:layout_width="match_parent"79 android:layout_height="1dp"80 android:background="#ff8091a8" />81 82 <ViewFlipper83 android:id="@+id/flipper"84 android:layout_width="match_parent"85 android:layout_height="wrap_content" />86 </LinearLayout>
XML文件够简单吧~上部一个简单的title布局,中间一个水平的线性布局用于显示周几,下面一个ViewFlipper用于左右滑动。
额,两个Selector
pre_month_selector.xml
1 <?xml version="1.0" encoding="utf-8"?>2 <selector xmlns:android="http://schemas.android.com/apk/res/android">3 4 <item android:state_pressed="true" android:drawable="@drawable/triangle06_pressed"/>5 <item android:state_pressed="false" android:drawable="@drawable/triangle06"/>6 7 </selector>
next_month_selector.xml
1 <?xml version="1.0" encoding="utf-8"?>2 <selector xmlns:android="http://schemas.android.com/apk/res/android">3 4 <item android:state_pressed="true" android:drawable="@drawable/triangle05_pressed"/>5 <item android:state_pressed="false" android:drawable="@drawable/triangle05"/>6 7 </selector>
7)使用,MainActivity.java
1 package com.example.nanchen.calendarviewdemo; 2 3 import android.os.Bundle; 4 import android.support.v7.app.AppCompatActivity; 5 import android.widget.Toast; 6 7 import com.example.nanchen.calendarviewdemo.view.MyCalendarView; 8 import com.example.nanchen.calendarviewdemo.view.ClickDataListener; 9 10 import java.util.Locale;11 12 /**13 * @author nanchen14 * @date 2016-08-11 10:48:2115 */16 public class MainActivity extends AppCompatActivity {17 18 @Override19 protected void onCreate(Bundle savedInstanceState) {20 super.onCreate(savedInstanceState);21 setContentView(R.layout.activity_main);22 23 MyCalendarView myCalendarView = (MyCalendarView) findViewById(R.id.main_calendar);24 myCalendarView.setClickDataListener(new ClickDataListener() {25 @Override26 public void onClickData(int year, int month, int day) {27 String date = String.format(Locale.CHINA,"%04d年%02d月%02d日",year,month,day);28 Toast.makeText(MainActivity.this,date,Toast.LENGTH_SHORT).show();29 }30 });31 }32 }
布局文件
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 android:orientation="vertical" 8 tools:context="com.example.nanchen.calendarviewdemo.MainActivity"> 9 10 <com.example.nanchen.calendarviewdemo.view.MyCalendarView11 android:layout_width="match_parent"12 android:layout_height="wrap_content"13 android:id="@+id/main_calendar"/>14 </LinearLayout>
更多详情源码请上github:https://github.com/nanchen2251/MyCalendarViewNewDemo
额,差不多啦,转载的小伙伴别忘了附上本文地址:http://www.cnblogs.com/liushilin/p/5763717.html
还是欢迎大家抨击,点赞,分享~你们的支持和打击是小主进步的源泉~
- Android自定义日历控件,自带农历节假日,已经开源,即取即用~
- 开源日历sunbird安装配置中国节假日和农历
- android 自带日历控件datePicker
- Android带日程安排的自定义日历控件
- Andorid 之日历控件,可左右滑动,包含公历,农历,节假日等
- Android定制日历,支持周月切换,日期标记,农历显示,节假日
- 带农历的日历
- 带农历的日历
- 带农历的日历
- 带农历的日历
- android 自定义日历控件
- Android自定义日历控件
- Android 自定义日历控件
- android自定义日历控件
- Android 自定义日历控件
- Android 自定义日历控件
- Android-自定义日历控件
- Android自定义日历控件
- 【hdu 3863】No Gambling
- 造了个轮子——java日期工具类
- mysql JDBC批量操作
- iOS tableView分区头自定义
- gem5学习1——Introduction
- Android自定义日历控件,自带农历节假日,已经开源,即取即用~
- wampserver You don't have permission to access / on this server. 解决 方法
- 说真的,我烦透了各种“干货”
- SegmentFault 技术周刊 Vol.19 - Linux 文本处理三利器
- pip和conda安装源更改
- Android键盘 AOSP监听delete按键
- 使用UPnP来穿透NAT使内网接口对外网可见
- android白盒测试之junit
- 目标识别