【无私分享】修订版干货!!!一个炫酷的自定义日历控件,摆脱日历时间选择烦恼,纯福利~
可能不少的小伙伴都有看楼主昨天发的自定义日历控件,虽然实现功能不多,但也还算将就吧。
没有看的小伙伴如果有兴趣的话可以去看看: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:44 10 * 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 else 103 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 else 142 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 * @return 152 */ 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 use 165 // 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 use 173 // 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-12 202 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 } else 213 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 else 286 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 else 296 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(new 304 // 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 * @return 320 */ 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 * @return 333 */ 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 nanchen 5 * @date 16-8-12 上午8:15 6 */ 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 @Override 118 public int getCount() { 119 return dayNumber.length; 120 } 121 122 @Override 123 public Object getItem(int position) { 124 return dayNumber[position]; 125 } 126 127 @Override 128 public long getItemId(int position) { 129 return position; 130 } 131 132 @Override 133 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 ? "" : String 212 .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 position 233 * @return 234 */ 235 public String getDateByClickItem(int position) { 236 return dayNumber[position]; 237 } 238 239 /** 240 * 在点击gridView时,得到这个月中第一天的位置 241 * 242 * @return 243 */ 244 public int getStartPositon() { 245 return dayOfWeek + 7; 246 } 247 248 /** 249 * 在点击gridView时,得到这个月中最后一天的位置 250 * 251 * @return 252 */ 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 position 299 * 设置点击的日期的颜色位置 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 gvFlag 114 */ 115 private void enterNextMonth(int gvFlag) { 116 addGridView(); // 添加一个gridView 117 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 gvFlag 136 */ 137 private void enterPrevMonth(int gvFlag) { 138 addGridView(); // 添加一个gridView 139 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 view 160 */ 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中的触摸事件回传给gestureDetector 193 194 public boolean onTouch(View v, MotionEvent event) { 195 // TODO Auto-generated method stub 196 return gestureDetector.onTouchEvent(event); 197 } 198 }); 199 200 gridView.setOnItemClickListener(new OnItemClickListener() { 201 202 @Override 203 public void onItemClick(AdapterView<?> arg0, View arg1, 204 int position, long arg3) { 205 // TODO Auto-generated method stub 206 // 点击任何一个item,得到这个item的日期(排除点击的是周日到周六(点击不响应)) 207 int startPosition = adapter.getStartPositon(); 208 int endPosition = adapter.getEndPosition(); 209 if (startPosition <= position + 7 210 && 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 @Override 228 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 @Override 246 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 <ImageView 13 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 <TextView 21 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 <ImageView 31 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 <LinearLayout 42 android:layout_width="match_parent" 43 android:layout_height="20dp" 44 android:background="#ffffffff" > 45 46 <TextView 47 style="@style/weekName" 48 android:text="日" 49 android:textColor="#177ed6" /> 50 51 <TextView 52 style="@style/weekName" 53 android:text="一" /> 54 55 <TextView 56 style="@style/weekName" 57 android:text="二" /> 58 59 <TextView 60 style="@style/weekName" 61 android:text="三" /> 62 63 <TextView 64 style="@style/weekName" 65 android:text="四" /> 66 67 <TextView 68 style="@style/weekName" 69 android:text="五" /> 70 71 <TextView 72 style="@style/weekName" 73 android:text="六" 74 android:textColor="#177ed6" /> 75 </LinearLayout> 76 77 <View 78 android:layout_width="match_parent" 79 android:layout_height="1dp" 80 android:background="#ff8091a8" /> 81 82 <ViewFlipper 83 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 nanchen 14 * @date 2016-08-11 10:48:21 15 */ 16 public class MainActivity extends AppCompatActivity { 17 18 @Override 19 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 @Override 26 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.MyCalendarView 11 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
还是欢迎大家抨击,点赞,分享~你们的支持和打击是小主进步的源泉~
作 者:
南 尘
出 处: http://www.cnblogs.com/liushilin/
关于作者:专注于移动前端的项目开发。如有问题或建议,请多多赐教!欢迎加入Android交流群:118116509
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信我
声援博主:如果您觉得文章对您有帮助,可以点击文章下部【推荐】或侧边【关注】。您的鼓励是作者坚持原创和持续写作的最大动力!
欢迎关注我的公众号,精讲面试、算法、Andrid、Java、Python,旨在打造全网最比心的公众号。