├── .gitignore ├── README.md ├── build.gradle ├── calendar ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── ldf │ │ └── mi │ │ └── calendar │ │ └── micalendar │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── ldf │ │ └── calendar │ │ ├── Const.java │ │ ├── Utils.java │ │ ├── behavior │ │ ├── MonthPagerBehavior.java │ │ └── RecyclerViewBehavior.java │ │ ├── component │ │ ├── CalendarAttr.java │ │ ├── CalendarRenderer.java │ │ ├── CalendarViewAdapter.java │ │ └── State.java │ │ ├── interf │ │ ├── IDayRenderer.java │ │ ├── OnAdapterSelectListener.java │ │ └── OnSelectDateListener.java │ │ ├── model │ │ └── CalendarDate.java │ │ └── view │ │ ├── Calendar.java │ │ ├── Day.java │ │ ├── DayView.java │ │ ├── MonthPager.java │ │ └── Week.java │ ├── readme │ └── test │ └── java │ └── com │ └── ldf │ └── mi │ └── calendar │ └── micalendar │ └── ExampleUnitTest.java ├── example ├── .gitignore ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── hqyxjy │ │ └── ldf │ │ └── supercalendar │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── hqyxjy │ │ │ └── ldf │ │ │ └── supercalendar │ │ │ ├── CustomDayView.java │ │ │ ├── ExampleAdapter.java │ │ │ ├── SyllabusActivity.java │ │ │ └── ThemeDayView.java │ └── res │ │ ├── drawable-xxhdpi │ │ └── select_background.png │ │ ├── drawable │ │ ├── background_white_3dp_corner.xml │ │ ├── button_bg.xml │ │ ├── selected_background.xml │ │ ├── selected_background_custom.xml │ │ ├── today_background.xml │ │ ├── view_syllabus_active_mark_background.xml │ │ ├── view_syllabus_mark_background.xml │ │ └── view_syllabus_unactive_mark_background.xml │ │ ├── layout │ │ ├── activity_syllabus.xml │ │ ├── custom_day.xml │ │ ├── custom_day_focus.xml │ │ └── item.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── array.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── hqyxjy │ └── ldf │ └── supercalendar │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── raw └── examples.gif └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SuperCalendar 2 | ## ChangeLog 3 | * 发布1.6 稳定版 4 | * 发布1.5 修改了一些BUG 5 | * 发布1.4稳定版 6 | * 清除了dev污染 7 | * 添加手动切换上一月下一月功能 8 | * 解决神奇的日历尺寸问题,确保日历尺寸正确 9 | ## 简介 10 | * [可能是第十好的Android开源日历](http://www.jianshu.com/p/8e3fc46e5a80) 11 | * 博主现在工作在一家教育公司,最近公司的产品狗扔过来一个需求,说要做一个可以周月切换的课表,可以展示用户在某一天的上课安排。接到这个任务之后我研究了很多的日历控件,并且抽出了一个calenderlib。先看一下最后的项目中的效果: 12 | 13 |
14 | 15 | 16 |
17 |
18 | 19 |
20 | 21 | * 看到本篇文章的同学估计也是实验课或者项目需求中需要一个日历表,当我接到这个需求的时候,当时脑子压根连想都没想,这么通用的控件,GitHub上一搜一大堆不是嘛。可是等到真正做起来的时候,扎心了老铁,GitHub上的大神居然异常的不给力,都是实现了基本功能,能够滑动切换月份,找实现了周月切换功能的开源库很难。终于我费尽千辛万苦找到一个能够完美切换的项目时,你周月切换之后的数据乱的一塌糊涂啊!!! 22 | * 算了,自己撸一个!!! 23 | 24 | ## 项目链接 [SuperCalendar][Tags] 25 | 26 | [Tags]: https://github.com/MagicMashRoom/SuperCalendar 27 | * 如果你感觉到对你有帮助,欢迎star 28 | * 如果你感觉对代码有疑惑,或者需要修改的地方,欢迎issue 29 | 30 | ## 主要特性 31 | * 日历样式完全自定义,拓展性强 32 | * 左右滑动切换上下周月,上下滑动切换周月模式 33 | * 抽屉式周月切换效果 34 | * 标记指定日期(marker) 35 | * 跳转到指定日期 36 | 37 | ## 思路 38 | 39 | ## 使用方法 40 | 41 | #### XML布局 42 | * 新建XML布局 43 | 44 | RecyclerView的layout_behavior为com.ldf.calendar.behavior.RecyclerViewBehavior 45 | 46 | ```xml 47 | 52 | 53 | 58 | 59 | 60 | 67 | 68 | 69 | 70 | ``` 71 | #### 自定义日历样式 72 | * 新建CustomDayView继承自DayView并重写refreshContent 和 copy 两个方法 73 | 74 | ```java 75 | @Override 76 | public void refreshContent() { 77 | //你的代码 你可以在这里定义你的显示规则 78 | super.refreshContent(); 79 | } 80 | 81 | @Override 82 | public IDayRenderer copy() { 83 | return new CustomDayView(context , layoutResource); 84 | } 85 | ``` 86 | 87 | * 新建CustomDayView实例,并作为参数构建CalendarViewAdapter 88 | 89 | 90 | ```java 91 | CustomDayView customDayView = new CustomDayView(context, R.layout.custom_day); 92 | calendarAdapter = new CalendarViewAdapter( 93 | context, 94 | onSelectDateListener, 95 | CalendarAttr.CalendarType.MONTH, 96 | CalendarAttr.WeekArrayType.Monday, 97 | customDayView); 98 | ``` 99 | #### 初始化View 100 | 101 | * 目前来看 相比于Dialog选择日历 我的控件更适合于Activity/Fragment在Activity的`onCreate` 或者Fragment的`onCreateView` 你需要实现这两个方法来启动日历并装填进数据 102 | 103 | ```java 104 | @Override 105 | protected void onCreate(@Nullable Bundle savedInstanceState) { 106 | super.onCreate(savedInstanceState); 107 | setContentView(R.layout.activity_syllabus); 108 | initCalendarView(); 109 | } 110 | 111 | private void initCalendarView() { 112 | initListener(); 113 | CustomDayView customDayView = new CustomDayView(context, R.layout.custom_day); 114 | calendarAdapter = new CalendarViewAdapter( 115 | context, 116 | onSelectDateListener, 117 | CalendarAttr.CalendarType.MONTH, 118 | CalendarAttr.WeekArrayType.Monday, 119 | customDayView); 120 | initMarkData(); 121 | initMonthPager(); 122 | } 123 | ``` 124 | 125 | 使用此方法回调日历点击事件 126 | ```java 127 | private void initListener() { 128 | onSelectDateListener = new OnSelectDateListener() { 129 | @Override 130 | public void onSelectDate(CalendarDate date) { 131 | //your code 132 | } 133 | 134 | @Override 135 | public void onSelectOtherMonth(int offset) { 136 | //偏移量 -1表示上一个月 , 1表示下一个月 137 | monthPager.selectOtherMonth(offset); 138 | } 139 | }; 140 | } 141 | ``` 142 | 143 | 使用此方法初始化日历标记数据 144 | ```java 145 | private void initMarkData() { 146 | HashMap markData = new HashMap<>(); 147 | //1表示红点,0表示灰点 148 | markData.put("2017-8-9" , "1"); 149 | markData.put("2017-7-9" , "0"); 150 | markData.put("2017-6-9" , "1"); 151 | markData.put("2017-6-10" , "0"); 152 | calendarAdapter.setMarkData(markData); 153 | } 154 | ``` 155 | 使用此方法给MonthPager添加上相关监听 156 | ```java 157 | monthPager.addOnPageChangeListener(new MonthPager.OnPageChangeListener() { 158 | @Override 159 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 160 | } 161 | 162 | @Override 163 | public void onPageSelected(int position) { 164 | mCurrentPage = position; 165 | currentCalendars = calendarAdapter.getAllItems(); 166 | if(currentCalendars.get(position % currentCalendars.size()) instanceof Calendar){ 167 | //you code 168 | } 169 | } 170 | 171 | @Override 172 | public void onPageScrollStateChanged(int state) { 173 | } 174 | }); 175 | ``` 176 | 177 | 重写onWindowFocusChanged方法,使用此方法得知calendar和day的尺寸 178 | 179 | ```java 180 | @Override 181 | public void onWindowFocusChanged(boolean hasFocus) { 182 | super.onWindowFocusChanged(hasFocus); 183 | if(hasFocus && !initiated) { 184 | CalendarDate today = new CalendarDate(); 185 | calendarAdapter.notifyDataChanged(today); 186 | initiated = true; 187 | } 188 | } 189 | ``` 190 | 191 | * 大功告成,如果还不清晰,请下载DEMO 192 | 193 | ## Download 194 | -------- 195 | Gradle: 196 | Step 1. Add it in your root build.gradle at the end of repositories: 197 | ```groovy 198 | allprojects { 199 | repositories { 200 | ... 201 | maven { url 'https://www.jitpack.io' } 202 | } 203 | } 204 | ``` 205 | Step 2. Add the dependency 206 | 207 | ```groovy 208 | dependencies { 209 |        compile 'com.github.MagicMashRoom:SuperCalendar:1.6' 210 | } 211 | 212 | ``` 213 | [![](https://www.jitpack.io/v/MagicMashRoom/SuperCalendar.svg)](https://www.jitpack.io/#MagicMashRoom/SuperCalendar) 214 | 215 | ## Licence 216 | 217 | Copyright 2017 MagicMashRoom, Inc. 218 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.0.1' 10 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | maven { url "https://jitpack.io" } 20 | google() 21 | } 22 | } 23 | 24 | task clean(type: Delete) { 25 | delete rootProject.buildDir 26 | } 27 | -------------------------------------------------------------------------------- /calendar/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### OSX template 3 | *.DS_Store 4 | .AppleDouble 5 | .LSOverride 6 | 7 | # Icon must end with two \r 8 | Icon 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear in the root of a volume 14 | .DocumentRevisions-V100 15 | .fseventsd 16 | .Spotlight-V100 17 | .TemporaryItems 18 | .Trashes 19 | .VolumeIcon.icns 20 | .com.apple.timemachine.donotpresent 21 | 22 | # Directories potentially created on remote AFP share 23 | .AppleDB 24 | .AppleDesktop 25 | Network Trash Folder 26 | Temporary Items 27 | .apdisk 28 | ### Android template 29 | # Built application files 30 | *.apk 31 | *.ap_ 32 | 33 | # Files for the ART/Dalvik VM 34 | *.dex 35 | 36 | # Java class files 37 | *.class 38 | 39 | # Generated files 40 | bin/ 41 | gen/ 42 | out/ 43 | 44 | # Gradle files 45 | .gradle/ 46 | build/ 47 | 48 | # Local configuration file (sdk path, etc) 49 | local.properties 50 | 51 | # Proguard folder generated by Eclipse 52 | proguard/ 53 | 54 | # Log Files 55 | *.log 56 | 57 | # Android Studio Navigation editor temp files 58 | .navigation/ 59 | 60 | # Android Studio captures folder 61 | captures/ 62 | 63 | # Intellij 64 | *.iml 65 | .idea/workspace.xml 66 | 67 | # Keystore files 68 | *.jks 69 | 70 | -------------------------------------------------------------------------------- /calendar/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin:'com.github.dcendents.android-maven' 3 | 4 | group='com.github.MagicMashRoom' 5 | 6 | android { 7 | compileSdkVersion 26 8 | buildToolsVersion '26.0.2' 9 | 10 | defaultConfig { 11 | minSdkVersion 16 12 | targetSdkVersion 26 13 | versionCode 1 14 | versionName "1.0" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | 24 | lintOptions { 25 | abortOnError false 26 | } 27 | } 28 | 29 | dependencies { 30 | compile fileTree(dir: 'libs', include: ['*.jar']) 31 | testCompile 'junit:junit:4.12' 32 | compile 'com.android.support:appcompat-v7:26.1.0' 33 | compile 'com.android.support:design:26.1.0' 34 | } 35 | -------------------------------------------------------------------------------- /calendar/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Tools\Android\studio_sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /calendar/src/androidTest/java/com/ldf/mi/calendar/micalendar/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.ldf.mi.calendar.micalendar; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /calendar/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/Const.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar; 2 | 3 | /** 4 | * Created by ldf on 17/6/27. 5 | */ 6 | 7 | public class Const { 8 | public final static int TOTAL_COL = 7; 9 | public final static int TOTAL_ROW = 6; 10 | public final static int CURRENT_PAGER_INDEX = 1000; 11 | } 12 | -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016. 3 | * wb-lijinwei.a@alibaba-inc.com 4 | */ 5 | 6 | package com.ldf.calendar; 7 | 8 | import android.annotation.SuppressLint; 9 | import android.content.Context; 10 | import android.support.design.widget.CoordinatorLayout; 11 | import android.support.v4.view.ViewCompat; 12 | import android.support.v7.widget.RecyclerView; 13 | import android.view.View; 14 | import android.view.ViewConfiguration; 15 | import android.widget.Scroller; 16 | 17 | import com.ldf.calendar.component.CalendarAttr; 18 | import com.ldf.calendar.model.CalendarDate; 19 | 20 | import java.text.ParseException; 21 | import java.text.SimpleDateFormat; 22 | import java.util.Calendar; 23 | import java.util.Date; 24 | import java.util.HashMap; 25 | 26 | public final class Utils { 27 | 28 | private static HashMap markData = new HashMap<>(); 29 | 30 | private Utils() { 31 | 32 | } 33 | 34 | /** 35 | * 得到某一个月的具体天数 36 | * 37 | * @param year 参数月所在年 38 | * @param month 参数月 39 | * @return int 参数月所包含的天数 40 | */ 41 | public static int getMonthDays(int year, int month) { 42 | if (month > 12) { 43 | month = 1; 44 | year += 1; 45 | } else if (month < 1) { 46 | month = 12; 47 | year -= 1; 48 | } 49 | int[] monthDays = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 50 | int days = 0; 51 | // 闰年2月29天 52 | if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { 53 | monthDays[1] = 29; 54 | } 55 | try { 56 | days = monthDays[month - 1]; 57 | } catch (Exception e) { 58 | e.getStackTrace(); 59 | } 60 | return days; 61 | } 62 | 63 | public static int getYear() { 64 | return Calendar.getInstance().get(Calendar.YEAR); 65 | } 66 | 67 | public static int getMonth() { 68 | return Calendar.getInstance().get(Calendar.MONTH) + 1; 69 | } 70 | 71 | public static int getDay() { 72 | return Calendar.getInstance().get(Calendar.DAY_OF_MONTH); 73 | } 74 | 75 | /** 76 | * 得到当前月第一天在其周的位置 77 | * 78 | * @param year 当前年 79 | * @param month 当前月 80 | * @param type 周排列方式 0代表周一作为本周的第一天, 2代表周日作为本周的第一天 81 | * @return int 本月第一天在其周的位置 82 | */ 83 | public static int getFirstDayWeekPosition(int year, int month, CalendarAttr.WeekArrayType type) { 84 | Calendar cal = Calendar.getInstance(); 85 | cal.setTime(getDateFromString(year, month)); 86 | int week_index = cal.get(Calendar.DAY_OF_WEEK) - 1; 87 | if (type == CalendarAttr.WeekArrayType.Sunday) { 88 | return week_index; 89 | } else { 90 | week_index = cal.get(Calendar.DAY_OF_WEEK) + 5; 91 | if (week_index >= 7) { 92 | week_index -= 7; 93 | } 94 | } 95 | return week_index; 96 | } 97 | 98 | /** 99 | * 将yyyy-MM-dd类型的字符串转化为对应的Date对象 100 | * 101 | * @param year 当前年 102 | * @param month 当前月 103 | * @return Date 对应的Date对象 104 | */ 105 | @SuppressLint("SimpleDateFormat") 106 | public static Date getDateFromString(int year, int month) { 107 | String dateString = year + "-" + (month > 9 ? month : ("0" + month)) + "-01"; 108 | Date date = new Date(); 109 | try { 110 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 111 | date = sdf.parse(dateString); 112 | } catch (ParseException e) { 113 | System.out.println(e.getMessage()); 114 | } 115 | return date; 116 | } 117 | 118 | /** 119 | * 计算参数日期月与当前月相差的月份数 120 | * 121 | * @param year 参数日期所在年 122 | * @param month 参数日期所在月 123 | * @param currentDate 当前月 124 | * @return int offset 相差月份数 125 | */ 126 | public static int calculateMonthOffset(int year, int month, CalendarDate currentDate) { 127 | int currentYear = currentDate.getYear(); 128 | int currentMonth = currentDate.getMonth(); 129 | int offset = (year - currentYear) * 12 + (month - currentMonth); 130 | return offset; 131 | } 132 | 133 | /** 134 | * 删除方法, 这里只会删除某个文件夹下的文件,如果传入的directory是个文件,将不做处理 135 | * 136 | * @param context 上下文 137 | * @param dpi dp为单位的尺寸 138 | * @return int 转化而来的对应像素 139 | */ 140 | public static int dpi2px(Context context, float dpi) { 141 | return (int) (context.getResources().getDisplayMetrics().density * dpi + 0.5f); 142 | } 143 | 144 | /** 145 | * 得到标记日期数据,可以通过该数据得到标记日期的信息,开发者可自定义格式 146 | * 目前HashMap的组成仅仅是为了DEMO效果 147 | * 148 | * @return HashMap 标记日期数据 149 | */ 150 | public static HashMap loadMarkData() { 151 | return markData; 152 | } 153 | 154 | /** 155 | * 设置标记日期数据 156 | * 157 | * @param data 标记日期数据 158 | * @return void 159 | */ 160 | public static void setMarkData(HashMap data) { 161 | markData = data; 162 | } 163 | 164 | public static void cleanMarkData(){ 165 | markData.clear(); 166 | } 167 | 168 | /** 169 | * 计算偏移距离 170 | * 171 | * @param offset 偏移值 172 | * @param min 最小偏移值 173 | * @param max 最大偏移值 174 | * @return int offset 175 | */ 176 | private static int calcOffset(int offset, int min, int max) { 177 | if (offset > max) { 178 | return max; 179 | } else if (offset < min) { 180 | return min; 181 | } else { 182 | return offset; 183 | } 184 | } 185 | 186 | /** 187 | * 删除方法, 这里只会删除某个文件夹下的文件,如果传入的directory是个文件,将不做处理 188 | * 189 | * @param child 需要移动的View 190 | * @param dy 实际偏移量 191 | * @param minOffset 最小偏移量 192 | * @param maxOffset 最大偏移量 193 | * @return void 194 | */ 195 | public static int scroll(View child, int dy, int minOffset, int maxOffset) { 196 | final int initOffset = child.getTop(); 197 | int offset = calcOffset(initOffset - dy, minOffset, maxOffset) - initOffset; 198 | child.offsetTopAndBottom(offset); 199 | return -offset; 200 | } 201 | 202 | /** 203 | * 得到TouchSlop 204 | * 205 | * @param context 上下文 206 | * @return int touchSlop的具体值 207 | */ 208 | public static int getTouchSlop(Context context) { 209 | return ViewConfiguration.get(context).getScaledTouchSlop(); 210 | } 211 | 212 | /** 213 | * 得到种子日期所在周的周日 214 | * 215 | * @param seedDate 种子日期 216 | * @return CalendarDate 所在周周日 217 | */ 218 | public static CalendarDate getSunday(CalendarDate seedDate) {// TODO: 16/12/12 得到一个CustomDate对象 219 | Calendar c = Calendar.getInstance(); 220 | String dateString = seedDate.toString(); 221 | Date date = new Date(); 222 | try { 223 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-d"); 224 | date = sdf.parse(dateString); 225 | } catch (ParseException e) { 226 | System.out.println(e.getMessage()); 227 | } 228 | c.setTime(date); 229 | if (c.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) { 230 | c.add(Calendar.DAY_OF_MONTH, 7 - c.get(Calendar.DAY_OF_WEEK) + 1); 231 | } 232 | return new CalendarDate(c.get(Calendar.YEAR), 233 | c.get(Calendar.MONTH) + 1, 234 | c.get(Calendar.DAY_OF_MONTH)); 235 | } 236 | 237 | /** 238 | * 得到种子日期所在周的周六 239 | * 240 | * @param seedDate 种子日期 241 | * @return CalendarDate 所在周周六 242 | */ 243 | public static CalendarDate getSaturday(CalendarDate seedDate) {// TODO: 16/12/12 得到一个CustomDate对象 244 | Calendar c = Calendar.getInstance(); 245 | String dateString = seedDate.toString(); 246 | Date date = null; 247 | try { 248 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-d"); 249 | date = sdf.parse(dateString); 250 | } catch (ParseException e) { 251 | System.out.println(e.getMessage()); 252 | } 253 | c.setTime(date); 254 | c.add(Calendar.DAY_OF_MONTH, 7 - c.get(Calendar.DAY_OF_WEEK)); 255 | return new CalendarDate(c.get(Calendar.YEAR), 256 | c.get(Calendar.MONTH) + 1, 257 | c.get(Calendar.DAY_OF_MONTH)); 258 | } 259 | 260 | private static int top; 261 | private static boolean customScrollToBottom = false; 262 | 263 | /** 264 | * 判断上一次滑动改变周月日历是向下滑还是向上滑 向下滑表示切换为月日历模式 向上滑表示切换为周日历模式 265 | * 266 | * @return boolean 是否是在向下滑动。(true: 已经收缩; false: 已经打开) 267 | */ 268 | public static boolean isScrollToBottom() { 269 | return customScrollToBottom; 270 | } 271 | 272 | /** 273 | * 设置上一次滑动改变周月日历是向下滑还是向上滑 向下滑表示切换为月日历模式 向上滑表示切换为周日历模式 274 | * 275 | * @return void 276 | */ 277 | public static void setScrollToBottom(boolean customScrollToBottom) { 278 | Utils.customScrollToBottom = customScrollToBottom; 279 | } 280 | 281 | /** 282 | * 通过scrollTo方法完成协调布局的滑动,其中主要使用了ViewCompat.postOnAnimation 283 | * 284 | * @param parent 协调布局parent 285 | * @param child 协调布局协调滑动的child 286 | * @param y 滑动目标位置y轴数值 287 | * @param duration 滑动执行时间 288 | * @return void 289 | */ 290 | public static void scrollTo(final CoordinatorLayout parent, final RecyclerView child, final int y, int duration) { 291 | final Scroller scroller = new Scroller(parent.getContext()); 292 | scroller.startScroll(0, top, 0, y - top, duration); //设置scroller的滚动偏移量 293 | ViewCompat.postOnAnimation(child, new Runnable() { 294 | @Override 295 | public void run() { 296 | //返回值为boolean,true说明滚动尚未完成,false说明滚动已经完成。 297 | // 这是一个很重要的方法,通常放在View.computeScroll()中,用来判断是否滚动是否结束。 298 | if (scroller.computeScrollOffset()) { 299 | int delta = scroller.getCurrY() - child.getTop(); 300 | child.offsetTopAndBottom(delta); 301 | saveTop(child.getTop()); 302 | parent.dispatchDependentViewsChanged(child); 303 | ViewCompat.postOnAnimation(child, this); 304 | } 305 | } 306 | }); 307 | } 308 | 309 | public static void saveTop(int y) { 310 | top = y; 311 | } 312 | 313 | public static int loadTop() { 314 | return top; 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/behavior/MonthPagerBehavior.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.behavior; 2 | 3 | import android.support.design.widget.CoordinatorLayout; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.util.Log; 6 | import android.view.MotionEvent; 7 | import android.view.View; 8 | 9 | import com.ldf.calendar.Utils; 10 | import com.ldf.calendar.component.CalendarViewAdapter; 11 | import com.ldf.calendar.view.MonthPager; 12 | 13 | /** 14 | * Created by ldf on 17/6/15. 15 | */ 16 | 17 | public class MonthPagerBehavior extends CoordinatorLayout.Behavior { 18 | private int top = 0; 19 | private int touchSlop = 1; 20 | private int offsetY = 0; 21 | 22 | @Override 23 | public boolean layoutDependsOn(CoordinatorLayout parent, MonthPager child, View dependency) { 24 | return dependency instanceof RecyclerView; 25 | } 26 | 27 | @Override 28 | public boolean onLayoutChild(CoordinatorLayout parent, MonthPager child, int layoutDirection) { 29 | parent.onLayoutChild(child, layoutDirection); 30 | child.offsetTopAndBottom(top); 31 | return true; 32 | } 33 | 34 | private float downX, downY, lastY, lastTop; 35 | private boolean isVerticalScroll; 36 | private boolean directionUpa; 37 | 38 | @Override 39 | public boolean onTouchEvent(CoordinatorLayout parent, MonthPager child, MotionEvent ev) { 40 | if (downY > lastTop) { 41 | return false; 42 | } 43 | switch (ev.getAction()) { 44 | case MotionEvent.ACTION_DOWN: 45 | break; 46 | case MotionEvent.ACTION_MOVE: 47 | if (isVerticalScroll) { 48 | if (ev.getY() > lastY) { 49 | Utils.setScrollToBottom(true); 50 | directionUpa = false; 51 | } else { 52 | Utils.setScrollToBottom(false); 53 | directionUpa = true; 54 | } 55 | 56 | if (lastTop < child.getViewHeight() / 2 + child.getCellHeight() / 2) { 57 | //这里表示本来是收缩状态 58 | if (ev.getY() - downY <= 0 || Utils.loadTop() >= child.getViewHeight()) { 59 | //向上滑或者已展开了 60 | lastY = ev.getY(); 61 | return true; 62 | } 63 | if (ev.getY() - downY + child.getCellHeight() >= child.getViewHeight()) { 64 | //将要滑过头了 65 | saveTop(child.getViewHeight()); 66 | Utils.scrollTo(parent, (RecyclerView) parent.getChildAt(1), child.getViewHeight(), 10); 67 | isVerticalScroll = false; 68 | } else { 69 | //正常下滑 70 | saveTop((int) (child.getCellHeight() + ((ev.getY() - downY)))); 71 | Utils.scroll(parent.getChildAt(1), (int) (lastY - ev.getY()), 72 | child.getCellHeight(), child.getViewHeight()); 73 | } 74 | } else { 75 | if (ev.getY() - downY >= 0 || Utils.loadTop() <= child.getCellHeight()) { 76 | lastY = ev.getY(); 77 | return true; 78 | } 79 | 80 | if (ev.getY() - downY + child.getViewHeight() <= child.getCellHeight()) { 81 | //将要滑过头了 82 | saveTop(child.getCellHeight()); 83 | Utils.scrollTo(parent, (RecyclerView) parent.getChildAt(1), child.getCellHeight(), 10); 84 | isVerticalScroll = false; 85 | } else { 86 | //正常上滑 87 | saveTop((int) (child.getViewHeight() + ((ev.getY() - downY)))); 88 | Utils.scroll(parent.getChildAt(1), (int) (lastY - ev.getY()), 89 | child.getCellHeight(), child.getViewHeight()); 90 | } 91 | } 92 | 93 | lastY = ev.getY(); 94 | return true; 95 | } 96 | break; 97 | case MotionEvent.ACTION_UP: 98 | if (isVerticalScroll) { 99 | 100 | child.setScrollable(true); 101 | 102 | CalendarViewAdapter calendarViewAdapter = 103 | (CalendarViewAdapter) child.getAdapter(); 104 | if (calendarViewAdapter != null) { 105 | if (directionUpa) { 106 | Utils.setScrollToBottom(true); 107 | calendarViewAdapter.switchToWeek(child.getRowIndex()); 108 | Utils.scrollTo(parent, (RecyclerView) parent.getChildAt(1), child.getCellHeight(), 300); 109 | } else { 110 | Utils.setScrollToBottom(false); 111 | calendarViewAdapter.switchToMonth(); 112 | Utils.scrollTo(parent, (RecyclerView) parent.getChildAt(1), child.getViewHeight(), 300); 113 | } 114 | } 115 | 116 | isVerticalScroll = false; 117 | return true; 118 | } 119 | break; 120 | } 121 | return false; 122 | } 123 | 124 | private void saveTop(int top) { 125 | Utils.saveTop(top); 126 | } 127 | 128 | @Override 129 | public boolean onInterceptTouchEvent(CoordinatorLayout parent, MonthPager child, MotionEvent ev) { 130 | switch (ev.getAction()) { 131 | case MotionEvent.ACTION_DOWN: 132 | downX = ev.getX(); 133 | downY = ev.getY(); 134 | lastTop = Utils.loadTop(); 135 | lastY = downY; 136 | break; 137 | case MotionEvent.ACTION_MOVE: 138 | if (downY > lastTop) { 139 | return false; 140 | } 141 | if (Math.abs(ev.getY() - downY) > 25 && Math.abs(ev.getX() - downX) <= 25 142 | && !isVerticalScroll) { 143 | isVerticalScroll = true; 144 | return true; 145 | } 146 | break; 147 | case MotionEvent.ACTION_UP: 148 | if (isVerticalScroll) { 149 | isVerticalScroll = false; 150 | return true; 151 | } 152 | break; 153 | } 154 | return isVerticalScroll; 155 | } 156 | 157 | private int dependentViewTop = -1; 158 | 159 | @Override 160 | public boolean onDependentViewChanged(CoordinatorLayout parent, MonthPager child, View dependency) { 161 | CalendarViewAdapter calendarViewAdapter = (CalendarViewAdapter) child.getAdapter(); 162 | //dependency对其依赖的view(本例依赖的view是RecycleView) 163 | if (dependentViewTop != -1) { 164 | int dy = dependency.getTop() - dependentViewTop; 165 | int top = child.getTop(); 166 | if (dy > touchSlop) { 167 | calendarViewAdapter.switchToMonth(); 168 | } else if (dy < -touchSlop) { 169 | calendarViewAdapter.switchToWeek(child.getRowIndex()); 170 | } 171 | 172 | if (dy > -top) { 173 | dy = -top; 174 | } 175 | 176 | if (dy < -top - child.getTopMovableDistance()) { 177 | dy = -top - child.getTopMovableDistance(); 178 | } 179 | 180 | child.offsetTopAndBottom(dy); 181 | Log.e("ldf", "onDependentViewChanged = " + dy); 182 | 183 | } 184 | 185 | dependentViewTop = dependency.getTop(); 186 | top = child.getTop(); 187 | 188 | if (offsetY > child.getCellHeight()) { 189 | calendarViewAdapter.switchToMonth(); 190 | } 191 | if (offsetY < -child.getCellHeight()) { 192 | calendarViewAdapter.switchToWeek(child.getRowIndex()); 193 | } 194 | 195 | if (dependentViewTop > child.getCellHeight() - 24 196 | && dependentViewTop < child.getCellHeight() + 24 197 | && top > -touchSlop - child.getTopMovableDistance() 198 | && top < touchSlop - child.getTopMovableDistance()) { 199 | Utils.setScrollToBottom(true); 200 | calendarViewAdapter.switchToWeek(child.getRowIndex()); 201 | offsetY = 0; 202 | } 203 | if (dependentViewTop > child.getViewHeight() - 24 204 | && dependentViewTop < child.getViewHeight() + 24 205 | && top < touchSlop 206 | && top > -touchSlop) { 207 | Utils.setScrollToBottom(false); 208 | calendarViewAdapter.switchToMonth(); 209 | offsetY = 0; 210 | } 211 | 212 | return true; 213 | // TODO: 16/12/8 dy为负时表示向上滑动,dy为正时表示向下滑动,dy为零时表示滑动停止 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/behavior/RecyclerViewBehavior.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.behavior; 2 | 3 | import android.content.Context; 4 | import android.support.design.widget.CoordinatorLayout; 5 | import android.support.v4.view.ViewCompat; 6 | import android.support.v4.view.ViewPager; 7 | import android.support.v7.widget.RecyclerView; 8 | import android.util.AttributeSet; 9 | import android.util.Log; 10 | import android.view.View; 11 | import android.widget.Toast; 12 | 13 | import com.ldf.calendar.Utils; 14 | import com.ldf.calendar.view.MonthPager; 15 | 16 | public class RecyclerViewBehavior extends CoordinatorLayout.Behavior { 17 | private int initOffset = -1; 18 | private int minOffset = -1; 19 | private Context context; 20 | private boolean initiated = false; 21 | boolean hidingTop = false; 22 | boolean showingTop = false; 23 | 24 | public RecyclerViewBehavior(Context context, AttributeSet attrs) { 25 | super(context, attrs); 26 | this.context = context; 27 | } 28 | 29 | @Override 30 | public boolean onLayoutChild(CoordinatorLayout parent, RecyclerView child, int layoutDirection) { 31 | parent.onLayoutChild(child, layoutDirection); 32 | MonthPager monthPager = getMonthPager(parent); 33 | initMinOffsetAndInitOffset(parent, child, monthPager); 34 | return true; 35 | } 36 | 37 | private void initMinOffsetAndInitOffset(CoordinatorLayout parent, 38 | RecyclerView child, 39 | MonthPager monthPager) { 40 | if (monthPager.getBottom() > 0 && initOffset == -1) { 41 | initOffset = monthPager.getViewHeight(); 42 | saveTop(initOffset); 43 | } 44 | if (!initiated) { 45 | initOffset = monthPager.getViewHeight(); 46 | saveTop(initOffset); 47 | initiated = true; 48 | } 49 | child.offsetTopAndBottom(Utils.loadTop()); 50 | minOffset = getMonthPager(parent).getCellHeight(); 51 | } 52 | 53 | @Override 54 | public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, RecyclerView child, 55 | View directTargetChild, View target, int nestedScrollAxes) { 56 | Log.e("ldf", "onStartNestedScroll"); 57 | 58 | MonthPager monthPager = (MonthPager) coordinatorLayout.getChildAt(0); 59 | monthPager.setScrollable(false); 60 | boolean isVertical = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; 61 | 62 | return isVertical; 63 | } 64 | 65 | @Override 66 | public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, RecyclerView child, 67 | View target, int dx, int dy, int[] consumed) { 68 | Log.e("ldf", "onNestedPreScroll"); 69 | super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed); 70 | child.setVerticalScrollBarEnabled(true); 71 | 72 | MonthPager monthPager = (MonthPager) coordinatorLayout.getChildAt(0); 73 | if (monthPager.getPageScrollState() != ViewPager.SCROLL_STATE_IDLE) { 74 | consumed[1] = dy; 75 | Log.w("ldf", "onNestedPreScroll: MonthPager dragging"); 76 | Toast.makeText(context, "loading month data", Toast.LENGTH_SHORT).show(); 77 | return; 78 | } 79 | 80 | // 上滑,正在隐藏顶部的日历 81 | hidingTop = dy > 0 && child.getTop() <= initOffset 82 | && child.getTop() > getMonthPager(coordinatorLayout).getCellHeight(); 83 | // 下滑,正在展示顶部的日历 84 | showingTop = dy < 0 && !ViewCompat.canScrollVertically(target, -1); 85 | 86 | if (hidingTop || showingTop) { 87 | consumed[1] = Utils.scroll(child, dy, 88 | getMonthPager(coordinatorLayout).getCellHeight(), 89 | getMonthPager(coordinatorLayout).getViewHeight()); 90 | saveTop(child.getTop()); 91 | } 92 | } 93 | 94 | @Override 95 | public void onStopNestedScroll(final CoordinatorLayout parent, final RecyclerView child, View target) { 96 | Log.e("ldf", "onStopNestedScroll"); 97 | super.onStopNestedScroll(parent, child, target); 98 | MonthPager monthPager = (MonthPager) parent.getChildAt(0); 99 | monthPager.setScrollable(true); 100 | if (!Utils.isScrollToBottom()) { 101 | if (initOffset - Utils.loadTop() > Utils.getTouchSlop(context) && hidingTop) { 102 | Utils.scrollTo(parent, child, getMonthPager(parent).getCellHeight(), 500); 103 | } else { 104 | Utils.scrollTo(parent, child, getMonthPager(parent).getViewHeight(), 150); 105 | } 106 | } else { 107 | if (Utils.loadTop() - minOffset > Utils.getTouchSlop(context) && showingTop) { 108 | Utils.scrollTo(parent, child, getMonthPager(parent).getViewHeight(), 500); 109 | } else { 110 | Utils.scrollTo(parent, child, getMonthPager(parent).getCellHeight(), 150); 111 | } 112 | } 113 | } 114 | 115 | @Override 116 | public boolean onNestedFling(CoordinatorLayout coordinatorLayout, RecyclerView child, View target, float velocityX, float velocityY, boolean consumed) { 117 | Log.d("ldf", "onNestedFling: velocityY: " + velocityY); 118 | return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed); 119 | } 120 | 121 | @Override 122 | public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, RecyclerView child, View target, float velocityX, float velocityY) { 123 | // 日历隐藏和展示过程,不允许RecyclerView进行fling 124 | if (hidingTop || showingTop) { 125 | return true; 126 | } else { 127 | return false; 128 | } 129 | } 130 | 131 | private MonthPager getMonthPager(CoordinatorLayout coordinatorLayout) { 132 | return (MonthPager) coordinatorLayout.getChildAt(0); 133 | } 134 | 135 | private void saveTop(int top) { 136 | Utils.saveTop(top); 137 | if (Utils.loadTop() == initOffset) { 138 | Utils.setScrollToBottom(false); 139 | } else if (Utils.loadTop() == minOffset) { 140 | Utils.setScrollToBottom(true); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/component/CalendarAttr.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.component; 2 | 3 | /** 4 | * Created by ldf on 17/6/26. 5 | */ 6 | 7 | public class CalendarAttr { 8 | /** 9 | * 以何种方式排列星期:
10 | * {@link CalendarAttr.WeekArrayType}
11 | */ 12 | private WeekArrayType weekArrayType; 13 | 14 | /** 15 | * 日历周布局或者月布局:
16 | * {@link CalendarType} 布局类型
17 | */ 18 | private CalendarType calendarType; 19 | 20 | /** 21 | * 日期格子高度 22 | */ 23 | private int cellHeight; 24 | 25 | /** 26 | * 日期格子宽度 27 | */ 28 | private int cellWidth; 29 | 30 | public WeekArrayType getWeekArrayType() { 31 | return weekArrayType; 32 | } 33 | 34 | public void setWeekArrayType(WeekArrayType weekArrayType) { 35 | this.weekArrayType = weekArrayType; 36 | } 37 | 38 | public CalendarType getCalendarType() { 39 | return calendarType; 40 | } 41 | 42 | public void setCalendarType(CalendarType calendarType) { 43 | this.calendarType = calendarType; 44 | } 45 | 46 | public int getCellHeight() { 47 | return cellHeight; 48 | } 49 | 50 | public void setCellHeight(int cellHeight) { 51 | this.cellHeight = cellHeight; 52 | } 53 | 54 | public int getCellWidth() { 55 | return cellWidth; 56 | } 57 | 58 | public void setCellWidth(int cellWidth) { 59 | this.cellWidth = cellWidth; 60 | } 61 | 62 | public enum WeekArrayType { 63 | /*周日作为本周的第一天*/Sunday, 64 | /*周一作为本周的第一天*/Monday 65 | } 66 | 67 | public enum CalendarType { 68 | WEEK, MONTH 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/component/CalendarRenderer.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.component; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.util.Log; 6 | 7 | import com.ldf.calendar.Const; 8 | import com.ldf.calendar.Utils; 9 | import com.ldf.calendar.interf.IDayRenderer; 10 | import com.ldf.calendar.interf.OnSelectDateListener; 11 | import com.ldf.calendar.model.CalendarDate; 12 | import com.ldf.calendar.view.Calendar; 13 | import com.ldf.calendar.view.Day; 14 | import com.ldf.calendar.view.Week; 15 | 16 | /** 17 | * Created by ldf on 17/6/26. 18 | */ 19 | 20 | public class CalendarRenderer { 21 | private Week weeks[] = new Week[Const.TOTAL_ROW]; // 行数组,每个元素代表一行 22 | private Calendar calendar; 23 | private CalendarAttr attr; 24 | private IDayRenderer dayRenderer; 25 | private Context context; 26 | private OnSelectDateListener onSelectDateListener; // 单元格点击回调事件 27 | private CalendarDate seedDate; //种子日期 28 | private CalendarDate selectedDate; //被选中的日期 29 | private int selectedRowIndex = 0; 30 | 31 | public CalendarRenderer(Calendar calendar, CalendarAttr attr, Context context) { 32 | this.calendar = calendar; 33 | this.attr = attr; 34 | this.context = context; 35 | } 36 | 37 | /** 38 | * 使用dayRenderer绘制每一天 39 | * 40 | * @return void 41 | */ 42 | public void draw(Canvas canvas) { 43 | for (int row = 0; row < Const.TOTAL_ROW; row++) { 44 | if (weeks[row] != null) { 45 | for (int col = 0; col < Const.TOTAL_COL; col++) { 46 | if (weeks[row].days[col] != null) { 47 | dayRenderer.drawDay(canvas, weeks[row].days[col]); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | /** 55 | * 点击某一天时刷新这一天的状态 56 | * 57 | * @return void 58 | */ 59 | public void onClickDate(int col, int row) { 60 | if (col >= Const.TOTAL_COL || row >= Const.TOTAL_ROW) 61 | return; 62 | if (weeks[row] != null) { 63 | if (attr.getCalendarType() == CalendarAttr.CalendarType.MONTH) { 64 | if (weeks[row].days[col].getState() == State.CURRENT_MONTH) { 65 | weeks[row].days[col].setState(State.SELECT); 66 | selectedDate = weeks[row].days[col].getDate(); 67 | CalendarViewAdapter.saveSelectedDate(selectedDate); 68 | onSelectDateListener.onSelectDate(selectedDate); 69 | seedDate = selectedDate; 70 | } 71 | // else if (weeks[row].days[col].getState() == State.PAST_MONTH) { 72 | // selectedDate = weeks[row].days[col].getDate(); 73 | // CalendarViewAdapter.saveSelectedDate(selectedDate); 74 | // onSelectDateListener.onSelectOtherMonth(-1); 75 | // onSelectDateListener.onSelectDate(selectedDate); 76 | // } else if (weeks[row].days[col].getState() == State.NEXT_MONTH) { 77 | // selectedDate = weeks[row].days[col].getDate(); 78 | // CalendarViewAdapter.saveSelectedDate(selectedDate); 79 | // onSelectDateListener.onSelectOtherMonth(1); 80 | // onSelectDateListener.onSelectDate(selectedDate); 81 | // } 82 | } else { 83 | weeks[row].days[col].setState(State.SELECT); 84 | selectedDate = weeks[row].days[col].getDate(); 85 | CalendarViewAdapter.saveSelectedDate(selectedDate); 86 | onSelectDateListener.onSelectDate(selectedDate); 87 | seedDate = selectedDate; 88 | } 89 | } 90 | } 91 | 92 | /** 93 | * 刷新指定行的周数据 94 | * 95 | * @param rowIndex 参数月所在年 96 | * @return void 97 | */ 98 | public void updateWeek(int rowIndex) { 99 | CalendarDate currentWeekLastDay; 100 | if (attr.getWeekArrayType() == CalendarAttr.WeekArrayType.Sunday) { 101 | currentWeekLastDay = Utils.getSaturday(seedDate); 102 | } else { 103 | currentWeekLastDay = Utils.getSunday(seedDate); 104 | } 105 | int day = currentWeekLastDay.day; 106 | for (int i = Const.TOTAL_COL - 1; i >= 0; i--) { 107 | CalendarDate date = currentWeekLastDay.modifyDay(day); 108 | if (weeks[rowIndex] == null) { 109 | weeks[rowIndex] = new Week(rowIndex); 110 | } 111 | if (weeks[rowIndex].days[i] != null) { 112 | if (date.equals(CalendarViewAdapter.loadSelectedDate())) { 113 | weeks[rowIndex].days[i].setState(State.SELECT); 114 | weeks[rowIndex].days[i].setDate(date); 115 | } else { 116 | weeks[rowIndex].days[i].setState(State.CURRENT_MONTH); 117 | weeks[rowIndex].days[i].setDate(date); 118 | } 119 | } else { 120 | if (date.equals(CalendarViewAdapter.loadSelectedDate())) { 121 | weeks[rowIndex].days[i] = new Day(State.SELECT, date, rowIndex, i); 122 | } else { 123 | weeks[rowIndex].days[i] = new Day(State.CURRENT_MONTH, date, rowIndex, i); 124 | } 125 | } 126 | day--; 127 | } 128 | } 129 | 130 | /** 131 | * 填充月数据 132 | * 133 | * @return void 134 | */ 135 | private void instantiateMonth() { 136 | int lastMonthDays = Utils.getMonthDays(seedDate.year, seedDate.month - 1); // 上个月的天数 137 | int currentMonthDays = Utils.getMonthDays(seedDate.year, seedDate.month); // 当前月的天数 138 | int firstDayPosition = Utils.getFirstDayWeekPosition( 139 | seedDate.year, 140 | seedDate.month, 141 | attr.getWeekArrayType()); 142 | Log.e("ldf","firstDayPosition = " + firstDayPosition); 143 | 144 | int day = 0; 145 | for (int row = 0; row < Const.TOTAL_ROW; row++) { 146 | day = fillWeek(lastMonthDays, currentMonthDays, firstDayPosition, day, row); 147 | } 148 | } 149 | 150 | public CalendarDate getFirstDate() { 151 | Week week = weeks[0]; 152 | Day day = week.days[0]; 153 | return day.getDate(); 154 | } 155 | 156 | public CalendarDate getLastDate() { 157 | Week week = weeks[weeks.length - 1]; 158 | Day day = week.days[week.days.length - 1]; 159 | return day.getDate(); 160 | } 161 | 162 | /** 163 | * 填充月中周数据 164 | * 165 | * @return void 166 | */ 167 | private int fillWeek(int lastMonthDays, 168 | int currentMonthDays, 169 | int firstDayWeek, 170 | int day, 171 | int row) { 172 | for (int col = 0; col < Const.TOTAL_COL; col++) { 173 | int position = col + row * Const.TOTAL_COL;// 单元格位置 174 | if (position >= firstDayWeek && position < firstDayWeek + currentMonthDays) { 175 | day++; 176 | fillCurrentMonthDate(day, row, col); 177 | } else if (position < firstDayWeek) { 178 | instantiateLastMonth(lastMonthDays, firstDayWeek, row, col, position); 179 | } else if (position >= firstDayWeek + currentMonthDays) { 180 | instantiateNextMonth(currentMonthDays, firstDayWeek, row, col, position); 181 | } 182 | } 183 | return day; 184 | } 185 | 186 | private void fillCurrentMonthDate(int day, int row, int col) { 187 | CalendarDate date = seedDate.modifyDay(day); 188 | if (weeks[row] == null) { 189 | weeks[row] = new Week(row); 190 | } 191 | if (weeks[row].days[col] != null) { 192 | if (date.equals(CalendarViewAdapter.loadSelectedDate())) { 193 | weeks[row].days[col].setDate(date); 194 | weeks[row].days[col].setState(State.SELECT); 195 | } else { 196 | weeks[row].days[col].setDate(date); 197 | weeks[row].days[col].setState(State.CURRENT_MONTH); 198 | } 199 | } else { 200 | if (date.equals(CalendarViewAdapter.loadSelectedDate())) { 201 | weeks[row].days[col] = new Day(State.SELECT, date, row, col); 202 | } else { 203 | weeks[row].days[col] = new Day(State.CURRENT_MONTH, date, row, col); 204 | } 205 | } 206 | if (date.equals(seedDate)) { 207 | selectedRowIndex = row; 208 | } 209 | } 210 | 211 | private void instantiateNextMonth(int currentMonthDays, 212 | int firstDayWeek, 213 | int row, 214 | int col, 215 | int position) { 216 | CalendarDate date = new CalendarDate( 217 | seedDate.year, 218 | seedDate.month + 1, 219 | position - firstDayWeek - currentMonthDays + 1); 220 | if (weeks[row] == null) { 221 | weeks[row] = new Week(row); 222 | } 223 | if (weeks[row].days[col] != null) { 224 | weeks[row].days[col].setDate(date); 225 | weeks[row].days[col].setState(State.NEXT_MONTH); 226 | } else { 227 | weeks[row].days[col] = new Day(State.NEXT_MONTH, date, row, col); 228 | } 229 | // TODO: 17/6/27 当下一个月的天数大于七时,说明该月有六周 230 | // if(position - firstDayWeek - currentMonthDays + 1 >= 7) { //当下一个月的天数大于七时,说明该月有六周 231 | // } 232 | } 233 | 234 | private void instantiateLastMonth(int lastMonthDays, int firstDayWeek, int row, int col, int position) { 235 | CalendarDate date = new CalendarDate( 236 | seedDate.year, 237 | seedDate.month - 1, 238 | lastMonthDays - (firstDayWeek - position - 1)); 239 | if (weeks[row] == null) { 240 | weeks[row] = new Week(row); 241 | } 242 | if (weeks[row].days[col] != null) { 243 | weeks[row].days[col].setDate(date); 244 | weeks[row].days[col].setState(State.PAST_MONTH); 245 | } else { 246 | weeks[row].days[col] = new Day(State.PAST_MONTH, date, row, col); 247 | } 248 | } 249 | 250 | /** 251 | * 根据种子日期孵化出本日历牌的数据 252 | * 253 | * @return void 254 | */ 255 | public void showDate(CalendarDate seedDate) { 256 | if (seedDate != null) { 257 | this.seedDate = seedDate; 258 | } else { 259 | this.seedDate = new CalendarDate(); 260 | } 261 | update(); 262 | } 263 | 264 | public void update() { 265 | instantiateMonth(); 266 | calendar.invalidate(); 267 | } 268 | 269 | public CalendarDate getSeedDate() { 270 | return this.seedDate; 271 | } 272 | 273 | public void cancelSelectState() { 274 | for (int i = 0; i < Const.TOTAL_ROW; i++) { 275 | if (weeks[i] != null) { 276 | for (int j = 0; j < Const.TOTAL_COL; j++) { 277 | if (weeks[i].days[j].getState() == State.SELECT) { 278 | weeks[i].days[j].setState(State.CURRENT_MONTH); 279 | resetSelectedRowIndex(); 280 | break; 281 | } 282 | } 283 | } 284 | } 285 | } 286 | 287 | public void resetSelectedRowIndex() { 288 | selectedRowIndex = 0; 289 | } 290 | 291 | public int getSelectedRowIndex() { 292 | return selectedRowIndex; 293 | } 294 | 295 | public void setSelectedRowIndex(int selectedRowIndex) { 296 | this.selectedRowIndex = selectedRowIndex; 297 | } 298 | 299 | public Calendar getCalendar() { 300 | return calendar; 301 | } 302 | 303 | public void setCalendar(Calendar calendar) { 304 | this.calendar = calendar; 305 | } 306 | 307 | public CalendarAttr getAttr() { 308 | return attr; 309 | } 310 | 311 | public void setAttr(CalendarAttr attr) { 312 | this.attr = attr; 313 | } 314 | 315 | public Context getContext() { 316 | return context; 317 | } 318 | 319 | public void setContext(Context context) { 320 | this.context = context; 321 | } 322 | 323 | public void setOnSelectDateListener(OnSelectDateListener onSelectDateListener) { 324 | this.onSelectDateListener = onSelectDateListener; 325 | } 326 | 327 | public void setDayRenderer(IDayRenderer dayRenderer) { 328 | this.dayRenderer = dayRenderer; 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/component/CalendarViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.component; 2 | 3 | import android.content.Context; 4 | import android.support.v4.view.PagerAdapter; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import com.ldf.calendar.interf.OnAdapterSelectListener; 10 | import com.ldf.calendar.interf.IDayRenderer; 11 | import com.ldf.calendar.interf.OnSelectDateListener; 12 | import com.ldf.calendar.Utils; 13 | import com.ldf.calendar.view.MonthPager; 14 | import com.ldf.calendar.model.CalendarDate; 15 | import com.ldf.calendar.view.Calendar; 16 | 17 | import java.util.ArrayList; 18 | import java.util.HashMap; 19 | 20 | public class CalendarViewAdapter extends PagerAdapter { 21 | 22 | private static CalendarDate date = new CalendarDate(); 23 | private ArrayList calendars = new ArrayList<>(); 24 | private int currentPosition = MonthPager.CURRENT_DAY_INDEX; 25 | private CalendarAttr.CalendarType calendarType = CalendarAttr.CalendarType.MONTH; 26 | private int rowCount = 0; 27 | private CalendarDate seedDate; 28 | private OnCalendarTypeChanged onCalendarTypeChangedListener; 29 | //周排列方式 1:代表周日显示为本周的第一天 30 | //0:代表周一显示为本周的第一天 31 | private CalendarAttr.WeekArrayType weekArrayType = CalendarAttr.WeekArrayType.Monday; 32 | 33 | public CalendarViewAdapter(Context context, 34 | OnSelectDateListener onSelectDateListener, 35 | CalendarAttr.WeekArrayType weekArrayType, 36 | IDayRenderer dayView) { 37 | super(); 38 | this.weekArrayType = weekArrayType; 39 | init(context, onSelectDateListener); 40 | setCustomDayRenderer(dayView); 41 | } 42 | 43 | public static void saveSelectedDate(CalendarDate calendarDate) { 44 | date = calendarDate; 45 | } 46 | 47 | public static CalendarDate loadSelectedDate() { 48 | return date; 49 | } 50 | 51 | private void init(Context context, OnSelectDateListener onSelectDateListener) { 52 | saveSelectedDate(new CalendarDate()); 53 | //初始化的种子日期为今天 54 | seedDate = new CalendarDate(); 55 | for (int i = 0; i < 3; i++) { 56 | CalendarAttr calendarAttr = new CalendarAttr(); 57 | calendarAttr.setCalendarType(CalendarAttr.CalendarType.MONTH); 58 | calendarAttr.setWeekArrayType(weekArrayType); 59 | Calendar calendar = new Calendar(context, onSelectDateListener, calendarAttr); 60 | calendar.setOnAdapterSelectListener(new OnAdapterSelectListener() { 61 | @Override 62 | public void cancelSelectState() { 63 | cancelOtherSelectState(); 64 | } 65 | 66 | @Override 67 | public void updateSelectState() { 68 | invalidateCurrentCalendar(); 69 | } 70 | }); 71 | calendars.add(calendar); 72 | } 73 | } 74 | 75 | @Override 76 | public void setPrimaryItem(ViewGroup container, int position, Object object) { 77 | Log.e("ldf", "setPrimaryItem"); 78 | super.setPrimaryItem(container, position, object); 79 | this.currentPosition = position; 80 | } 81 | 82 | @Override 83 | public Object instantiateItem(ViewGroup container, int position) { 84 | Log.e("ldf", "instantiateItem"); 85 | if (position < 2) { 86 | return null; 87 | } 88 | Calendar calendar = calendars.get(position % calendars.size()); 89 | if (calendarType == CalendarAttr.CalendarType.MONTH) { 90 | CalendarDate current = seedDate.modifyMonth(position - MonthPager.CURRENT_DAY_INDEX); 91 | current.setDay(1);//每月的种子日期都是1号 92 | calendar.showDate(current); 93 | } else { 94 | CalendarDate current = seedDate.modifyWeek(position - MonthPager.CURRENT_DAY_INDEX); 95 | if (weekArrayType == CalendarAttr.WeekArrayType.Sunday) { 96 | calendar.showDate(Utils.getSaturday(current)); 97 | } else { 98 | calendar.showDate(Utils.getSunday(current)); 99 | }//每周的种子日期为这一周的最后一天 100 | calendar.updateWeek(rowCount); 101 | } 102 | if (container.getChildCount() == calendars.size()) { 103 | container.removeView(calendars.get(position % 3)); 104 | } 105 | if (container.getChildCount() < calendars.size()) { 106 | container.addView(calendar, 0); 107 | } else { 108 | container.addView(calendar, position % 3); 109 | } 110 | return calendar; 111 | } 112 | 113 | @Override 114 | public int getCount() { 115 | return Integer.MAX_VALUE; 116 | } 117 | 118 | @Override 119 | public boolean isViewFromObject(View view, Object object) { 120 | return view == ((View) object); 121 | } 122 | 123 | @Override 124 | public void destroyItem(ViewGroup container, int position, Object object) { 125 | container.removeView(container); 126 | } 127 | 128 | public ArrayList getPagers() { 129 | return calendars; 130 | } 131 | 132 | public CalendarDate getFirstVisibleDate() { 133 | return calendars.get(currentPosition % 3).getFirstDate(); 134 | } 135 | 136 | public CalendarDate getLastVisibleDate() { 137 | return calendars.get(currentPosition % 3).getLastDate(); 138 | } 139 | 140 | public void cancelOtherSelectState() { 141 | for (int i = 0; i < calendars.size(); i++) { 142 | Calendar calendar = calendars.get(i); 143 | calendar.cancelSelectState(); 144 | } 145 | } 146 | 147 | public void invalidateCurrentCalendar() { 148 | for (int i = 0; i < calendars.size(); i++) { 149 | Calendar calendar = calendars.get(i); 150 | calendar.update(); 151 | if (calendar.getCalendarType() == CalendarAttr.CalendarType.WEEK) { 152 | calendar.updateWeek(rowCount); 153 | } 154 | } 155 | } 156 | 157 | public void setMarkData(HashMap markData) { 158 | Utils.setMarkData(markData); 159 | notifyDataChanged(); 160 | } 161 | 162 | public void switchToMonth() { 163 | if (calendars != null && calendars.size() > 0 && calendarType != CalendarAttr.CalendarType.MONTH) { 164 | if (onCalendarTypeChangedListener != null) { 165 | onCalendarTypeChangedListener.onCalendarTypeChanged(CalendarAttr.CalendarType.MONTH); 166 | } 167 | calendarType = CalendarAttr.CalendarType.MONTH; 168 | MonthPager.CURRENT_DAY_INDEX = currentPosition; 169 | Calendar v = calendars.get(currentPosition % 3);//0 170 | seedDate = v.getSeedDate(); 171 | 172 | Calendar v1 = calendars.get(currentPosition % 3);//0 173 | v1.switchCalendarType(CalendarAttr.CalendarType.MONTH); 174 | v1.showDate(seedDate); 175 | 176 | Calendar v2 = calendars.get((currentPosition - 1) % 3);//2 177 | v2.switchCalendarType(CalendarAttr.CalendarType.MONTH); 178 | CalendarDate last = seedDate.modifyMonth(-1); 179 | last.setDay(1); 180 | v2.showDate(last); 181 | 182 | Calendar v3 = calendars.get((currentPosition + 1) % 3);//1 183 | v3.switchCalendarType(CalendarAttr.CalendarType.MONTH); 184 | CalendarDate next = seedDate.modifyMonth(1); 185 | next.setDay(1); 186 | v3.showDate(next); 187 | } 188 | } 189 | 190 | public void switchToWeek(int rowIndex) { 191 | rowCount = rowIndex; 192 | if (calendars != null && calendars.size() > 0 && calendarType != CalendarAttr.CalendarType.WEEK) { 193 | if (onCalendarTypeChangedListener != null) { 194 | onCalendarTypeChangedListener.onCalendarTypeChanged(CalendarAttr.CalendarType.WEEK); 195 | } 196 | calendarType = CalendarAttr.CalendarType.WEEK; 197 | MonthPager.CURRENT_DAY_INDEX = currentPosition; 198 | Calendar v = calendars.get(currentPosition % 3); 199 | seedDate = v.getSeedDate(); 200 | 201 | rowCount = v.getSelectedRowIndex(); 202 | 203 | Calendar v1 = calendars.get(currentPosition % 3); 204 | v1.switchCalendarType(CalendarAttr.CalendarType.WEEK); 205 | v1.showDate(seedDate); 206 | v1.updateWeek(rowIndex); 207 | 208 | Calendar v2 = calendars.get((currentPosition - 1) % 3); 209 | v2.switchCalendarType(CalendarAttr.CalendarType.WEEK); 210 | CalendarDate last = seedDate.modifyWeek(-1); 211 | if (weekArrayType == CalendarAttr.WeekArrayType.Sunday) { 212 | v2.showDate(Utils.getSaturday(last)); 213 | } else { 214 | v2.showDate(Utils.getSunday(last)); 215 | }//每周的种子日期为这一周的最后一天 216 | v2.updateWeek(rowIndex); 217 | 218 | Calendar v3 = calendars.get((currentPosition + 1) % 3); 219 | v3.switchCalendarType(CalendarAttr.CalendarType.WEEK); 220 | CalendarDate next = seedDate.modifyWeek(1); 221 | if (weekArrayType == CalendarAttr.WeekArrayType.Sunday) { 222 | v3.showDate(Utils.getSaturday(next)); 223 | } else { 224 | v3.showDate(Utils.getSunday(next)); 225 | }//每周的种子日期为这一周的最后一天 226 | v3.updateWeek(rowIndex); 227 | } 228 | } 229 | 230 | public void notifyMonthDataChanged(CalendarDate date) { 231 | seedDate = date; 232 | refreshCalendar(); 233 | } 234 | 235 | public void notifyDataChanged(CalendarDate date) { 236 | seedDate = date; 237 | saveSelectedDate(date); 238 | refreshCalendar(); 239 | } 240 | 241 | public void notifyDataChanged() { 242 | refreshCalendar(); 243 | } 244 | 245 | private void refreshCalendar() { 246 | if (calendarType == CalendarAttr.CalendarType.WEEK) { 247 | MonthPager.CURRENT_DAY_INDEX = currentPosition; 248 | Calendar v1 = calendars.get(currentPosition % 3); 249 | v1.showDate(seedDate); 250 | v1.updateWeek(rowCount); 251 | 252 | Calendar v2 = calendars.get((currentPosition - 1) % 3); 253 | CalendarDate last = seedDate.modifyWeek(-1); 254 | if (weekArrayType == CalendarAttr.WeekArrayType.Sunday) { 255 | v2.showDate(Utils.getSaturday(last)); 256 | } else { 257 | v2.showDate(Utils.getSunday(last)); 258 | } 259 | v2.updateWeek(rowCount); 260 | 261 | Calendar v3 = calendars.get((currentPosition + 1) % 3); 262 | CalendarDate next = seedDate.modifyWeek(1); 263 | if (weekArrayType == CalendarAttr.WeekArrayType.Sunday) { 264 | v3.showDate(Utils.getSaturday(next)); 265 | } else { 266 | v3.showDate(Utils.getSunday(next)); 267 | }//每周的种子日期为这一周的最后一天 268 | v3.updateWeek(rowCount); 269 | } else { 270 | MonthPager.CURRENT_DAY_INDEX = currentPosition; 271 | 272 | Calendar v1 = calendars.get(currentPosition % 3);//0 273 | v1.showDate(seedDate); 274 | 275 | Calendar v2 = calendars.get((currentPosition - 1) % 3);//2 276 | CalendarDate last = seedDate.modifyMonth(-1); 277 | last.setDay(1); 278 | v2.showDate(last); 279 | 280 | Calendar v3 = calendars.get((currentPosition + 1) % 3);//1 281 | CalendarDate next = seedDate.modifyMonth(1); 282 | next.setDay(1); 283 | v3.showDate(next); 284 | } 285 | } 286 | 287 | public CalendarAttr.CalendarType getCalendarType() { 288 | return calendarType; 289 | } 290 | 291 | /** 292 | * 为每一个Calendar实例设置renderer对象 293 | * 294 | * @return void 295 | */ 296 | public void setCustomDayRenderer(IDayRenderer dayRenderer) { 297 | Calendar c0 = calendars.get(0); 298 | c0.setDayRenderer(dayRenderer); 299 | 300 | Calendar c1 = calendars.get(1); 301 | c1.setDayRenderer(dayRenderer.copy()); 302 | 303 | Calendar c2 = calendars.get(2); 304 | c2.setDayRenderer(dayRenderer.copy()); 305 | } 306 | 307 | public void setOnCalendarTypeChangedListener(OnCalendarTypeChanged onCalendarTypeChangedListener) { 308 | this.onCalendarTypeChangedListener = onCalendarTypeChangedListener; 309 | } 310 | 311 | public CalendarAttr.WeekArrayType getWeekArrayType() { 312 | return weekArrayType; 313 | } 314 | 315 | public interface OnCalendarTypeChanged { 316 | void onCalendarTypeChanged(CalendarAttr.CalendarType type); 317 | } 318 | } -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/component/State.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.component; 2 | 3 | /** 4 | * Created by ldf on 17/6/27. 5 | */ 6 | 7 | public enum State { 8 | CURRENT_MONTH, PAST_MONTH, NEXT_MONTH, SELECT 9 | } 10 | -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/interf/IDayRenderer.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.interf; 2 | 3 | import android.graphics.Canvas; 4 | 5 | import com.ldf.calendar.component.State; 6 | import com.ldf.calendar.model.CalendarDate; 7 | import com.ldf.calendar.view.Day; 8 | import com.ldf.calendar.view.DayView; 9 | 10 | /** 11 | * Created by ldf on 17/6/26. 12 | */ 13 | 14 | public interface IDayRenderer { 15 | 16 | void refreshContent(); 17 | 18 | void drawDay(Canvas canvas, Day day); 19 | 20 | IDayRenderer copy(); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/interf/OnAdapterSelectListener.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.interf; 2 | 3 | import com.ldf.calendar.model.CalendarDate; 4 | 5 | /** 6 | * Created by ldf on 17/6/15. 7 | */ 8 | 9 | public interface OnAdapterSelectListener { 10 | void cancelSelectState(); 11 | void updateSelectState(); 12 | } 13 | -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/interf/OnSelectDateListener.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.interf; 2 | 3 | import com.ldf.calendar.model.CalendarDate; 4 | 5 | /** 6 | * Created by ldf on 17/6/2. 7 | */ 8 | 9 | public interface OnSelectDateListener { 10 | void onSelectDate(CalendarDate date); 11 | 12 | void onSelectOtherMonth(int offset);//点击其它月份日期 13 | } 14 | -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/model/CalendarDate.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.model; 2 | 3 | import android.util.Log; 4 | 5 | import com.ldf.calendar.Utils; 6 | 7 | import java.io.Serializable; 8 | import java.util.Calendar; 9 | 10 | public class CalendarDate implements Serializable { 11 | private static final long serialVersionUID = 1L; 12 | public int year; 13 | public int month; //1~12 14 | public int day; 15 | 16 | public CalendarDate(int year, int month, int day) { 17 | if (month > 12) { 18 | month = 1; 19 | year++; 20 | } else if (month < 1) { 21 | month = 12; 22 | year--; 23 | } 24 | this.year = year; 25 | this.month = month; 26 | this.day = day; 27 | } 28 | 29 | public CalendarDate() { 30 | this.year = Utils.getYear(); 31 | this.month = Utils.getMonth(); 32 | this.day = Utils.getDay(); 33 | } 34 | 35 | /** 36 | * 通过修改当前Date对象的天数返回一个修改后的Date 37 | * 38 | * @return CalendarDate 修改后的日期 39 | */ 40 | public CalendarDate modifyDay(int day) { 41 | int lastMonthDays = Utils.getMonthDays(this.year, this.month - 1); 42 | int currentMonthDays = Utils.getMonthDays(this.year, this.month); 43 | 44 | CalendarDate modifyDate; 45 | if (day > currentMonthDays) { 46 | modifyDate = new CalendarDate(this.year, this.month, this.day); 47 | Log.e("ldf", "移动天数过大"); 48 | } else if (day > 0) { 49 | modifyDate = new CalendarDate(this.year, this.month, day); 50 | } else if (day > 0 - lastMonthDays) { 51 | modifyDate = new CalendarDate(this.year, this.month - 1, lastMonthDays + day); 52 | } else { 53 | modifyDate = new CalendarDate(this.year, this.month, this.day); 54 | Log.e("ldf", "移动天数过大"); 55 | } 56 | return modifyDate; 57 | } 58 | 59 | /** 60 | * 通过修改当前Date对象的所在周返回一个修改后的Date 61 | * 62 | * @return CalendarDate 修改后的日期 63 | */ 64 | public CalendarDate modifyWeek(int offset) { 65 | CalendarDate result = new CalendarDate(); 66 | Calendar c = Calendar.getInstance(); 67 | c.set(Calendar.YEAR, year); 68 | c.set(Calendar.MONTH, month - 1); 69 | c.set(Calendar.DAY_OF_MONTH, day); 70 | c.add(Calendar.DATE, offset * 7); 71 | result.setYear(c.get(Calendar.YEAR)); 72 | result.setMonth(c.get(Calendar.MONTH) + 1); 73 | result.setDay(c.get(Calendar.DATE)); 74 | return result; 75 | } 76 | 77 | /** 78 | * 通过修改当前Date对象的所在月返回一个修改后的Date 79 | * 80 | * @return CalendarDate 修改后的日期 81 | */ 82 | public CalendarDate modifyMonth(int offset) { 83 | CalendarDate result = new CalendarDate(); 84 | int addToMonth = this.month + offset; 85 | if (offset > 0) { 86 | if (addToMonth > 12) { 87 | result.setYear(this.year + (addToMonth - 1) / 12); 88 | result.setMonth(addToMonth % 12 == 0 ? 12 : addToMonth % 12); 89 | } else { 90 | result.setYear(this.year); 91 | result.setMonth(addToMonth); 92 | } 93 | } else { 94 | if (addToMonth == 0) { 95 | result.setYear(this.year - 1); 96 | result.setMonth(12); 97 | } else if (addToMonth < 0) { 98 | result.setYear(this.year + addToMonth / 12 - 1); 99 | int month = 12 - Math.abs(addToMonth) % 12; 100 | result.setMonth(month == 0 ? 12 : month); 101 | } else { 102 | result.setYear(this.year); 103 | result.setMonth(addToMonth == 0 ? 12 : addToMonth); 104 | } 105 | } 106 | return result; 107 | } 108 | 109 | @Override 110 | public String toString() { 111 | return year + "-" + month + "-" + day; 112 | } 113 | 114 | public int getYear() { 115 | return year; 116 | } 117 | 118 | public void setYear(int year) { 119 | this.year = year; 120 | } 121 | 122 | public int getMonth() { 123 | return month; 124 | } 125 | 126 | public void setMonth(int month) { 127 | this.month = month; 128 | } 129 | 130 | public int getDay() { 131 | return day; 132 | } 133 | 134 | public void setDay(int day) { 135 | this.day = day; 136 | } 137 | 138 | public boolean equals(CalendarDate date) { 139 | if (date == null) { 140 | return false; 141 | } 142 | if (this.getYear() == date.getYear() 143 | && this.getMonth() == date.getMonth() 144 | && this.getDay() == date.getDay()) { 145 | return true; 146 | } 147 | return false; 148 | } 149 | 150 | public CalendarDate cloneSelf() { 151 | return new CalendarDate(year, month, day); 152 | } 153 | 154 | } -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/view/Calendar.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.view; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.view.MotionEvent; 7 | import android.view.View; 8 | 9 | import com.ldf.calendar.Const; 10 | import com.ldf.calendar.interf.IDayRenderer; 11 | import com.ldf.calendar.interf.OnAdapterSelectListener; 12 | import com.ldf.calendar.component.CalendarAttr; 13 | import com.ldf.calendar.component.CalendarRenderer; 14 | import com.ldf.calendar.interf.OnSelectDateListener; 15 | import com.ldf.calendar.model.CalendarDate; 16 | import com.ldf.calendar.Utils; 17 | 18 | @SuppressLint("ViewConstructor") 19 | public class Calendar extends View { 20 | /** 21 | * 日历列数 22 | */ 23 | private CalendarAttr.CalendarType calendarType; 24 | private int cellHeight; // 单元格高度 25 | private int cellWidth; // 单元格宽度 26 | 27 | private OnSelectDateListener onSelectDateListener; // 单元格点击回调事件 28 | private Context context; 29 | private CalendarAttr calendarAttr; 30 | private CalendarRenderer renderer; 31 | 32 | private OnAdapterSelectListener onAdapterSelectListener; 33 | private float touchSlop; 34 | 35 | public Calendar(Context context, 36 | OnSelectDateListener onSelectDateListener, 37 | CalendarAttr attr) { 38 | super(context); 39 | this.onSelectDateListener = onSelectDateListener; 40 | calendarAttr = attr; 41 | init(context); 42 | } 43 | 44 | private void init(Context context) { 45 | this.context = context; 46 | touchSlop = Utils.getTouchSlop(context); 47 | initAttrAndRenderer(); 48 | } 49 | 50 | private void initAttrAndRenderer() { 51 | renderer = new CalendarRenderer(this, calendarAttr, context); 52 | renderer.setOnSelectDateListener(onSelectDateListener); 53 | } 54 | 55 | @Override 56 | protected void onDraw(Canvas canvas) { 57 | super.onDraw(canvas); 58 | renderer.draw(canvas); 59 | } 60 | 61 | @Override 62 | protected void onSizeChanged(int w, int h, int oldW, int oldH) { 63 | super.onSizeChanged(w, h, oldW, oldH); 64 | cellHeight = h / Const.TOTAL_ROW; 65 | cellWidth = w / Const.TOTAL_COL; 66 | calendarAttr.setCellHeight(cellHeight); 67 | calendarAttr.setCellWidth(cellWidth); 68 | renderer.setAttr(calendarAttr); 69 | } 70 | 71 | private float posX = 0; 72 | private float posY = 0; 73 | 74 | /* 75 | * 触摸事件为了确定点击的位置日期 76 | */ 77 | @Override 78 | public boolean onTouchEvent(MotionEvent event) { 79 | switch (event.getAction()) { 80 | case MotionEvent.ACTION_DOWN: 81 | posX = event.getX(); 82 | posY = event.getY(); 83 | break; 84 | case MotionEvent.ACTION_UP: 85 | float disX = event.getX() - posX; 86 | float disY = event.getY() - posY; 87 | if (Math.abs(disX) < touchSlop && Math.abs(disY) < touchSlop) { 88 | int col = (int) (posX / cellWidth); 89 | int row = (int) (posY / cellHeight); 90 | onAdapterSelectListener.cancelSelectState(); 91 | renderer.onClickDate(col, row); 92 | onAdapterSelectListener.updateSelectState(); 93 | invalidate(); 94 | } 95 | break; 96 | } 97 | return true; 98 | } 99 | 100 | public CalendarAttr.CalendarType getCalendarType() { 101 | return calendarAttr.getCalendarType(); 102 | } 103 | 104 | public void switchCalendarType(CalendarAttr.CalendarType calendarType) { 105 | calendarAttr.setCalendarType(calendarType); 106 | renderer.setAttr(calendarAttr); 107 | } 108 | 109 | public int getCellHeight() { 110 | return cellHeight; 111 | } 112 | 113 | public void resetSelectedRowIndex() { 114 | renderer.resetSelectedRowIndex(); 115 | } 116 | 117 | public int getSelectedRowIndex() { 118 | return renderer.getSelectedRowIndex(); 119 | } 120 | 121 | public void setSelectedRowIndex(int selectedRowIndex) { 122 | renderer.setSelectedRowIndex(selectedRowIndex); 123 | } 124 | 125 | public void setOnAdapterSelectListener(OnAdapterSelectListener onAdapterSelectListener) { 126 | this.onAdapterSelectListener = onAdapterSelectListener; 127 | } 128 | 129 | public void showDate(CalendarDate current) { 130 | renderer.showDate(current); 131 | } 132 | 133 | public void updateWeek(int rowCount) { 134 | renderer.updateWeek(rowCount); 135 | invalidate(); 136 | } 137 | 138 | public void update() { 139 | renderer.update(); 140 | } 141 | 142 | public void cancelSelectState() { 143 | renderer.cancelSelectState(); 144 | } 145 | 146 | public CalendarDate getSeedDate() { 147 | return renderer.getSeedDate(); 148 | } 149 | 150 | public CalendarDate getFirstDate() { 151 | return renderer.getFirstDate(); 152 | } 153 | 154 | public CalendarDate getLastDate() { 155 | return renderer.getLastDate(); 156 | } 157 | 158 | public void setDayRenderer(IDayRenderer dayRenderer) { 159 | renderer.setDayRenderer(dayRenderer); 160 | } 161 | } -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/view/Day.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.view; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.ldf.calendar.component.State; 7 | import com.ldf.calendar.model.CalendarDate; 8 | 9 | /** 10 | * Created by ldf on 17/7/5. 11 | */ 12 | 13 | public class Day implements Parcelable { 14 | private State state; 15 | private CalendarDate date; 16 | private int posRow; 17 | private int posCol; 18 | 19 | public Day(State state , CalendarDate date , int posRow , int posCol) { 20 | this.state = state; 21 | this.date = date; 22 | this.posRow = posRow; 23 | this.posCol = posCol; 24 | } 25 | 26 | public State getState() { 27 | return state; 28 | } 29 | 30 | public void setState(State state) { 31 | this.state = state; 32 | } 33 | 34 | public CalendarDate getDate() { 35 | return date; 36 | } 37 | 38 | public void setDate(CalendarDate date) { 39 | this.date = date; 40 | } 41 | 42 | public int getPosRow() { 43 | return posRow; 44 | } 45 | 46 | public void setPosRow(int posRow) { 47 | this.posRow = posRow; 48 | } 49 | 50 | public int getPosCol() { 51 | return posCol; 52 | } 53 | 54 | public void setPosCol(int posCol) { 55 | this.posCol = posCol; 56 | } 57 | 58 | @Override 59 | public int describeContents() { 60 | return 0; 61 | } 62 | 63 | @Override 64 | public void writeToParcel(Parcel dest, int flags) { 65 | dest.writeInt(this.state == null ? -1 : this.state.ordinal()); 66 | dest.writeSerializable(this.date); 67 | dest.writeInt(this.posRow); 68 | dest.writeInt(this.posCol); 69 | } 70 | 71 | protected Day(Parcel in) { 72 | int tmpState = in.readInt(); 73 | this.state = tmpState == -1 ? null : State.values()[tmpState]; 74 | this.date = (CalendarDate) in.readSerializable(); 75 | this.posRow = in.readInt(); 76 | this.posCol = in.readInt(); 77 | } 78 | 79 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 80 | @Override 81 | public Day createFromParcel(Parcel source) { 82 | return new Day(source); 83 | } 84 | 85 | @Override 86 | public Day[] newArray(int size) { 87 | return new Day[size]; 88 | } 89 | }; 90 | } 91 | -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/view/DayView.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.util.Log; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.widget.RelativeLayout; 9 | 10 | import com.ldf.calendar.Utils; 11 | import com.ldf.calendar.component.State; 12 | import com.ldf.calendar.interf.IDayRenderer; 13 | import com.ldf.calendar.model.CalendarDate; 14 | 15 | /** 16 | * Created by ldf on 16/10/19. 17 | */ 18 | 19 | public abstract class DayView extends RelativeLayout implements IDayRenderer { 20 | 21 | protected Day day; 22 | protected Context context; 23 | protected int layoutResource; 24 | 25 | /** 26 | * 构造器 传入资源文件创建DayView 27 | * 28 | * @param layoutResource 资源文件 29 | * @param context 上下文 30 | */ 31 | public DayView(Context context, int layoutResource) { 32 | super(context); 33 | setupLayoutResource(layoutResource); 34 | this.context = context; 35 | this.layoutResource = layoutResource; 36 | } 37 | 38 | /** 39 | * 为自定义的DayView设置资源文件 40 | * 41 | * @param layoutResource 资源文件 42 | * @return CalendarDate 修改后的日期 43 | */ 44 | private void setupLayoutResource(int layoutResource) { 45 | View inflated = LayoutInflater.from(getContext()).inflate(layoutResource, this); 46 | inflated.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 47 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 48 | inflated.layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); 49 | } 50 | 51 | @Override 52 | public void refreshContent() { 53 | measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 54 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 55 | layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); 56 | } 57 | 58 | @Override 59 | public void drawDay(Canvas canvas, Day day) { 60 | this.day = day; 61 | refreshContent(); 62 | int saveId = canvas.save(); 63 | canvas.translate(getTranslateX(canvas, day), 64 | day.getPosRow() * getMeasuredHeight()); 65 | draw(canvas); 66 | canvas.restoreToCount(saveId); 67 | } 68 | 69 | private int getTranslateX(Canvas canvas, Day day) { 70 | int dx; 71 | int canvasWidth = canvas.getWidth() / 7; 72 | int viewWidth = getMeasuredWidth(); 73 | int moveX = (canvasWidth - viewWidth) / 2; 74 | dx = day.getPosCol() * canvasWidth + moveX; 75 | return dx; 76 | } 77 | 78 | @Override 79 | protected void onDetachedFromWindow() { 80 | super.onDetachedFromWindow(); 81 | Utils.cleanMarkData(); 82 | } 83 | } -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/view/MonthPager.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.view; 2 | 3 | import android.content.Context; 4 | import android.support.design.widget.CoordinatorLayout; 5 | import android.support.v4.view.ViewPager; 6 | import android.util.AttributeSet; 7 | import android.util.Log; 8 | import android.view.MotionEvent; 9 | 10 | import com.ldf.calendar.behavior.MonthPagerBehavior; 11 | import com.ldf.calendar.component.CalendarViewAdapter; 12 | 13 | @CoordinatorLayout.DefaultBehavior(MonthPagerBehavior.class) 14 | public class MonthPager extends ViewPager { 15 | public static int CURRENT_DAY_INDEX = 1000; 16 | 17 | private int currentPosition = CURRENT_DAY_INDEX; 18 | private int cellHeight; 19 | private int viewHeight; 20 | private int rowIndex = 6; 21 | 22 | private OnPageChangeListener monthPageChangeListener; 23 | private boolean pageChangeByGesture = false; 24 | private boolean hasPageChangeListener = false; 25 | private boolean scrollable = true; 26 | private int pageScrollState = ViewPager.SCROLL_STATE_IDLE; 27 | 28 | public MonthPager(Context context) { 29 | this(context, null); 30 | } 31 | 32 | public MonthPager(Context context, AttributeSet attrs) { 33 | super(context, attrs); 34 | init(); 35 | } 36 | 37 | private void init() { 38 | ViewPager.OnPageChangeListener viewPageChangeListener = new ViewPager.OnPageChangeListener() { 39 | @Override 40 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 41 | if (monthPageChangeListener != null) { 42 | monthPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels); 43 | } 44 | } 45 | 46 | @Override 47 | public void onPageSelected(int position) { 48 | currentPosition = position; 49 | if (pageChangeByGesture) { 50 | if (monthPageChangeListener != null) { 51 | monthPageChangeListener.onPageSelected(position); 52 | } 53 | pageChangeByGesture = false; 54 | } 55 | } 56 | 57 | @Override 58 | public void onPageScrollStateChanged(int state) { 59 | pageScrollState = state; 60 | if (monthPageChangeListener != null) { 61 | monthPageChangeListener.onPageScrollStateChanged(state); 62 | } 63 | pageChangeByGesture = true; 64 | } 65 | }; 66 | addOnPageChangeListener(viewPageChangeListener); 67 | hasPageChangeListener = true; 68 | } 69 | 70 | @Override 71 | public void addOnPageChangeListener(ViewPager.OnPageChangeListener listener) { 72 | if (hasPageChangeListener) { 73 | Log.e("ldf", "MonthPager Just Can Use Own OnPageChangeListener"); 74 | } else { 75 | super.addOnPageChangeListener(listener); 76 | } 77 | } 78 | 79 | public void addOnPageChangeListener(OnPageChangeListener listener) { 80 | this.monthPageChangeListener = listener; 81 | Log.e("ldf", "MonthPager Just Can Use Own OnPageChangeListener"); 82 | } 83 | 84 | public void setScrollable(boolean scrollable) { 85 | this.scrollable = scrollable; 86 | } 87 | 88 | @Override 89 | public boolean onTouchEvent(MotionEvent me) { 90 | if (!scrollable) 91 | return false; 92 | else 93 | return super.onTouchEvent(me); 94 | } 95 | 96 | @Override 97 | public boolean onInterceptTouchEvent(MotionEvent me) { 98 | if (!scrollable) 99 | return false; 100 | else 101 | return super.onInterceptTouchEvent(me); 102 | } 103 | 104 | public void selectOtherMonth(int offset) { 105 | setCurrentItem(currentPosition + offset); 106 | CalendarViewAdapter calendarViewAdapter = (CalendarViewAdapter) getAdapter(); 107 | calendarViewAdapter.notifyDataChanged(CalendarViewAdapter.loadSelectedDate()); 108 | } 109 | 110 | public int getPageScrollState() { 111 | return pageScrollState; 112 | } 113 | 114 | public interface OnPageChangeListener { 115 | void onPageScrolled(int position, float positionOffset, int positionOffsetPixels); 116 | 117 | void onPageSelected(int position); 118 | 119 | void onPageScrollStateChanged(int state); 120 | } 121 | 122 | public int getTopMovableDistance() { 123 | CalendarViewAdapter calendarViewAdapter = (CalendarViewAdapter) getAdapter(); 124 | if(calendarViewAdapter == null) { 125 | return cellHeight; 126 | } 127 | rowIndex = calendarViewAdapter.getPagers().get(currentPosition % 3).getSelectedRowIndex(); 128 | return cellHeight * rowIndex; 129 | } 130 | 131 | public int getCellHeight() { 132 | return cellHeight; 133 | } 134 | 135 | public void setViewHeight(int viewHeight) { 136 | cellHeight = viewHeight / 6; 137 | this.viewHeight = viewHeight; 138 | } 139 | 140 | public int getViewHeight() { 141 | return viewHeight; 142 | } 143 | 144 | public int getCurrentPosition() { 145 | return currentPosition; 146 | } 147 | 148 | public void setCurrentPosition(int currentPosition) { 149 | this.currentPosition = currentPosition; 150 | } 151 | 152 | public int getRowIndex() { 153 | CalendarViewAdapter calendarViewAdapter = (CalendarViewAdapter) getAdapter(); 154 | rowIndex = calendarViewAdapter.getPagers().get(currentPosition % 3).getSelectedRowIndex(); 155 | Log.e("ldf", "getRowIndex = " + rowIndex); 156 | return rowIndex; 157 | } 158 | 159 | public void setRowIndex(int rowIndex) { 160 | this.rowIndex = rowIndex; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /calendar/src/main/java/com/ldf/calendar/view/Week.java: -------------------------------------------------------------------------------- 1 | package com.ldf.calendar.view; 2 | 3 | import android.graphics.Canvas; 4 | 5 | import com.ldf.calendar.Const; 6 | import com.ldf.calendar.interf.IDayRenderer; 7 | 8 | /** 9 | * Created by ldf on 17/6/27. 10 | */ 11 | 12 | public class Week { 13 | public int row; 14 | public Day[] days = new Day[Const.TOTAL_COL]; 15 | 16 | public Week(int row) { 17 | this.row = row; 18 | } 19 | 20 | public int getRow() { 21 | return row; 22 | } 23 | 24 | public void setRow(int row) { 25 | this.row = row; 26 | } 27 | 28 | public Day[] getDays() { 29 | return days; 30 | } 31 | 32 | public void setDays(Day[] days) { 33 | this.days = days; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /calendar/src/readme: -------------------------------------------------------------------------------- 1 | 1. 左右滑动的view左边有时候也会显示今天的view,划过去之后才会变 - fixed 2 | 2. 月周切换问题 3 | 3. 收缩后的周日历滑动问题 4 | 4. 下拉时下面的view的滑动问题,能否做成先下拉下面的view, 拉不动时才处理上面的日历的view --fixed 5 | 5. 回到今天的问题(跳转到指定日期) - fixed 6 | 6. 滑动的下一页的选中问题 -- fixed 7 | 7. 自定义日历View的Wrap_content模式 --not fixed full 8 | 8. 日历和RecycleView的依赖关系是什么时候建立的 9 | 9. 不同分辨率的显示问题 -------------------------------------------------------------------------------- /calendar/src/test/java/com/ldf/mi/calendar/micalendar/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.ldf.mi.calendar.micalendar; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /example/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion '26.0.2' 6 | defaultConfig { 7 | applicationId "com.hqyxjy.ldf.supercalendar" 8 | minSdkVersion 19 9 | targetSdkVersion 26 10 | versionCode 2 11 | versionName "2.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | 20 | lintOptions { 21 | abortOnError false 22 | } 23 | dexOptions { 24 | incremental true 25 | } 26 | compileOptions { 27 | sourceCompatibility JavaVersion.VERSION_1_8 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | } 30 | } 31 | 32 | dependencies { 33 | compile fileTree(include: ['*.jar'], dir: 'libs') 34 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 35 | exclude group: 'com.android.support', module: 'support-annotations' 36 | }) 37 | compile 'com.android.support:appcompat-v7:26.1.0' 38 | testCompile 'junit:junit:4.12' 39 | compile project(path: ':calendar') 40 | } 41 | -------------------------------------------------------------------------------- /example/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImVeryGood/SuperCalendar-master/1f9bbb256da25587f0cf2a558f0e438e0fb4744a/example/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Apr 02 11:29:06 GMT+08:00 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip 7 | -------------------------------------------------------------------------------- /example/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /example/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /example/local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Tue Apr 02 11:29:01 GMT+08:00 2019 8 | sdk.dir=C\:\\Users\\Administrator\\AppData\\Local\\Android\\Sdk 9 | -------------------------------------------------------------------------------- /example/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/ldf/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /example/src/androidTest/java/com/hqyxjy/ldf/supercalendar/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.hqyxjy.ldf.supercalendar; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.hqyxjy.ldf.fuckcalendar", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/src/main/java/com/hqyxjy/ldf/supercalendar/CustomDayView.java: -------------------------------------------------------------------------------- 1 | package com.hqyxjy.ldf.supercalendar; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.util.Log; 7 | import android.view.View; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import com.ldf.calendar.Utils; 12 | import com.ldf.calendar.component.State; 13 | import com.ldf.calendar.interf.IDayRenderer; 14 | import com.ldf.calendar.model.CalendarDate; 15 | import com.ldf.calendar.view.DayView; 16 | 17 | import java.util.HashMap; 18 | 19 | /** 20 | * Created by ldf on 17/6/26. 21 | */ 22 | 23 | @SuppressLint("ViewConstructor") 24 | public class CustomDayView extends DayView { 25 | 26 | private TextView dateTv; 27 | private ImageView marker; 28 | private View selectedBackground; 29 | private View todayBackground; 30 | private final CalendarDate today = new CalendarDate(); 31 | 32 | /** 33 | * 构造器 34 | * 35 | * @param context 上下文 36 | * @param layoutResource 自定义DayView的layout资源 37 | */ 38 | public CustomDayView(Context context, int layoutResource) { 39 | super(context, layoutResource); 40 | dateTv = (TextView) findViewById(R.id.date); 41 | marker = (ImageView) findViewById(R.id.maker); 42 | selectedBackground = findViewById(R.id.selected_background); 43 | todayBackground = findViewById(R.id.today_background); 44 | } 45 | 46 | @Override 47 | public void refreshContent() { 48 | renderToday(day.getDate(), day.getState()); 49 | renderMarker(day.getDate(), day.getState()); 50 | renderSelect(day.getDate(),day.getState()); 51 | super.refreshContent(); 52 | } 53 | 54 | private void renderMarker(CalendarDate date, State state) { 55 | //如果是当月,并且包含此Mark 56 | if (Utils.loadMarkData().containsKey(date.toString())&&state==State.CURRENT_MONTH) { 57 | marker.setVisibility(VISIBLE); 58 | if (Utils.loadMarkData().get(date.toString()).equals("0")) { 59 | marker.setEnabled(true); 60 | } else { 61 | marker.setEnabled(false); 62 | } 63 | 64 | } else { 65 | marker.setVisibility(GONE); 66 | } 67 | } 68 | private void renderSelect(CalendarDate date,State state) { 69 | if (state == State.SELECT) { 70 | // 本月 71 | selectedBackground.setVisibility(VISIBLE); 72 | dateTv.setTextColor(Color.WHITE); 73 | // 如果是是Mark就显示 74 | if (Utils.loadMarkData().containsKey(date.toString())){ 75 | marker.setVisibility(VISIBLE); 76 | } 77 | 78 | } else if (state == State.NEXT_MONTH || state == State.PAST_MONTH) { 79 | // 不是本月 80 | selectedBackground.setVisibility(GONE); 81 | // dateTv.setTextColor(Color.parseColor("#5b5b5b")); 82 | dateTv.setText(""); 83 | } else { 84 | selectedBackground.setVisibility(GONE); 85 | dateTv.setTextColor(Color.parseColor("#111111")); 86 | } 87 | } 88 | 89 | private void renderToday(CalendarDate date, State state) { 90 | if (date != null) { 91 | // 只渲染本月的 92 | if (date.equals(today) && state == State.SELECT) { 93 | dateTv.setText(date.day + ""); 94 | todayBackground.setVisibility(VISIBLE); 95 | } else { 96 | dateTv.setText(date.day + ""); 97 | todayBackground.setVisibility(GONE); 98 | } 99 | } 100 | 101 | } 102 | 103 | @Override 104 | public IDayRenderer copy() { 105 | return new CustomDayView(context, layoutResource); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /example/src/main/java/com/hqyxjy/ldf/supercalendar/ExampleAdapter.java: -------------------------------------------------------------------------------- 1 | package com.hqyxjy.ldf.supercalendar; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.util.Log; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.TextView; 10 | 11 | /** 12 | * Created by ldf on 17/6/14. 13 | */ 14 | 15 | public class ExampleAdapter extends RecyclerView.Adapter { 16 | 17 | private final LayoutInflater layoutInflater; 18 | private final Context context; 19 | private String[] titles; 20 | 21 | public ExampleAdapter(Context context) { 22 | titles = context.getResources().getStringArray(R.array.titles); 23 | this.context = context; 24 | layoutInflater = LayoutInflater.from(context); 25 | } 26 | 27 | @Override 28 | public ExampleAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 29 | return new ViewHolder(layoutInflater.inflate(R.layout.item, parent, false)); 30 | } 31 | 32 | @Override 33 | public void onBindViewHolder(ViewHolder holder, int position) { 34 | holder.textView.setText(titles[position]); 35 | } 36 | 37 | @Override 38 | public int getItemCount() { 39 | return titles == null ? 0 : titles.length; 40 | } 41 | 42 | public static class ViewHolder extends RecyclerView.ViewHolder { 43 | TextView textView; 44 | 45 | ViewHolder(View view) { 46 | super(view); 47 | textView = (TextView) view.findViewById(R.id.text_view); 48 | view.setOnClickListener(new View.OnClickListener() { 49 | @Override 50 | public void onClick(View v) { 51 | Log.d("ViewHolder", "onClick--> position = " + getPosition()); 52 | } 53 | }); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /example/src/main/java/com/hqyxjy/ldf/supercalendar/SyllabusActivity.java: -------------------------------------------------------------------------------- 1 | package com.hqyxjy.ldf.supercalendar; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.design.widget.CoordinatorLayout; 8 | import android.support.v4.view.ViewPager; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.support.v7.widget.LinearLayoutManager; 11 | import android.support.v7.widget.RecyclerView; 12 | import android.util.Log; 13 | import android.view.View; 14 | import android.widget.Button; 15 | import android.widget.TextView; 16 | import android.widget.Toast; 17 | 18 | import com.ldf.calendar.Utils; 19 | import com.ldf.calendar.component.CalendarAttr; 20 | import com.ldf.calendar.view.MonthPager; 21 | import com.ldf.calendar.interf.OnSelectDateListener; 22 | import com.ldf.calendar.component.CalendarViewAdapter; 23 | import com.ldf.calendar.model.CalendarDate; 24 | import com.ldf.calendar.view.Calendar; 25 | 26 | import java.text.SimpleDateFormat; 27 | import java.util.ArrayList; 28 | import java.util.Date; 29 | import java.util.HashMap; 30 | 31 | /** 32 | * Created by ldf on 16/11/4. 33 | */ 34 | 35 | @SuppressLint("SetTextI18n") 36 | public class SyllabusActivity extends AppCompatActivity implements View.OnClickListener { 37 | TextView tvYear; 38 | TextView tvMonth; 39 | TextView backToday; 40 | CoordinatorLayout content; 41 | MonthPager monthPager; 42 | RecyclerView rvToDoList; 43 | TextView scrollSwitch; 44 | TextView themeSwitch; 45 | TextView nextMonthBtn; 46 | TextView lastMonthBtn; 47 | Button mark; 48 | CustomDayView customDayView; 49 | private ArrayList currentCalendars = new ArrayList<>(); 50 | private CalendarViewAdapter calendarAdapter; 51 | private OnSelectDateListener onSelectDateListener; 52 | private int mCurrentPage = MonthPager.CURRENT_DAY_INDEX; 53 | private Context context; 54 | private CalendarDate currentDate; 55 | private boolean initiated = false; 56 | 57 | @Override 58 | protected void onCreate(@Nullable Bundle savedInstanceState) { 59 | super.onCreate(savedInstanceState); 60 | setContentView(R.layout.activity_syllabus); 61 | context = this; 62 | content = (CoordinatorLayout) findViewById(R.id.content); 63 | monthPager = (MonthPager) findViewById(R.id.calendar_view); 64 | //此处强行setViewHeight,毕竟你知道你的日历牌的高度 65 | monthPager.setViewHeight(Utils.dpi2px(context, 270)); 66 | tvYear = (TextView) findViewById(R.id.show_year_view); 67 | tvMonth = (TextView) findViewById(R.id.show_month_view); 68 | backToday = (TextView) findViewById(R.id.back_today_button); 69 | scrollSwitch = (TextView) findViewById(R.id.scroll_switch); 70 | themeSwitch = (TextView) findViewById(R.id.theme_switch); 71 | nextMonthBtn = (TextView) findViewById(R.id.next_month); 72 | lastMonthBtn = (TextView) findViewById(R.id.last_month); 73 | rvToDoList = (RecyclerView) findViewById(R.id.list); 74 | mark=findViewById(R.id.marks); 75 | mark.setOnClickListener(this); 76 | rvToDoList.setHasFixedSize(true); 77 | //这里用线性显示 类似于listview 78 | rvToDoList.setLayoutManager(new LinearLayoutManager(this)); 79 | rvToDoList.setAdapter(new ExampleAdapter(this)); 80 | initCurrentDate(); 81 | initCalendarView(); 82 | initToolbarClickListener(); 83 | Log.e("ldf","OnCreated"); 84 | } 85 | 86 | /** 87 | * onWindowFocusChanged回调时,将当前月的种子日期修改为今天 88 | * 89 | * @return void 90 | */ 91 | @Override 92 | public void onWindowFocusChanged(boolean hasFocus) { 93 | super.onWindowFocusChanged(hasFocus); 94 | if (hasFocus && !initiated) { 95 | refreshMonthPager(); 96 | initiated = true; 97 | } 98 | } 99 | 100 | /* 101 | * 如果你想以周模式启动你的日历,请在onResume是调用 102 | * Utils.scrollTo(content, rvToDoList, monthPager.getCellHeight(), 200); 103 | * calendarAdapter.switchToWeek(monthPager.getRowIndex()); 104 | * */ 105 | @Override 106 | protected void onResume() { 107 | super.onResume(); 108 | } 109 | 110 | /** 111 | * 初始化对应功能的listener 112 | * 113 | * @return void 114 | */ 115 | private void initToolbarClickListener() { 116 | backToday.setOnClickListener(new View.OnClickListener() { 117 | @Override 118 | public void onClick(View view) { 119 | onClickBackToDayBtn(); 120 | } 121 | }); 122 | scrollSwitch.setOnClickListener(new View.OnClickListener() { 123 | @Override 124 | public void onClick(View view) { 125 | if (calendarAdapter.getCalendarType() == CalendarAttr.CalendarType.WEEK) { 126 | Utils.scrollTo(content, rvToDoList, monthPager.getViewHeight(), 200); 127 | calendarAdapter.switchToMonth(); 128 | } else { 129 | Utils.scrollTo(content, rvToDoList, monthPager.getCellHeight(), 200); 130 | calendarAdapter.switchToWeek(monthPager.getRowIndex()); 131 | } 132 | } 133 | }); 134 | themeSwitch.setOnClickListener(new View.OnClickListener() { 135 | @Override 136 | public void onClick(View view) { 137 | refreshSelectBackground(); 138 | } 139 | }); 140 | nextMonthBtn.setOnClickListener(new View.OnClickListener() { 141 | @Override 142 | public void onClick(View view) { 143 | monthPager.setCurrentItem(monthPager.getCurrentPosition() + 1); 144 | } 145 | }); 146 | lastMonthBtn.setOnClickListener(new View.OnClickListener() { 147 | @Override 148 | public void onClick(View view) { 149 | monthPager.setCurrentItem(monthPager.getCurrentPosition() - 1); 150 | } 151 | }); 152 | } 153 | 154 | /** 155 | * 初始化currentDate 156 | * 157 | * @return void 158 | */ 159 | private void initCurrentDate() { 160 | currentDate = new CalendarDate(); 161 | tvYear.setText(currentDate.getYear() + "年"); 162 | tvMonth.setText(currentDate.getMonth() + ""); 163 | } 164 | 165 | /** 166 | * 初始化CustomDayView,并作为CalendarViewAdapter的参数传入 167 | */ 168 | private void initCalendarView() { 169 | initListener(); 170 | CustomDayView customDayView = new CustomDayView(context, R.layout.custom_day); 171 | calendarAdapter = new CalendarViewAdapter( 172 | context, 173 | onSelectDateListener, 174 | CalendarAttr.WeekArrayType.Monday, 175 | customDayView); 176 | calendarAdapter.setOnCalendarTypeChangedListener(new CalendarViewAdapter.OnCalendarTypeChanged() { 177 | @Override 178 | public void onCalendarTypeChanged(CalendarAttr.CalendarType type) { 179 | rvToDoList.scrollToPosition(0); 180 | } 181 | }); 182 | initMarkData(); 183 | initMonthPager(); 184 | } 185 | public static String getDate(Long time){ 186 | String ret = null; 187 | SimpleDateFormat format = new SimpleDateFormat("y-M-d"); 188 | ret = format.format(new Date(time)); 189 | return ret; 190 | } 191 | /** 192 | * 初始化标记数据,HashMap的形式,可自定义 193 | * 如果存在异步的话,在使用setMarkData之后调用 calendarAdapter.notifyDataChanged(); 194 | */ 195 | private void initMarkData() { 196 | HashMap markData = new HashMap<>(); 197 | markData.put("2019-4-2", "0"); 198 | markData.put("2019-4-1", "0"); 199 | markData.put("2019-4-20", "0"); 200 | markData.put("2019-4-30", "0"); 201 | markData.put("2019-5-25", "0"); 202 | markData.put("2019-5-28", "0"); 203 | markData.put("2019-3-25", "0"); 204 | markData.put("2019-3-24", "0"); 205 | calendarAdapter.setMarkData(markData); 206 | calendarAdapter.notifyDataChanged(); 207 | } 208 | 209 | private void initListener() { 210 | onSelectDateListener = new OnSelectDateListener() { 211 | @Override 212 | public void onSelectDate(CalendarDate date) { 213 | refreshClickDate(date); 214 | } 215 | 216 | @Override 217 | public void onSelectOtherMonth(int offset) { 218 | //偏移量 -1表示刷新成上一个月数据 , 1表示刷新成下一个月数据 219 | monthPager.selectOtherMonth(offset); 220 | } 221 | }; 222 | } 223 | 224 | 225 | private void refreshClickDate(CalendarDate date) { 226 | currentDate = date; 227 | tvYear.setText(date.getYear() + "年"); 228 | tvMonth.setText(date.getMonth() + ""); 229 | } 230 | 231 | /** 232 | * 初始化monthPager,MonthPager继承自ViewPager 233 | * 234 | * @return void 235 | */ 236 | private void initMonthPager() { 237 | monthPager.setAdapter(calendarAdapter); 238 | monthPager.setCurrentItem(MonthPager.CURRENT_DAY_INDEX); 239 | monthPager.setPageTransformer(false, new ViewPager.PageTransformer() { 240 | @Override 241 | public void transformPage(View page, float position) { 242 | position = (float) Math.sqrt(1 - Math.abs(position)); 243 | page.setAlpha(position); 244 | } 245 | }); 246 | monthPager.addOnPageChangeListener(new MonthPager.OnPageChangeListener() { 247 | @Override 248 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 249 | } 250 | 251 | @Override 252 | public void onPageSelected(int position) { 253 | Log.d("SSSSSSs", "onPageSelected: "+position); 254 | mCurrentPage = position; 255 | currentCalendars = calendarAdapter.getPagers(); 256 | if (currentCalendars.get(position % currentCalendars.size()) != null) { 257 | CalendarDate date = currentCalendars.get(position % currentCalendars.size()).getSeedDate(); 258 | currentDate = date; 259 | tvYear.setText(date.getYear() + "年"); 260 | tvMonth.setText(date.getMonth() + ""); 261 | } 262 | } 263 | 264 | @Override 265 | public void onPageScrollStateChanged(int state) { 266 | } 267 | }); 268 | } 269 | 270 | public void onClickBackToDayBtn() { 271 | refreshMonthPager(); 272 | } 273 | 274 | private void refreshMonthPager() { 275 | CalendarDate today = new CalendarDate(); 276 | calendarAdapter.notifyDataChanged(today); 277 | tvYear.setText(today.getYear() + "年"); 278 | tvMonth.setText(today.getMonth() + ""); 279 | } 280 | 281 | private void refreshSelectBackground() { 282 | ThemeDayView themeDayView = new ThemeDayView(context, R.layout.custom_day_focus); 283 | calendarAdapter.setCustomDayRenderer(themeDayView); 284 | calendarAdapter.notifyDataSetChanged(); 285 | calendarAdapter.notifyDataChanged(new CalendarDate()); 286 | } 287 | 288 | @Override 289 | public void onClick(View v) { 290 | initMarkData(); 291 | } 292 | } 293 | 294 | -------------------------------------------------------------------------------- /example/src/main/java/com/hqyxjy/ldf/supercalendar/ThemeDayView.java: -------------------------------------------------------------------------------- 1 | package com.hqyxjy.ldf.supercalendar; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import android.widget.ImageView; 6 | import android.widget.TextView; 7 | 8 | import com.ldf.calendar.component.State; 9 | import com.ldf.calendar.interf.IDayRenderer; 10 | import com.ldf.calendar.model.CalendarDate; 11 | import com.ldf.calendar.view.DayView; 12 | 13 | /** 14 | * Created by ldf on 17/6/27. 15 | */ 16 | 17 | public class ThemeDayView extends DayView { 18 | private TextView dateTv; 19 | private ImageView marker; 20 | private View selectedBackground; 21 | private View todayBackground; 22 | private final CalendarDate today = new CalendarDate(); 23 | 24 | /** 25 | * Constructor. Sets up the MarkerView with a custom layout resource. 26 | * 27 | * @param context 28 | * @param layoutResource the layout resource to use for the MarkerView 29 | */ 30 | public ThemeDayView(Context context, int layoutResource) { 31 | super(context, layoutResource); 32 | dateTv = (TextView) findViewById(R.id.date); 33 | marker = (ImageView) findViewById(R.id.maker); 34 | selectedBackground = findViewById(R.id.selected_background); 35 | todayBackground = findViewById(R.id.today_background); 36 | } 37 | 38 | @Override 39 | public void refreshContent() { 40 | CalendarDate date = day.getDate(); 41 | State state = day.getState(); 42 | if (date != null) { 43 | if (state!=null){ 44 | if (date.equals(today)) { 45 | dateTv.setText("今"); 46 | todayBackground.setVisibility(VISIBLE); 47 | } else { 48 | dateTv.setText(date.day + ""); 49 | todayBackground.setVisibility(GONE); 50 | } 51 | }else { 52 | dateTv.setText(""); 53 | todayBackground.setVisibility(GONE); 54 | } 55 | } 56 | if (state == State.SELECT) { 57 | selectedBackground.setVisibility(VISIBLE); 58 | } else { 59 | selectedBackground.setVisibility(GONE); 60 | } 61 | super.refreshContent(); 62 | } 63 | 64 | @Override 65 | public IDayRenderer copy() { 66 | return new ThemeDayView(context, layoutResource); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/src/main/res/drawable-xxhdpi/select_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImVeryGood/SuperCalendar-master/1f9bbb256da25587f0cf2a558f0e438e0fb4744a/example/src/main/res/drawable-xxhdpi/select_background.png -------------------------------------------------------------------------------- /example/src/main/res/drawable/background_white_3dp_corner.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/src/main/res/drawable/button_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/src/main/res/drawable/selected_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /example/src/main/res/drawable/selected_background_custom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /example/src/main/res/drawable/today_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 12 | -------------------------------------------------------------------------------- /example/src/main/res/drawable/view_syllabus_active_mark_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /example/src/main/res/drawable/view_syllabus_mark_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | -------------------------------------------------------------------------------- /example/src/main/res/drawable/view_syllabus_unactive_mark_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /example/src/main/res/layout/activity_syllabus.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 |