diff --git a/CalendarView/.gitignore b/CalendarView/.gitignore
new file mode 100644
index 00000000..f03807b4
--- /dev/null
+++ b/CalendarView/.gitignore
@@ -0,0 +1,49 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+# Built application files
+*.apk
+*.ap_
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+out/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+# Android Studio Navigation editor temp files
+.navigation/
+
+# Android Studio captures folder
+captures/
+
+# Intellij
+*.iml
+.idea/workspace.xml
+
+# Keystore files
+*.jks
\ No newline at end of file
diff --git a/CalendarView/.idea/caches/build_file_checksums.ser b/CalendarView/.idea/caches/build_file_checksums.ser
new file mode 100644
index 00000000..0c6a56b7
Binary files /dev/null and b/CalendarView/.idea/caches/build_file_checksums.ser differ
diff --git a/CalendarView/.idea/caches/gradle_models.ser b/CalendarView/.idea/caches/gradle_models.ser
new file mode 100644
index 00000000..835428cc
Binary files /dev/null and b/CalendarView/.idea/caches/gradle_models.ser differ
diff --git a/CalendarView/.idea/codeStyles/Project.xml b/CalendarView/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..0c51a83d
--- /dev/null
+++ b/CalendarView/.idea/codeStyles/Project.xml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/.idea/encodings.xml b/CalendarView/.idea/encodings.xml
new file mode 100644
index 00000000..15a15b21
--- /dev/null
+++ b/CalendarView/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/.idea/gradle.xml b/CalendarView/.idea/gradle.xml
new file mode 100644
index 00000000..d093aaad
--- /dev/null
+++ b/CalendarView/.idea/gradle.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/.idea/misc.xml b/CalendarView/.idea/misc.xml
new file mode 100644
index 00000000..b5c78bcb
--- /dev/null
+++ b/CalendarView/.idea/misc.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/.idea/modules.xml b/CalendarView/.idea/modules.xml
new file mode 100644
index 00000000..ca9de94e
--- /dev/null
+++ b/CalendarView/.idea/modules.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/.idea/runConfigurations.xml b/CalendarView/.idea/runConfigurations.xml
new file mode 100644
index 00000000..7f68460d
--- /dev/null
+++ b/CalendarView/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/.idea/vcs.xml b/CalendarView/.idea/vcs.xml
new file mode 100644
index 00000000..35eb1ddf
--- /dev/null
+++ b/CalendarView/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/LICENSE b/CalendarView/LICENSE
new file mode 100644
index 00000000..8dada3ed
--- /dev/null
+++ b/CalendarView/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/CalendarView/QUESTION.md b/CalendarView/QUESTION.md
new file mode 100644
index 00000000..7250422c
--- /dev/null
+++ b/CalendarView/QUESTION.md
@@ -0,0 +1,403 @@
+# CalendarView doc
+
+### **CalendarView** Characteristics
+
+- Based on Canvas, Extreme speed
+- Hot plug UI customization
+- Support month view、 week view、year view、lunar calendar and so on
+- Support static or dynamic setting of week start
+- Support for static or dynamic setting of calendar item height
+- Support date interceptor and range
+- Support multi-touch, smooth scrolling calendar
+- Support like NestedScrolling
+- All UI depends on your drawing
+
+
+### 1、Create a class extends the **MonthView** or **RangeMonthView**、**MultiMonthView**.
+### If you need WeekView, also create a class extends the **WeekView** or **RangeWeekView**、**MultiWeekView**.
+
+- Implement these three methods :**onDrawSelected** **onDrawScheme** **onDrawText**, like this
+
+```java
+/**
+ * CustomMonthView with canvas
+ */
+public class CustomMonthView extends MonthView {
+
+ /**
+ * draw select calendar
+ *
+ * @param canvas canvas
+ * @param calendar select calendar
+ * @param x calendar item x start point
+ * @param y calendar item y start point
+ * @param hasScheme is calendar has scheme?
+ * @return if return true will call onDrawScheme again
+ */
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+ canvas.drawRect(x , y , x + mItemWidth , y + mItemHeight, mSelectedPaint);
+ return true;
+ }
+
+ /**
+ * draw scheme if calendar has scheme
+ *
+ * @param canvas canvas
+ * @param calendar calendar has scheme
+ * @param x calendar item x start point
+ * @param y calendar item y start point
+ */
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+ canvas.drawCircle(x + mItemWidth / 2, y + mItemHeight - 3 * mPadding, mPointRadius, mPointPaint);
+ }
+
+ /**
+ * draw text
+ *
+ * @param canvas canvas
+ * @param calendar calendar
+ * @param x calendar item x start point
+ * @param y calendar item y start point
+ * @param hasScheme is calendar has scheme?
+ * @param isSelected is calendar selected?
+ */
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine + y;
+ int cx = x + mItemWidth / 2;
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+ }
+}
+```
+
+- Add the class path in xml
+
+```xml
+
+
+
+
+
+
+```
+
+- You can also use the hot plug feature, Quickly switch UI styles
+
+```java
+
+mCalendarView.setWeekView(MeiZuWeekView.class);
+
+mCalendarView.setMonthView(MeiZuMonthView.class);
+
+```
+
+- Calendar has multiple view modes to choose depending on your needs
+
+```xml
+
+MonthView、WeekView,and select_mode="default_mode"
+Same as mobile phone calendar,date interceptor is not supported
+
+```
+
+```xml
+
+MonthView、WeekView,and select_mode="single_mode"
+Single mode, support date interceptor
+
+```
+
+```xml
+
+RangeMonthView、RangeWeekView,need set select_mode="range_mode"
+support date intercept
+
+```
+
+```xml
+MultiMonthView、MultiWeekView,need set select_mode="multi_mode"
+support date intercept
+
+```
+
+- If you need WeekView,Add parent layout **CalendarLayout** in **CalendarView**
+
+```xml
+
+
+
+
+
+
+```
+
+- You can use the listener to monitor the WeekView and MonthView switch
+
+```java
+
+public void setOnViewChangeListener(OnViewChangeListener listener);
+
+```
+
+- All off **CalendarLayout** attr,Smooth scrolling calendar. If used **CalendarLayout**,you need set **calendar_content_view_id**
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+- If you need a full style **CalendarView**, make app:calendar_match_parent="true", No need to use CalendarLayout
+
+
+
+
+- The **CalendarView** also provides a YearView if you need it
+
+
+
+
+- If you need to quick jump date
+```
+
+CalendarView.scrollToCalendar();
+
+CalendarView.scrollToNext();
+
+CalendarView.scrollToPre();
+
+CalendarView.scrollToXXX();
+
+```
+
+- Support static or dynamic setting of week start
+```
+
+app:week_start_with="mon、sun、sat"
+
+mCalendarView.setWeekStarWithSun();
+
+mCalendarView.setWeekStarWithMon();
+
+mCalendarView.setWeekStarWithSat();
+
+```
+
+- Set calendar range
+
+```
+
+
+
+
+
+
+
+
+CalendarView.setRange(int minYear, int minYearMonth, int minYearDay,
+ int maxYear, int maxYearMonth, int maxYearDay)
+
+```
+
+- Add OnCalendarInterceptListener to intercept calendar
+
+```java
+mCalendarView.setOnCalendarInterceptListener(new CalendarView.OnCalendarInterceptListener() {
+ @Override
+ public boolean onCalendarIntercept(Calendar calendar) {
+
+ return calendar.isWeekend();
+ }
+
+ @Override
+ public void onCalendarInterceptClick(Calendar calendar, boolean isClick) {
+ //todo 点击拦截的日期回调
+ }
+});
+```
+
+- You can get interception and range results in month and week view
+
+```java
+
+boolean isInRange = isInRange(calendar);
+
+boolean isEnable = !onCalendarIntercept(calendar);
+
+```
+
+
+
+
+- The **CalendarView** provides scheme setting, **setSchemeDate(Map mSchemeDates)**
+
+- class **Calendar**
+
+```java
+boolean isWeekend();
+
+int getWeek();
+
+String getSolarTerm();
+
+String getGregorianFestival();
+
+String getTraditionFestival();
+
+boolean isLeapYear();
+
+int getLeapMonth();
+
+boolean isSameMonth(Calendar calendar);
+
+int compareTo(Calendar calendar);
+
+long getTimeInMillis();
+
+int differ(Calendar calendar);
+```
+
+### All of **CalendarView** attr
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### Better use with Demo when using.The calendar is very simple to customize
\ No newline at end of file
diff --git a/CalendarView/QUESTION_ZH.md b/CalendarView/QUESTION_ZH.md
new file mode 100644
index 00000000..afbff840
--- /dev/null
+++ b/CalendarView/QUESTION_ZH.md
@@ -0,0 +1,428 @@
+# CalendarView使用详细文档
+
+### 日历控件定制是移动开发平台上比较常见的而且比较难的需求,一般会遇到以下问题:
+- **性能差,加载速度慢**,原因是各种基于GridView或RecyclerView等ViewGroup实现的日历,控件数太多,假设一个月视图界面有42个item,每个item里面分别就有2个子TextView:天数、农历数和本身3个控件,这样一个月视图就有42 \* 3+1(RecyclerView or GridView),清楚ViewPager特性的开发者就会明白,一般ViewPager持有3个item,那么一个日历控件持有的View控件数的数量将达到 1(ViewPager)+ 3(RecyclerView or GridView) + 3 \* 42 \* 3 = **382**,如果用1个View来代替RecyclerView等,用Canvas来代替各种TextView,那View的数量瞬间将下降360+,内存和性能优势将相当明显了
+- **难定制** 一般日历框架发布的同时也将UI风格确定下来了,假如人人都使用这个日历框架,那么将会千篇一律,难以突出自己的风格,要么就得改源码,成本太大,不太实际
+- **功能性不足** 例如无法自定义周起始、无法更改选择模式、动态设置UI等等
+- **无法满足产品经理提出的变态需求** 今天产品经历说我们要这样的实现、明天跟你说这里得改、后天说我们得限制一些日期...
+
+### 但现在有了全新的 **CalendarView** 控件,它解锁了各种姿势,而且你可以任意定制,直到你满足为止...
+
+### 插拔式设计
+
+插拔式设计:好比插座一样,插上灯泡就会亮,插上风扇就会转,看用户需求什么而不是看插座有什么,只要是电器即可。此框架使用插拔式,既可以在编译时指定年月日视图,如:app:month_view="xxx.xxx.MonthView.class",也可在运行时动态更换年月日视图,如:CalendarView.setMonthViewClass(MonthView.Class),从而达到UI即插即用的效果,相当于框架不提供UI实现,让UI都由客户端实现,不至于日历UI都千篇一律,只需遵守插拔式接口即可随意定制,自由化程度非常高。
+
+
+### **CalendarView** 的特性
+
+- 基于Canvas绘制,极速性能
+- 热插拔思想,任意定制周视图、月视图,即插即用!
+- 支持单选、多选、范围选择、国内手机日历默认自动选择等选择模式
+- 支持静态、动态设置周起始,一行代码搞定
+- 支持静态、动态设置日历项高度、日历填充模式
+- 支持设置任意日期范围、任意拦截日期
+- 支持多点触控、手指平滑切换过渡,拒绝界面抖动
+- 类NestedScrolling特性,嵌套滚动
+- 既然这么多支持,那一定支持英语、繁体、简体,任意定制实现
+
+### **注意:** 框架本身只是实现各自逻辑,不实现UI,UI如同一张白纸,任凭客户端自行通过Canvas绘制实现,如果不熟悉Canvas的,请自行了解各自Canvas.drawXXX方法,UI都靠Canvas实现,坐标都已经计算好了,因此怎么隐藏农历,怎么换某些日期的字,这些都不属于框架范畴,只要你想换,都能随便换。
+
+### **再次注意:** app Demo只是Demo,只是示例如何使用,与框架本身没有关联,不属于框架一部分
+
+### 接下来请看**CalendarView**操作,前方高能
+
+- 你这样继承自己的月视图和周视图,只需要依次实现绘制选中:**onDrawSelected**、绘制事务:**onDrawScheme**、绘制文本:**onDrawText** 这三个回调即可,参数和坐标都已经在回调函数上实现好,周视图也是一样的逻辑,只是不需要y参数
+
+```java
+/**
+ * 定制高仿魅族日历界面,按你的想象力绘制出各种各样的界面
+ *
+ */
+public class MeiZuMonthView extends MonthView {
+
+ /**
+ * 绘制选中的日子
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @return 返回true 则绘制onDrawScheme,因为这里背景色不是是互斥的,所以返回true
+ */
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+ //这里绘制选中的日子样式,看需求需不需要继续调用onDrawScheme
+ return true;
+ }
+
+ /**
+ * 绘制标记的事件日子
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ */
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+ //这里绘制标记的日期样式,想怎么操作就怎么操作
+ }
+
+ /**
+ * 绘制文本
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme 是否是标记的日期
+ * @param isSelected 是否选中
+ */
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ //这里绘制文本,不要再问我怎么隐藏农历了,不要再问我怎么把某个日期换成特殊字符串了,要怎么显示你就在这里怎么画,你不画就不显示,是看你想怎么显示日历的,而不是看框架
+ }
+}
+```
+
+- 当你实现好之后,直接在xml界面上添加特性,编译后可以即时预览效果:
+
+```xml
+
+
+
+
+app:month_view="com.haibin.calendarviewproject.MeiZuMonthView"
+app:week_view="com.haibin.calendarviewproject.MeiZuWeekView"
+
+```
+
+- 视图有多种模式可供选择,几乎涵盖了各种需求,看各自的需求自行继承
+
+```xml
+
+如果继承这2个,MonthView、WeekView,即select_mode="default_mode",这是默认的手机自带的日历模式,会自动选择月的第一天,不支持拦截器,
+也可以设置select_mode="single_mode",即单选模式,支持拦截器
+
+如果继承这2个,RangeMonthView、RangeWeekView,即select_mode="range_mode",这是范围选择模式,支持拦截器
+
+如果继承这2个,MultiMonthView、MultiWeekView,即select_mode="multi_mode",这是多选模式,支持拦截器
+
+```
+
+
+- 如果静态模式无法满足你的需求,你可能需要动态变换定制的视图界面,你可以使用热插拔特性,即插即用,不爽就换:
+
+```java
+
+mCalendarView.setWeekView(MeiZuWeekView.class);
+
+mCalendarView.setMonthView(MeiZuMonthView.class);
+
+```
+
+- 如果你需要可收缩的日历,你可以在 **CalendarView** 父布局添加 **CalendarLayout**,当然你不需要周视图也可以不用,例如原生日历,使用如下:
+
+```xml
+
+
+
+
+
+
+```
+
+- 使用可收缩的日历你可以使用监听器,监听视图变换
+
+```java
+
+public void setOnViewChangeListener(OnViewChangeListener listener);
+
+```
+
+- 当然 **CalendarLayout** 有很多特性可提供周月视图无缝切换,而且,平滑手势不抖动!使用 **CalendarLayout**,你需要指定 **calendar_content_view_id**,用他来平移收缩月视图,更多特性如下:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+- **CalendarView** 可以设置全屏,只需设置 app:calendar_match_parent="true"即可,全屏**CalendarView**是不需要周视图的,不必嵌套CalendarLayout
+
+
+
+
+- **CalendarView** 也提供了高效便利的年视图,可以快速切换年份、月份,十分便利
+
+
+
+
+- 但年视图也不一定就适合你的胃口,如果你希望像弹出 **DatePickerView**,通过它来跳转日期,你可以使用以下的API来让日历与其它控件联动
+```
+
+CalendarView.scrollToCalendar();
+
+CalendarView.scrollToNext();
+
+CalendarView.scrollToPre();
+
+CalendarView.scrollToXXX();
+
+```
+
+- 你也许需要像魅族日历一样,可以静态、动态更换周起始
+```
+
+app:week_start_with="mon、sun、sat"
+
+CalendarView.setWeekStarWithSun();
+
+CalendarView.setWeekStarWithMon();
+
+CalendarView.setWeekStarWithSat();
+
+```
+
+- 假如你是做酒店、旅游等应用场景的APP的,那么需要可选范围的日历,你可以这样继承,和普通视图实现完全一样
+
+```java
+public class CustomRangeMonthView extends RangeMonthView{
+
+}
+
+public class CustomRangeWeekView extends RangeWeekView{
+
+}
+
+```
+
+- 然后你需要设置选择模式为范围模式:**select\_mode="range\_mode"**
+
+
+- 酒店式日历场景当然是不能从昨天开始订房的,也不能无限期订房,所以你需要静态或动态设置日历范围、精确到具体某一天!!!
+
+```
+
+
+
+
+
+
+
+
+CalendarView.setRange(int minYear, int minYearMonth, int minYearDay,
+ int maxYear, int maxYearMonth, int maxYearDay)
+
+```
+
+- 当然还有更特殊的日子也是不能选择的,例如:某月某号起这N天时间内因为超强台风来袭,酒店需停止营业N天,这段期间不可订房,这时日期拦截器就排上用场了
+```java
+//设置日期拦截事件
+mCalendarView.setOnCalendarInterceptListener(new CalendarView.OnCalendarInterceptListener() {
+ @Override
+ public boolean onCalendarIntercept(Calendar calendar) {
+ //这里写拦截条件,返回true代表拦截,尽量以最高效的代码执行
+ return calendar.isWeekend();
+ }
+
+ @Override
+ public void onCalendarInterceptClick(Calendar calendar, boolean isClick) {
+ //todo 点击拦截的日期回调
+ }
+});
+```
+
+- 添加日期拦截器和范围设置后,你可以在周月视图按需求获得他们的结果
+```java
+
+boolean isInRange = isInRange(calendar);//日期是否在范围内,超出范围的可以置灰
+
+boolean isEnable = !onCalendarIntercept(calendar);//日期是否可用,没有被拦截,被拦截的可以置灰
+
+```
+
+
+
+
+- 假如你是做清单类、任务类APP的,可能会有这样的需求:标记某天事务的进度,这也很简单,因为:日历界面长什么样,你自己说了算!!!
+
+
+
+
+- 也许你只需要像原生日历那样就够了,但原生日历那奇怪且十分不友好的style,受到theme的影响,各种头疼,使用此控件,你只需要简简单单定制月视图就够了,**CalendarView** 能非常简单就高仿各种日历UI
+
+- **CalendarView** 提供了 **setSchemeDate(Map mSchemeDates)** 这个十分高效的API用来动态标记事务,即时你的数据量达到数千、数万、数十万,都不会对UI渲染造成影响
+
+- 日历类 **Calendar** 提供了许多十分有用的API
+
+```java
+boolean isWeekend();//判断是不是周末,可以用不同的画笔绘制周末的样式
+
+int getWeek();//获取星期
+
+String getSolarTerm();//获取24节气,可以用不同颜色标记不同节日
+
+String getGregorianFestival();//获取公历节日,自由判断,把节日换上喜欢的颜色
+
+String getTraditionFestival();//获取传统节日
+
+boolean isLeapYear();//是否是闰年
+
+int getLeapMonth();//获取闰月
+
+boolean isSameMonth(Calendar calendar);//是否相同月
+
+int compareTo(Calendar calendar);//比较日期大小 -1 0 1
+
+long getTimeInMillis();//获取时间戳
+
+int differ(Calendar calendar);//日期运算,相差多少天
+```
+
+### **CalendarView** 的全部xml特性如下:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### 写在最后,其它各种场景姿势就不多说了,看自己需求去实现。**再次注意:Demo只是Demo,只是示例如何使用,与框架本身没有关联,不属于框架一部分**
+### 框架本身是为了解决各种各样的场景而设计的,UI本身是靠自己绘制的,非常简单,如果还不懂的请优先看Demo,你可以自由发挥想象力定制最喜欢的日历,只有你想不到,Demo基本给出了各种场景的实现思路。觉得可以的请给个star或者留下你宝贵的意见。
\ No newline at end of file
diff --git a/CalendarView/README.md b/CalendarView/README.md
new file mode 100644
index 00000000..087d4fca
--- /dev/null
+++ b/CalendarView/README.md
@@ -0,0 +1,98 @@
+# CalenderView
+
+### RecyclerView? ListView or ViewGroup? No, it's Canvas! Ultra performance and free customization interface! Fast rendering and extremely low memory.
+
+[**Download CalendarViewDemo.apk**](https://github.com/huanghaibin-dev/CalendarView/blob/master/CalendarViewDemo.apk)
+
+# 温馨提醒 Warm tips
+
+Github代码全部开源无限制使用,免费开源最终版本为3.7.1,垂直、水平切换日历、自定义动画高仿iOS日历等源码不再开源。
+The final version of the free and open source part is 3.7.1, the vertical and horizontal switching calendar liked iOS calendar are no longer open source.
+
+
+
+
+
+### AndroidStudio v3.5+
+
+### support version if using support package
+```
+implementation 'com.haibin:calendarview:3.6.8'
+```
+
+### Androidx version if using Androidx
+```
+implementation 'com.haibin:calendarview:3.7.1'
+```
+
+```
+
+ com.haibin
+ calendarview
+ 3.7.1
+ pom
+
+```
+
+## How to use?
+
+[**English Doc**](https://github.com/huanghaibin-dev/CalendarView/blob/master/QUESTION.md)
+
+[**中文使用文档**](https://github.com/huanghaibin-dev/CalendarView/blob/master/QUESTION_ZH.md)
+
+### proguard-rules
+```java
+-keepclasseswithmembers class * {
+ public (android.content.Context);
+}
+```
+
+### or using this proguard-rules
+``` java
+-keep class your project path.MonthView {
+ public (android.content.Context);
+}
+-keep class your project path.WeekBar {
+ public (android.content.Context);
+}
+-keep class your project path.WeekView {
+ public (android.content.Context);
+}
+-keep class your project path.YearView {
+ public (android.content.Context);
+}
+```
+
+### Effect Preview
+
+### func
+
+### YearView and Range Style
+
+### Beautiful Chinese style
+
+### Meizu mobile phone calendar
+
+### Colorful and Full style
+
+### Progress bar style
+
+### Galaxy style
+
+
+
+
+## Licenses
+- Copyright (C) 2013 huanghaibin_dev
+
+- Licensed under the Apache License, Version 2.0 (the "License");
+- you may not use this file except in compliance with the License.
+- You may obtain a copy of the License at
+
+- http://www.apache.org/licenses/LICENSE-2.0
+
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/CalendarView/README_ZH.md b/CalendarView/README_ZH.md
new file mode 100644
index 00000000..3bf471e8
--- /dev/null
+++ b/CalendarView/README_ZH.md
@@ -0,0 +1,350 @@
+# CalenderView
+Android上一个优雅、高度自定义、性能高效的日历控件,完美支持周视图,支持标记、自定义颜色、农历等,任意控制月视图显示、任意日期拦截条件、自定义周起始等。Canvas绘制,极速性能、占用内存低,,支持简单定制即可实现任意自定义布局、自定义UI,支持收缩展开、性能非常高效,
+这个控件内存和效率优势相当明显,而且真正做到收缩+展开,适配多种场景,支持同时多种颜色标记日历事务,支持多点触控,你真的想不到日历还可以如此优雅!更多参考用法请移步Demo,Demo实现了一些精美的自定义效果,用法仅供参考。
+
+# 温馨提醒
+Github代码全部开源无限制使用,免费开源最终版本为3.7.1,垂直、水平切换日历、高仿iOS日历等源码不再开源。
+
+### 插拔式设计
+
+插拔式设计:好比插座一样,插上灯泡就会亮,插上风扇就会转,看用户需求什么而不是看插座有什么,只要是电器即可。此框架使用插拔式,既可以在编译时指定年月日视图,如:app:month_view="xxx.xxx.MonthView.class",也可在运行时动态更换年月日视图,如:CalendarView.setMonthViewClass(MonthView.Class),从而达到UI即插即用的效果,相当于框架不提供UI实现,让UI都由客户端实现,不至于日历UI都千篇一律,只需遵守插拔式接口即可随意定制,自由化程度非常高。
+
+[**English Version**](https://github.com/huanghaibin-dev/CalendarView/blob/master/QUESTION.md)
+
+[**详细介绍**](https://github.com/huanghaibin-dev/CalendarView/blob/master/QUESTION_ZH.md)
+
+### AndroidStudio v3.5+
+
+### support版本使用
+```
+implementation 'com.haibin:calendarview:3.6.8
+```
+
+### Androidx版本使用
+```
+implementation 'com.haibin:calendarview:3.7.1'
+```
+
+```
+
+ com.haibin
+ calendarview
+ 3.7.0
+ pom
+
+```
+
+
+### 混淆proguard-rules
+```java
+-keepclasseswithmembers class * {
+ public (android.content.Context);
+}
+```
+
+### 或者针对性的使用混淆,请自行配置测试!
+``` java
+-keep class your project path.MonthView {
+ public (android.content.Context);
+}
+-keep class your project path.WeekBar {
+ public (android.content.Context);
+}
+-keep class your project path.WeekView {
+ public (android.content.Context);
+}
+-keep class your project path.YearView {
+ public (android.content.Context);
+}
+```
+
+### 效果预览
+### 功能性展示
+
+### 年视图和范围选择风格
+
+### 中国式变态需求风格
+
+### 收缩展开的魅族风格效果a
+
+### 全屏和多彩风格
+
+### 进度条风格
+
+### 星系图风格
+
+
+### 特别的,请注意不要复制这三个路径,自行替换您自己的自定义路径
+
+```xml
+app:month_view="com.haibin.calendarviewproject.simple.SimpleMonthView"
+app:week_view="com.haibin.calendarviewproject.simple.SimpleWeekView"
+app:week_bar_view="com.haibin.calendarviewproject.EnglishWeekBar"
+```
+
+### 使用方法
+```xml
+
+
+
+
+
+
+```
+
+### CalendarView attrs
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+### CalendarView api
+```java
+
+public void setRange(int minYear, int minYearMonth, int minYearDay,
+ int maxYear, int maxYearMonth, int maxYearDay) ;//置日期范围
+
+public int getCurDay(); //今天
+public int getCurMonth(); //当前的月份
+public int getCurYear(); //今年
+
+public boolean isYearSelectLayoutVisible();//年月份选择视图是否打开
+public void closeYearSelectLayout();//关闭年月视图选择布局
+public void showYearSelectLayout(final int year); //快速弹出年份选择月份
+
+public void setOnMonthChangeListener(OnMonthChangeListener listener);//月份改变事件
+
+public void setOnYearChangeListener(OnYearChangeListener listener);//年份切换事件
+
+public void setOnCalendarSelectListener(OnCalendarSelectListener listener)//日期选择事件
+
+public void setOnCalendarLongClickListener(OnCalendarLongClickListener listener);//日期长按事件
+
+public void setOnCalendarLongClickListener(OnCalendarLongClickListener listener, boolean preventLongPressedSelect);//日期长按事件
+
+public void setOnCalendarInterceptListener(OnCalendarInterceptListener listener);//日期拦截和日期有效性绘制
+
+public void setSchemeDate(Map mSchemeDates);//标记日期
+
+public void update();//动态更新
+
+public Calendar getSelectedCalendar(); //获取选择的日期
+
+/**
+ * 特别的,如果你需要自定义或者使用其它选择器,可以用以下方法进行和日历联动
+ */
+public void scrollToCurrent();//滚动到当前日期
+
+public void scrollToCurrent(boolean smoothScroll);//滚动到当前日期
+
+public void scrollToYear(int year);//滚动到某一年
+
+public void scrollToPre();//滚动到上一个月
+
+public void scrollToNext();//滚动到下一个月
+
+public void scrollToCalendar(int year, int month, int day);//滚动到指定日期
+
+public Calendar getMinRangeCalendar();//获得最小范围日期
+
+public Calendar getMaxRangeCalendar();//获得最大范围日期
+
+/**
+ * 设置背景色
+ *
+ * @param monthLayoutBackground 月份卡片的背景色
+ * @param weekBackground 星期栏背景色
+ * @param lineBg 线的颜色
+ */
+public void setBackground(int monthLayoutBackground, int weekBackground, int lineBg)
+
+/**
+ * 设置文本颜色
+ *
+ * @param curMonthTextColor 当前月份字体颜色
+ * @param otherMonthColor 其它月份字体颜色
+ * @param lunarTextColor 农历字体颜色
+ */
+public void setTextColor(int curMonthTextColor,int otherMonthColor,int lunarTextColor)
+
+/**
+ * 设置选择的效果
+ *
+ * @param style 选中的style CalendarCardView.STYLE_FILL or CalendarCardView.STYLE_STROKE
+ * @param selectedThemeColor 选中的标记颜色
+ * @param selectedTextColor 选中的字体颜色
+ */
+public void setSelectedColor(int style, int selectedThemeColor, int selectedTextColor)
+
+/**
+ * 设置标记的色
+ *
+ * @param style 标记的style CalendarCardView.STYLE_FILL or CalendarCardView.STYLE_STROKE
+ * @param schemeColor 标记背景色
+ * @param schemeTextColor 标记字体颜色
+ */
+public void setSchemeColor(int style, int schemeColor, int schemeTextColor)
+
+
+/**
+ * 设置星期栏的背景和字体颜色
+ *
+ * @param weekBackground 背景色
+ * @param weekTextColor 字体颜色
+ */
+public void setWeeColor(int weekBackground, int weekTextColor)
+```
+### CalendarLayout api
+
+```java
+public void expand(); //展开
+
+public void shrink(); //收缩
+
+public boolean isExpand();//是否展开了
+```
+
+### CalendarLayout attrs
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+## Licenses
+- Copyright (C) 2013 huanghaibin_dev
+
+- Licensed under the Apache License, Version 2.0 (the "License");
+- you may not use this file except in compliance with the License.
+- You may obtain a copy of the License at
+
+- http://www.apache.org/licenses/LICENSE-2.0
+
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/CalendarView/app/.gitignore b/CalendarView/app/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/CalendarView/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/CalendarView/app/build.gradle b/CalendarView/app/build.gradle
new file mode 100644
index 00000000..572aedfc
--- /dev/null
+++ b/CalendarView/app/build.gradle
@@ -0,0 +1,39 @@
+//noinspection GradleCompatible
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 28
+ //noinspection GradleDependency
+ defaultConfig {
+ applicationId "com.haibin.calendarviewproject"
+ minSdkVersion 15
+ //noinspection OldTargetApi
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+
+ implementation 'com.google.android.material:material:1.2.1'
+
+ implementation 'com.github.bumptech.glide:glide:4.5.0'
+
+ testImplementation 'junit:junit:4.12'
+ implementation project(':calendarview')
+
+}
diff --git a/CalendarView/app/proguard-rules.pro b/CalendarView/app/proguard-rules.pro
new file mode 100644
index 00000000..f3e3ccae
--- /dev/null
+++ b/CalendarView/app/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in D:\android\sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/CalendarView/app/src/androidTest/java/com/haibin/calendarviewproject/ExampleInstrumentedTest.java b/CalendarView/app/src/androidTest/java/com/haibin/calendarviewproject/ExampleInstrumentedTest.java
new file mode 100644
index 00000000..5bfe2f53
--- /dev/null
+++ b/CalendarView/app/src/androidTest/java/com/haibin/calendarviewproject/ExampleInstrumentedTest.java
@@ -0,0 +1,15 @@
+package com.haibin.calendarviewproject;
+
+import org.junit.Test;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+
+ }
+}
diff --git a/CalendarView/app/src/main/AndroidManifest.xml b/CalendarView/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..d6cd6eae
--- /dev/null
+++ b/CalendarView/app/src/main/AndroidManifest.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/app/src/main/assets/WechatIMG65.jpeg b/CalendarView/app/src/main/assets/WechatIMG65.jpeg
new file mode 100644
index 00000000..bebcaca7
Binary files /dev/null and b/CalendarView/app/src/main/assets/WechatIMG65.jpeg differ
diff --git a/CalendarView/app/src/main/assets/color_expand.png b/CalendarView/app/src/main/assets/color_expand.png
new file mode 100644
index 00000000..7cc5f287
Binary files /dev/null and b/CalendarView/app/src/main/assets/color_expand.png differ
diff --git a/CalendarView/app/src/main/assets/color_shrink.png b/CalendarView/app/src/main/assets/color_shrink.png
new file mode 100644
index 00000000..d44f654b
Binary files /dev/null and b/CalendarView/app/src/main/assets/color_shrink.png differ
diff --git a/CalendarView/app/src/main/assets/colorful.jpeg b/CalendarView/app/src/main/assets/colorful.jpeg
new file mode 100644
index 00000000..d8b1bd37
Binary files /dev/null and b/CalendarView/app/src/main/assets/colorful.jpeg differ
diff --git a/CalendarView/app/src/main/assets/colorl_shrink.png b/CalendarView/app/src/main/assets/colorl_shrink.png
new file mode 100644
index 00000000..6819efbb
Binary files /dev/null and b/CalendarView/app/src/main/assets/colorl_shrink.png differ
diff --git a/CalendarView/app/src/main/assets/custom_expand.png b/CalendarView/app/src/main/assets/custom_expand.png
new file mode 100644
index 00000000..9988b41c
Binary files /dev/null and b/CalendarView/app/src/main/assets/custom_expand.png differ
diff --git a/CalendarView/app/src/main/assets/custom_shrink.png b/CalendarView/app/src/main/assets/custom_shrink.png
new file mode 100644
index 00000000..63b79e18
Binary files /dev/null and b/CalendarView/app/src/main/assets/custom_shrink.png differ
diff --git a/CalendarView/app/src/main/assets/dark_list.jpg b/CalendarView/app/src/main/assets/dark_list.jpg
new file mode 100644
index 00000000..2d45acea
Binary files /dev/null and b/CalendarView/app/src/main/assets/dark_list.jpg differ
diff --git a/CalendarView/app/src/main/assets/full_calendar.jpg b/CalendarView/app/src/main/assets/full_calendar.jpg
new file mode 100644
index 00000000..3561cc74
Binary files /dev/null and b/CalendarView/app/src/main/assets/full_calendar.jpg differ
diff --git a/CalendarView/app/src/main/assets/full_calendar.png b/CalendarView/app/src/main/assets/full_calendar.png
new file mode 100644
index 00000000..eae60ea4
Binary files /dev/null and b/CalendarView/app/src/main/assets/full_calendar.png differ
diff --git a/CalendarView/app/src/main/assets/index.jpeg b/CalendarView/app/src/main/assets/index.jpeg
new file mode 100644
index 00000000..d18fe466
Binary files /dev/null and b/CalendarView/app/src/main/assets/index.jpeg differ
diff --git a/CalendarView/app/src/main/assets/index_expand.png b/CalendarView/app/src/main/assets/index_expand.png
new file mode 100644
index 00000000..32286b46
Binary files /dev/null and b/CalendarView/app/src/main/assets/index_expand.png differ
diff --git a/CalendarView/app/src/main/assets/main_en_func.png b/CalendarView/app/src/main/assets/main_en_func.png
new file mode 100644
index 00000000..a41ca367
Binary files /dev/null and b/CalendarView/app/src/main/assets/main_en_func.png differ
diff --git a/CalendarView/app/src/main/assets/main_en_list.png b/CalendarView/app/src/main/assets/main_en_list.png
new file mode 100644
index 00000000..21918fdc
Binary files /dev/null and b/CalendarView/app/src/main/assets/main_en_list.png differ
diff --git a/CalendarView/app/src/main/assets/main_zh_func.png b/CalendarView/app/src/main/assets/main_zh_func.png
new file mode 100644
index 00000000..a166ecfe
Binary files /dev/null and b/CalendarView/app/src/main/assets/main_zh_func.png differ
diff --git a/CalendarView/app/src/main/assets/main_zh_list.png b/CalendarView/app/src/main/assets/main_zh_list.png
new file mode 100644
index 00000000..dfb9a440
Binary files /dev/null and b/CalendarView/app/src/main/assets/main_zh_list.png differ
diff --git a/CalendarView/app/src/main/assets/meizu.png b/CalendarView/app/src/main/assets/meizu.png
new file mode 100644
index 00000000..74e73779
Binary files /dev/null and b/CalendarView/app/src/main/assets/meizu.png differ
diff --git a/CalendarView/app/src/main/assets/meizu_expand.png b/CalendarView/app/src/main/assets/meizu_expand.png
new file mode 100644
index 00000000..31183c4b
Binary files /dev/null and b/CalendarView/app/src/main/assets/meizu_expand.png differ
diff --git a/CalendarView/app/src/main/assets/meizu_shrink.png b/CalendarView/app/src/main/assets/meizu_shrink.png
new file mode 100644
index 00000000..b7c269df
Binary files /dev/null and b/CalendarView/app/src/main/assets/meizu_shrink.png differ
diff --git a/CalendarView/app/src/main/assets/month.jpeg b/CalendarView/app/src/main/assets/month.jpeg
new file mode 100644
index 00000000..6fe892ad
Binary files /dev/null and b/CalendarView/app/src/main/assets/month.jpeg differ
diff --git a/CalendarView/app/src/main/assets/new_ios.gif b/CalendarView/app/src/main/assets/new_ios.gif
new file mode 100644
index 00000000..7d8d1940
Binary files /dev/null and b/CalendarView/app/src/main/assets/new_ios.gif differ
diff --git a/CalendarView/app/src/main/assets/progress_expand.png b/CalendarView/app/src/main/assets/progress_expand.png
new file mode 100644
index 00000000..5708d5fa
Binary files /dev/null and b/CalendarView/app/src/main/assets/progress_expand.png differ
diff --git a/CalendarView/app/src/main/assets/progress_shrink.png b/CalendarView/app/src/main/assets/progress_shrink.png
new file mode 100644
index 00000000..9d27d668
Binary files /dev/null and b/CalendarView/app/src/main/assets/progress_shrink.png differ
diff --git a/CalendarView/app/src/main/assets/range.png b/CalendarView/app/src/main/assets/range.png
new file mode 100644
index 00000000..12e19c9d
Binary files /dev/null and b/CalendarView/app/src/main/assets/range.png differ
diff --git a/CalendarView/app/src/main/assets/range_select.jpg b/CalendarView/app/src/main/assets/range_select.jpg
new file mode 100644
index 00000000..69b40c82
Binary files /dev/null and b/CalendarView/app/src/main/assets/range_select.jpg differ
diff --git a/CalendarView/app/src/main/assets/screen_main.gif b/CalendarView/app/src/main/assets/screen_main.gif
new file mode 100644
index 00000000..aac19c40
Binary files /dev/null and b/CalendarView/app/src/main/assets/screen_main.gif differ
diff --git a/CalendarView/app/src/main/assets/screen_recorder_flip.gif b/CalendarView/app/src/main/assets/screen_recorder_flip.gif
new file mode 100644
index 00000000..49aa219c
Binary files /dev/null and b/CalendarView/app/src/main/assets/screen_recorder_flip.gif differ
diff --git a/CalendarView/app/src/main/assets/screen_recorder_main.gif b/CalendarView/app/src/main/assets/screen_recorder_main.gif
new file mode 100644
index 00000000..a38f8a5e
Binary files /dev/null and b/CalendarView/app/src/main/assets/screen_recorder_main.gif differ
diff --git a/CalendarView/app/src/main/assets/screen_recorder_multi.gif b/CalendarView/app/src/main/assets/screen_recorder_multi.gif
new file mode 100644
index 00000000..22e456e6
Binary files /dev/null and b/CalendarView/app/src/main/assets/screen_recorder_multi.gif differ
diff --git a/CalendarView/app/src/main/assets/screen_recorder_range.gif b/CalendarView/app/src/main/assets/screen_recorder_range.gif
new file mode 100644
index 00000000..d4fcff96
Binary files /dev/null and b/CalendarView/app/src/main/assets/screen_recorder_range.gif differ
diff --git a/CalendarView/app/src/main/assets/shrink.jpeg b/CalendarView/app/src/main/assets/shrink.jpeg
new file mode 100644
index 00000000..1fff2f04
Binary files /dev/null and b/CalendarView/app/src/main/assets/shrink.jpeg differ
diff --git a/CalendarView/app/src/main/assets/simple.jpeg b/CalendarView/app/src/main/assets/simple.jpeg
new file mode 100644
index 00000000..f63808a8
Binary files /dev/null and b/CalendarView/app/src/main/assets/simple.jpeg differ
diff --git a/CalendarView/app/src/main/assets/simple.jpg b/CalendarView/app/src/main/assets/simple.jpg
new file mode 100644
index 00000000..bab230e1
Binary files /dev/null and b/CalendarView/app/src/main/assets/simple.jpg differ
diff --git a/CalendarView/app/src/main/assets/simple_expand.png b/CalendarView/app/src/main/assets/simple_expand.png
new file mode 100644
index 00000000..c4c5f9db
Binary files /dev/null and b/CalendarView/app/src/main/assets/simple_expand.png differ
diff --git a/CalendarView/app/src/main/assets/simple_shrink.png b/CalendarView/app/src/main/assets/simple_shrink.png
new file mode 100644
index 00000000..3ae65950
Binary files /dev/null and b/CalendarView/app/src/main/assets/simple_shrink.png differ
diff --git a/CalendarView/app/src/main/assets/solar_expand.png b/CalendarView/app/src/main/assets/solar_expand.png
new file mode 100644
index 00000000..9c726f21
Binary files /dev/null and b/CalendarView/app/src/main/assets/solar_expand.png differ
diff --git a/CalendarView/app/src/main/assets/solar_shrink.png b/CalendarView/app/src/main/assets/solar_shrink.png
new file mode 100644
index 00000000..e1bc4a8f
Binary files /dev/null and b/CalendarView/app/src/main/assets/solar_shrink.png differ
diff --git a/CalendarView/app/src/main/assets/year_view.png b/CalendarView/app/src/main/assets/year_view.png
new file mode 100644
index 00000000..6e92488f
Binary files /dev/null and b/CalendarView/app/src/main/assets/year_view.png differ
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/Article.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/Article.java
new file mode 100644
index 00000000..016519de
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/Article.java
@@ -0,0 +1,74 @@
+package com.haibin.calendarviewproject;
+
+import java.io.Serializable;
+
+/**
+ * 一个简单的bean
+ * Created by huanghaibin on 2017/12/4.
+ */
+@SuppressWarnings("all")
+public class Article implements Serializable {
+ private int id;
+ private String title;
+ private String content;
+ private String imgUrl;
+ private String desc;
+ private String date;
+ private int type;
+
+ public int getId() {
+ return id;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public String getImgUrl() {
+ return imgUrl;
+ }
+
+ public void setImgUrl(String imgUrl) {
+ this.imgUrl = imgUrl;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getDate() {
+ return date;
+ }
+
+ public void setDate(String date) {
+ this.date = date;
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/ArticleAdapter.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/ArticleAdapter.java
new file mode 100644
index 00000000..9a188c88
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/ArticleAdapter.java
@@ -0,0 +1,138 @@
+package com.haibin.calendarviewproject;
+
+import android.content.Context;
+import androidx.recyclerview.widget.RecyclerView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.RequestManager;
+import com.haibin.calendarviewproject.group.GroupRecyclerAdapter;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+/**
+ * 适配器
+ * Created by huanghaibin on 2017/12/4.
+ */
+
+public class ArticleAdapter extends GroupRecyclerAdapter {
+
+
+ private RequestManager mLoader;
+
+ public ArticleAdapter(Context context) {
+ super(context);
+ mLoader = Glide.with(context.getApplicationContext());
+ LinkedHashMap> map = new LinkedHashMap<>();
+ List titles = new ArrayList<>();
+ map.put("今日推荐", create(0));
+ map.put("每周热点", create(1));
+ map.put("最高评论", create(2));
+ titles.add("今日推荐");
+ titles.add("每周热点");
+ titles.add("最高评论");
+ resetGroups(map,titles);
+ }
+
+
+ @Override
+ protected RecyclerView.ViewHolder onCreateDefaultViewHolder(ViewGroup parent, int type) {
+ return new ArticleViewHolder(mInflater.inflate(R.layout.item_list_article, parent, false));
+ }
+
+ @Override
+ protected void onBindViewHolder(RecyclerView.ViewHolder holder, Article item, int position) {
+ ArticleViewHolder h = (ArticleViewHolder) holder;
+ h.mTextTitle.setText(item.getTitle());
+ h.mTextContent.setText(item.getContent());
+ mLoader.load(item.getImgUrl())
+ .into(h.mImageView);
+ }
+
+ private static class ArticleViewHolder extends RecyclerView.ViewHolder {
+ private TextView mTextTitle,
+ mTextContent;
+ private ImageView mImageView;
+
+ private ArticleViewHolder(View itemView) {
+ super(itemView);
+ mTextTitle = itemView.findViewById(R.id.tv_title);
+ mTextContent = itemView.findViewById(R.id.tv_content);
+ mImageView = itemView.findViewById(R.id.imageView);
+ }
+ }
+
+
+ private static Article create(String title, String content, String imgUrl) {
+ Article article = new Article();
+ article.setTitle(title);
+ article.setContent(content);
+ article.setImgUrl(imgUrl);
+ return article;
+ }
+
+ private static List create(int p) {
+ List list = new ArrayList<>();
+ if (p == 0) {
+ list.add(create("新西兰克马德克群岛发生5.7级地震 震源深度10千米",
+ "#地震快讯#中国地震台网正式测定:12月04日08时08分在克马德克群岛(南纬32.82度,西经178.73度)发生5.7级地震,震源深度10千米。",
+ "http://cms-bucket.nosdn.127.net/catchpic/2/27/27e2ce7fd02e6c096e21b1689a8a3fe9.jpg?imageView&thumbnail=550x0"));
+ list.add(create("俄罗斯喊冤不当\"背锅侠\" 俄美陷入\"后真相\"旋涡",
+ "“差到令人震惊”,但不怪特朗普。俄罗斯总理德米特里·梅德韦杰夫近日在谈到俄美关系时这样说。俄罗斯近来连遭美国“恶毒”指责和西方国家连环出击。一些国际舆论认为,俄罗斯成了“背锅侠”,俄罗斯自己也公开喊冤斥责美国。在俄美你来我往的互掐中,真相似乎变得已不那么重要了。",
+ "http://cms-bucket.nosdn.127.net/catchpic/c/c8/c8b0685089258b82f3ca1997def78d8d.png?imageView&thumbnail=550x0"));
+ list.add(create("中企投资巴西获支持 英媒:巴西人感激\"保住饭碗\"",
+ "参考消息网12月4日报道 英媒称,里约热内卢附近的阿苏港曾被埃克·巴蒂斯塔称为“通往中国的公路”,10多年前,这位现已名誉扫地的巴西前首富创建了这个超级港,大宗商品热潮结束后,他在巴西的商业帝国几乎无一幸存并于2014年破产,但此后至今有一个项目仍蓬勃发展,那就是阿苏港。",
+ "http://cms-bucket.nosdn.127.net/catchpic/8/8b/8ba2d19b7f63efc5cf714960d5edd2c3.jpg?imageView&thumbnail=550x0"));
+ list.add(create("美电视台记者因误报有关弗林新闻被停职四周",
+ "【环球网报道】据俄罗斯卫星网12月3日报道,美国ABC电视台记者布莱恩·罗素因在有关美国总统前国家安全顾问迈克尔·弗林的新闻报道中的失误,临时被停职。",
+ "http://cms-bucket.nosdn.127.net/5d18566fde70407b9cc3a728822115c320171203133214.jpeg?imageView&thumbnail=550x0"));
+ list.add(create("预计明年3月上市 曝全新奥迪Q5L无伪谍照",
+ "随着之前全新一代国产奥迪Q5L在工信部目录亮相,最近曝光的测试车也都基本褪去了伪装,不过正式上市还是要等到2018年3月份。从最新曝光的内饰来看,轴距的加长令后排的空间有着非常明显的提升。",
+ "http://cms-bucket.nosdn.127.net/eda9ca222352470190c4f0d6b9a8c29420171201160854.jpeg?imageView&thumbnail=550x0"));
+ } else if (p == 1) {
+ list.add(create(
+ "2019年投产 电咖整车生产基地落户浙江绍兴",
+ "网易汽车11月30日报道 两周前的广州车展上,电咖发布了其首款电动汽车EV10,官方指导价为13.38万-14.18万,扣除补贴后的零售价为5.98万元-6.78万元,性价比很高。抛开车辆本身,引起业界关注的是这家新势力造车企业的几位核心成员,当年上汽大众团队的三位老兵--张海亮、向东平、牛胜福携手用了957天造了一辆可以上市的车。",
+ "http://cms-bucket.nosdn.127.net/674c392123254bb69bdd9227442965eb20171129203658.jpeg?imageView&thumbnail=550x0"));
+ list.add(create(
+ "2017年进入尾声,苹果大笔押注的ARkit还好么?",
+ "谷歌推出了AR眼镜、ARCore平台和应用在手机上的Project Tango,Facebook也上线了AR开发平台和工具。至于苹果,更是把AR当做发展的重中之重。在新品iPhone8和iPhoneX中,后置摄像头专为AR进行校准,前置摄像头还添加了可以带来更好AR效果的深度传感器。",
+ "http://cms-bucket.nosdn.127.net/catchpic/7/76/76135ac5d3107a1d5ba11a8ee2fc7e27.jpg?imageView&thumbnail=550x0"));
+ list.add(create(
+ "亚马逊CTO:我们要让人类成为机器人的中心!",
+ "那些相信应用下载会让世界变得更美好的智能手机布道者和应用爱好者们,会在AWS re:Invent大会上感到不自在。亚马逊网络服务首席技术官Werner Vogels表示,所有这些都未能实现信息的民主化。",
+ "http://cms-bucket.nosdn.127.net/ddb758f16a7d4aa3aa422ec385fc3e5020171204081818.jpeg?imageView&thumbnail=550x0"));
+ list.add(create(
+ "有特斯拉车主想用免费的充电桩挖矿,但这可能行不通",
+ "在社交网络 Facebook 上的一个特斯拉车主群组中,有人开脑洞说可以尝试自己组装矿机放在特斯拉后备箱中,接入车载电池的电源,然后将车停到超级充电桩附近,就能用免费获得的电力挖矿了。",
+ "http://crawl.nosdn.127.net/nbotreplaceimg/4ce9c743e6c02f6777d22278e2ef8bc3/2b33e32532db204fe207693c82719660.jpg"));
+ } else if (p == 2) {
+ list.add(create(
+ "2017年进入尾声,苹果大笔押注的ARkit还好么?",
+ "谷歌推出了AR眼镜、ARCore平台和应用在手机上的Project Tango,Facebook也上线了AR开发平台和工具。至于苹果,更是把AR当做发展的重中之重。在新品iPhone8和iPhoneX中,后置摄像头专为AR进行校准,前置摄像头还添加了可以带来更好AR效果的深度传感器。",
+ "http://cms-bucket.nosdn.127.net/catchpic/7/76/76135ac5d3107a1d5ba11a8ee2fc7e27.jpg?imageView&thumbnail=550x0"));
+ list.add(create(
+ "亚马逊CTO:我们要让人类成为机器人的中心!",
+ "那些相信应用下载会让世界变得更美好的智能手机布道者和应用爱好者们,会在AWS re:Invent大会上感到不自在。亚马逊网络服务首席技术官Werner Vogels表示,所有这些都未能实现信息的民主化。",
+ "http://cms-bucket.nosdn.127.net/ddb758f16a7d4aa3aa422ec385fc3e5020171204081818.jpeg?imageView&thumbnail=550x0"));
+ list.add(create("中企投资巴西获支持 英媒:巴西人感激\"保住饭碗\"",
+ "参考消息网12月4日报道 英媒称,里约热内卢附近的阿苏港曾被埃克·巴蒂斯塔称为“通往中国的公路”,10多年前,这位现已名誉扫地的巴西前首富创建了这个超级港,大宗商品热潮结束后,他在巴西的商业帝国几乎无一幸存并于2014年破产,但此后至今有一个项目仍蓬勃发展,那就是阿苏港。",
+ "http://cms-bucket.nosdn.127.net/catchpic/8/8b/8ba2d19b7f63efc5cf714960d5edd2c3.jpg?imageView&thumbnail=550x0"));
+ list.add(create("美电视台记者因误报有关弗林新闻被停职四周",
+ "【环球网报道】据俄罗斯卫星网12月3日报道,美国ABC电视台记者布莱恩·罗素因在有关美国总统前国家安全顾问迈克尔·弗林的新闻报道中的失误,临时被停职。",
+ "http://cms-bucket.nosdn.127.net/5d18566fde70407b9cc3a728822115c320171203133214.jpeg?imageView&thumbnail=550x0"));
+ list.add(create(
+ "2019年投产 电咖整车生产基地落户浙江绍兴",
+ "网易汽车11月30日报道 两周前的广州车展上,电咖发布了其首款电动汽车EV10,官方指导价为13.38万-14.18万,扣除补贴后的零售价为5.98万元-6.78万元,性价比很高。抛开车辆本身,引起业界关注的是这家新势力造车企业的几位核心成员,当年上汽大众团队的三位老兵--张海亮、向东平、牛胜福携手用了957天造了一辆可以上市的车。",
+ "http://cms-bucket.nosdn.127.net/674c392123254bb69bdd9227442965eb20171129203658.jpeg?imageView&thumbnail=550x0"));
+ }
+
+
+ return list;
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/CalendarActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/CalendarActivity.java
new file mode 100644
index 00000000..7d27adc4
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/CalendarActivity.java
@@ -0,0 +1,33 @@
+package com.haibin.calendarviewproject;
+
+import android.content.Context;
+import android.content.Intent;
+
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+
+/**
+ * Only calendar
+ * Created by haibin on 2019/6/12.
+ */
+
+public class CalendarActivity extends BaseActivity {
+
+ public static void show(Context context) {
+ context.startActivity(new Intent(context, CalendarActivity.class));
+ }
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_calendar;
+ }
+
+ @Override
+ protected void initView() {
+ setStatusBarDarkMode();
+ }
+
+ @Override
+ protected void initData() {
+
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/EnglishWeekBar.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/EnglishWeekBar.java
new file mode 100644
index 00000000..deb54a8c
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/EnglishWeekBar.java
@@ -0,0 +1,78 @@
+package com.haibin.calendarviewproject;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.view.LayoutInflater;
+import android.widget.TextView;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.WeekBar;
+
+/**
+ * 自定义英文栏
+ * Created by huanghaibin on 2017/11/30.
+ */
+
+public class EnglishWeekBar extends WeekBar {
+
+ private int mPreSelectedIndex;
+
+ public EnglishWeekBar(Context context) {
+ super(context);
+ LayoutInflater.from(context).inflate(R.layout.english_week_bar, this, true);
+ setBackgroundColor(Color.WHITE);
+ int padding = dipToPx(context, 10);
+ setPadding(padding, 0, padding, 0);
+ }
+
+ @Override
+ protected void onDateSelected(Calendar calendar, int weekStart, boolean isClick) {
+ getChildAt(mPreSelectedIndex).setSelected(false);
+ int viewIndex = getViewIndexByCalendar(calendar, weekStart);
+ getChildAt(viewIndex).setSelected(true);
+ mPreSelectedIndex = viewIndex;
+ }
+
+ /**
+ * 当周起始发生变化,使用自定义布局需要重写这个方法,避免出问题
+ *
+ * @param weekStart 周起始
+ */
+ @Override
+ protected void onWeekStartChange(int weekStart) {
+ for (int i = 0; i < getChildCount(); i++) {
+ ((TextView) getChildAt(i)).setText(getWeekString(i, weekStart));
+ }
+ }
+
+ /**
+ * 或者周文本,这个方法仅供父类使用
+ *
+ * @param index index
+ * @param weekStart weekStart
+ * @return 或者周文本
+ */
+ private String getWeekString(int index, int weekStart) {
+ String[] weeks = getContext().getResources().getStringArray(R.array.english_week_string_array);
+
+ if (weekStart == 1) {
+ return weeks[index];
+ }
+ if (weekStart == 2) {
+ return weeks[index == 6 ? 0 : index + 1];
+ }
+ return weeks[index == 0 ? 6 : index - 1];
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/MainActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/MainActivity.java
new file mode 100644
index 00000000..a41e8a0b
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/MainActivity.java
@@ -0,0 +1,483 @@
+package com.haibin.calendarviewproject;
+
+import android.annotation.SuppressLint;
+import android.content.DialogInterface;
+
+import androidx.appcompat.app.AlertDialog;
+
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarLayout;
+import com.haibin.calendarview.CalendarView;
+import com.haibin.calendarview.TrunkBranchAnnals;
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+import com.haibin.calendarviewproject.colorful.ColorfulActivity;
+import com.haibin.calendarviewproject.custom.CustomActivity;
+import com.haibin.calendarviewproject.full.FullActivity;
+import com.haibin.calendarviewproject.index.IndexActivity;
+import com.haibin.calendarviewproject.meizu.MeiZuActivity;
+import com.haibin.calendarviewproject.meizu.MeiZuMonthView;
+import com.haibin.calendarviewproject.meizu.MeizuWeekView;
+import com.haibin.calendarviewproject.mix.MixActivity;
+import com.haibin.calendarviewproject.multi.MultiActivity;
+import com.haibin.calendarviewproject.pager.ViewPagerActivity;
+import com.haibin.calendarviewproject.progress.ProgressActivity;
+import com.haibin.calendarviewproject.range.RangeActivity;
+import com.haibin.calendarviewproject.simple.SimpleActivity;
+import com.haibin.calendarviewproject.single.SingleActivity;
+import com.haibin.calendarviewproject.solay.SolarActivity;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MainActivity extends BaseActivity implements
+ CalendarView.OnCalendarSelectListener,
+ CalendarView.OnCalendarLongClickListener,
+ CalendarView.OnMonthChangeListener,
+ CalendarView.OnYearChangeListener,
+ CalendarView.OnWeekChangeListener,
+ CalendarView.OnViewChangeListener,
+ CalendarView.OnCalendarInterceptListener,
+ CalendarView.OnYearViewChangeListener,
+ DialogInterface.OnClickListener,
+ View.OnClickListener {
+
+ TextView mTextMonthDay;
+
+ TextView mTextYear;
+
+ TextView mTextLunar;
+
+ TextView mTextCurrentDay;
+
+ CalendarView mCalendarView;
+
+ RelativeLayout mRelativeTool;
+ private int mYear;
+ CalendarLayout mCalendarLayout;
+
+ private AlertDialog mMoreDialog;
+
+ private AlertDialog mFuncDialog;
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_main;
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void initView() {
+ setStatusBarDarkMode();
+ mTextMonthDay = findViewById(R.id.tv_month_day);
+ mTextYear = findViewById(R.id.tv_year);
+ mTextLunar = findViewById(R.id.tv_lunar);
+
+ mRelativeTool = findViewById(R.id.rl_tool);
+ mCalendarView = findViewById(R.id.calendarView);
+
+ //mCalendarView.setRange(2018, 7, 1, 2019, 4, 28);
+ mTextCurrentDay = findViewById(R.id.tv_current_day);
+ mTextMonthDay.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!mCalendarLayout.isExpand()) {
+ mCalendarLayout.expand();
+ return;
+ }
+ mCalendarView.showYearSelectLayout(mYear);
+ mTextLunar.setVisibility(View.GONE);
+ mTextYear.setVisibility(View.GONE);
+ mTextMonthDay.setText(String.valueOf(mYear));
+ }
+ });
+ findViewById(R.id.iv_more).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mMoreDialog == null) {
+ mMoreDialog = new AlertDialog.Builder(MainActivity.this)
+ .setTitle(R.string.list_dialog_title)
+ .setItems(R.array.list_dialog_items, MainActivity.this)
+ .create();
+ }
+ mMoreDialog.show();
+ }
+ });
+
+ final DialogInterface.OnClickListener listener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case 0:
+ mCalendarLayout.expand();
+ break;
+ case 1:
+ boolean result = mCalendarLayout.shrink();
+ Log.e("shrink", " -- " + result);
+ break;
+ case 2:
+ mCalendarView.scrollToPre(false);
+ break;
+ case 3:
+ mCalendarView.scrollToNext(false);
+ break;
+ case 4:
+ //mCalendarView.scrollToCurrent(true);
+ mCalendarView.scrollToCalendar(2018, 12, 30);
+ break;
+ case 5:
+ mCalendarView.setRange(2018, 7, 1, 2019, 4, 28);
+// mCalendarView.setRange(mCalendarView.getCurYear(), mCalendarView.getCurMonth(), 6,
+// mCalendarView.getCurYear(), mCalendarView.getCurMonth(), 23);
+ break;
+ case 6:
+ Log.e("scheme", " " + mCalendarView.getSelectedCalendar().getScheme() + " -- "
+ + mCalendarView.getSelectedCalendar().isCurrentDay());
+ List weekCalendars = mCalendarView.getCurrentWeekCalendars();
+ for (Calendar calendar : weekCalendars) {
+ Log.e("onWeekChange", calendar.toString() + " -- " + calendar.getScheme());
+ }
+ new AlertDialog.Builder(MainActivity.this)
+ .setMessage(String.format("Calendar Range: \n%s —— %s",
+ mCalendarView.getMinRangeCalendar(),
+ mCalendarView.getMaxRangeCalendar()))
+ .show();
+ break;
+ }
+ }
+ };
+
+ findViewById(R.id.iv_func).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mFuncDialog == null) {
+ mFuncDialog = new AlertDialog.Builder(MainActivity.this)
+ .setTitle(R.string.func_dialog_title)
+ .setItems(R.array.func_dialog_items, listener)
+ .create();
+ }
+ mFuncDialog.show();
+ }
+ });
+
+ mCalendarLayout = findViewById(R.id.calendarLayout);
+ mCalendarView.setOnYearChangeListener(this);
+ mCalendarView.setOnCalendarSelectListener(this);
+ mCalendarView.setOnMonthChangeListener(this);
+ mCalendarView.setOnCalendarLongClickListener(this, true);
+ mCalendarView.setOnWeekChangeListener(this);
+ mCalendarView.setOnYearViewChangeListener(this);
+
+ //设置日期拦截事件,仅适用单选模式,当前无效
+ mCalendarView.setOnCalendarInterceptListener(this);
+
+ mCalendarView.setOnViewChangeListener(this);
+ mTextYear.setText(String.valueOf(mCalendarView.getCurYear()));
+ mYear = mCalendarView.getCurYear();
+ mTextMonthDay.setText(mCalendarView.getCurMonth() + "月" + mCalendarView.getCurDay() + "日");
+ mTextLunar.setText("今日");
+ mTextCurrentDay.setText(String.valueOf(mCalendarView.getCurDay()));
+ }
+
+ @SuppressWarnings("unused")
+ @Override
+ protected void initData() {
+
+ final int year = mCalendarView.getCurYear();
+ final int month = mCalendarView.getCurMonth();
+
+ Map map = new HashMap<>();
+ for (int y = 1997; y < 2082; y++) {
+ for (int m = 1; m <= 12; m++) {
+
+ map.put(getSchemeCalendar(y, m, 1, 0xFF40db25, "假").toString(),
+ getSchemeCalendar(y, m, 1, 0xFF40db25, "假"));
+
+ map.put(getSchemeCalendar(y, m, 2, 0xFFe69138, "游").toString(),
+ getSchemeCalendar(y, m, 2, 0xFFe69138, "游"));
+ map.put(getSchemeCalendar(y, m, 3, 0xFFdf1356, "事").toString(),
+ getSchemeCalendar(y, m, 3, 0xFFdf1356, "事"));
+ map.put(getSchemeCalendar(y, m, 4, 0xFFaacc44, "车").toString(),
+ getSchemeCalendar(y, m, 4, 0xFFaacc44, "车"));
+ map.put(getSchemeCalendar(y, m, 5, 0xFFbc13f0, "驾").toString(),
+ getSchemeCalendar(y, m, 5, 0xFFbc13f0, "驾"));
+ map.put(getSchemeCalendar(y, m, 6, 0xFF542261, "记").toString(),
+ getSchemeCalendar(y, m, 6, 0xFF542261, "记"));
+ map.put(getSchemeCalendar(y, m, 7, 0xFF4a4bd2, "会").toString(),
+ getSchemeCalendar(y, m, 7, 0xFF4a4bd2, "会"));
+ map.put(getSchemeCalendar(y, m, 8, 0xFFe69138, "车").toString(),
+ getSchemeCalendar(y, m, 8, 0xFFe69138, "车"));
+ map.put(getSchemeCalendar(y, m, 9, 0xFF542261, "考").toString(),
+ getSchemeCalendar(y, m, 9, 0xFF542261, "考"));
+ map.put(getSchemeCalendar(y, m, 10, 0xFF87af5a, "记").toString(),
+ getSchemeCalendar(y, m, 10, 0xFF87af5a, "记"));
+ map.put(getSchemeCalendar(y, m, 11, 0xFF40db25, "会").toString(),
+ getSchemeCalendar(y, m, 11, 0xFF40db25, "会"));
+ map.put(getSchemeCalendar(y, m, 12, 0xFFcda1af, "游").toString(),
+ getSchemeCalendar(y, m, 12, 0xFFcda1af, "游"));
+ map.put(getSchemeCalendar(y, m, 13, 0xFF95af1a, "事").toString(),
+ getSchemeCalendar(y, m, 13, 0xFF95af1a, "事"));
+ map.put(getSchemeCalendar(y, m, 14, 0xFF33aadd, "学").toString(),
+ getSchemeCalendar(y, m, 14, 0xFF33aadd, "学"));
+ map.put(getSchemeCalendar(y, m, 15, 0xFF1aff1a, "码").toString(),
+ getSchemeCalendar(y, m, 15, 0xFF1aff1a, "码"));
+ map.put(getSchemeCalendar(y, m, 16, 0xFF22acaf, "驾").toString(),
+ getSchemeCalendar(y, m, 16, 0xFF22acaf, "驾"));
+ map.put(getSchemeCalendar(y, m, 17, 0xFF99a6fa, "校").toString(),
+ getSchemeCalendar(y, m, 17, 0xFF99a6fa, "校"));
+ map.put(getSchemeCalendar(y, m, 18, 0xFFe69138, "车").toString(),
+ getSchemeCalendar(y, m, 18, 0xFFe69138, "车"));
+ map.put(getSchemeCalendar(y, m, 19, 0xFF40db25, "码").toString(),
+ getSchemeCalendar(y, m, 19, 0xFF40db25, "码"));
+ map.put(getSchemeCalendar(y, m, 20, 0xFFe69138, "火").toString(),
+ getSchemeCalendar(y, m, 20, 0xFFe69138, "火"));
+ map.put(getSchemeCalendar(y, m, 21, 0xFF40db25, "假").toString(),
+ getSchemeCalendar(y, m, 21, 0xFF40db25, "假"));
+ map.put(getSchemeCalendar(y, m, 22, 0xFF99a6fa, "记").toString(),
+ getSchemeCalendar(y, m, 22, 0xFF99a6fa, "记"));
+ map.put(getSchemeCalendar(y, m, 23, 0xFF33aadd, "假").toString(),
+ getSchemeCalendar(y, m, 23, 0xFF33aadd, "假"));
+ map.put(getSchemeCalendar(y, m, 24, 0xFF40db25, "校").toString(),
+ getSchemeCalendar(y, m, 24, 0xFF40db25, "校"));
+ map.put(getSchemeCalendar(y, m, 25, 0xFF1aff1a, "假").toString(),
+ getSchemeCalendar(y, m, 25, 0xFF1aff1a, "假"));
+ map.put(getSchemeCalendar(y, m, 26, 0xFF40db25, "议").toString(),
+ getSchemeCalendar(y, m, 26, 0xFF40db25, "议"));
+ map.put(getSchemeCalendar(y, m, 27, 0xFF95af1a, "假").toString(),
+ getSchemeCalendar(y, m, 27, 0xFF95af1a, "假"));
+ map.put(getSchemeCalendar(y, m, 28, 0xFF40db25, "码").toString(),
+ getSchemeCalendar(y, m, 28, 0xFF40db25, "码"));
+ }
+ }
+
+ //28560 数据量增长不会影响UI响应速度,请使用这个API替换
+ mCalendarView.setSchemeDate(map);
+
+ //可自行测试性能差距
+ //mCalendarView.setSchemeDate(schemes);
+
+ findViewById(R.id.ll_flyme).setOnClickListener(this);
+ findViewById(R.id.ll_simple).setOnClickListener(this);
+ findViewById(R.id.ll_range).setOnClickListener(this);
+ findViewById(R.id.ll_mix).setOnClickListener(this);
+ findViewById(R.id.ll_colorful).setOnClickListener(this);
+ findViewById(R.id.ll_index).setOnClickListener(this);
+ findViewById(R.id.ll_tab).setOnClickListener(this);
+ findViewById(R.id.ll_single).setOnClickListener(this);
+ findViewById(R.id.ll_multi).setOnClickListener(this);
+ findViewById(R.id.ll_solar_system).setOnClickListener(this);
+ findViewById(R.id.ll_progress).setOnClickListener(this);
+ findViewById(R.id.ll_custom).setOnClickListener(this);
+ findViewById(R.id.ll_full).setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case 0:
+ mCalendarView.setWeekStarWithSun();
+ break;
+ case 1:
+ mCalendarView.setWeekStarWithMon();
+ break;
+ case 2:
+ mCalendarView.setWeekStarWithSat();
+ break;
+ case 3:
+ if (mCalendarView.isSingleSelectMode()) {
+ mCalendarView.setSelectDefaultMode();
+ } else {
+ mCalendarView.setSelectSingleMode();
+ }
+ break;
+ case 4:
+ mCalendarView.setWeekView(MeizuWeekView.class);
+ mCalendarView.setMonthView(MeiZuMonthView.class);
+ mCalendarView.setWeekBar(EnglishWeekBar.class);
+ break;
+ case 5:
+ mCalendarView.setAllMode();
+ break;
+ case 6:
+ mCalendarView.setOnlyCurrentMode();
+ break;
+ case 7:
+ mCalendarView.setFixMode();
+ break;
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.ll_flyme:
+ MeiZuActivity.show(this);
+ //CalendarActivity.show(this);
+
+ break;
+ case R.id.ll_custom:
+ CustomActivity.show(this);
+ break;
+ case R.id.ll_mix:
+ MixActivity.show(this);
+ break;
+ case R.id.ll_full:
+ FullActivity.show(this);
+ break;
+ case R.id.ll_range:
+ RangeActivity.show(this);
+ break;
+ case R.id.ll_simple:
+ SimpleActivity.show(this);
+ break;
+ case R.id.ll_colorful:
+ ColorfulActivity.show(this);
+ break;
+ case R.id.ll_index:
+ IndexActivity.show(this);
+ break;
+ case R.id.ll_tab:
+ ViewPagerActivity.show(this);
+ break;
+ case R.id.ll_single:
+ SingleActivity.show(this);
+ break;
+ case R.id.ll_multi:
+ MultiActivity.show(this);
+ break;
+ case R.id.ll_solar_system:
+ SolarActivity.show(this);
+ break;
+ case R.id.ll_progress:
+ ProgressActivity.show(this);
+ break;
+
+ }
+ }
+
+ private Calendar getSchemeCalendar(int year, int month, int day, int color, String text) {
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setSchemeColor(color);//如果单独标记颜色、则会使用这个颜色
+ calendar.setScheme(text);
+ return calendar;
+ }
+
+
+ @Override
+ public void onCalendarOutOfRange(Calendar calendar) {
+ Toast.makeText(this, String.format("%s : OutOfRange", calendar), Toast.LENGTH_SHORT).show();
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onCalendarSelect(Calendar calendar, boolean isClick) {
+ //Log.e("onDateSelected", " -- " + calendar.getYear() + " -- " + calendar.getMonth() + " -- " + calendar.getDay());
+ mTextLunar.setVisibility(View.VISIBLE);
+ mTextYear.setVisibility(View.VISIBLE);
+ mTextMonthDay.setText(calendar.getMonth() + "月" + calendar.getDay() + "日");
+ mTextYear.setText(String.valueOf(calendar.getYear()));
+ mTextLunar.setText(calendar.getLunar());
+ mYear = calendar.getYear();
+ if (isClick) {
+ Toast.makeText(this, getCalendarText(calendar), Toast.LENGTH_SHORT).show();
+ }
+// Log.e("lunar "," -- " + calendar.getLunarCalendar().toString() + "\n" +
+// " -- " + calendar.getLunarCalendar().getYear());
+ Log.e("onDateSelected", " -- " + calendar.getYear() +
+ " -- " + calendar.getMonth() +
+ " -- " + calendar.getDay() +
+ " -- " + isClick + " -- " + calendar.getScheme());
+ Log.e("onDateSelected", " " + mCalendarView.getSelectedCalendar().getScheme() +
+ " -- " + mCalendarView.getSelectedCalendar().isCurrentDay());
+ Log.e("干支年纪 : ", " -- " + TrunkBranchAnnals.getTrunkBranchYear(calendar.getLunarCalendar().getYear()));
+ }
+
+ @Override
+ public void onCalendarLongClickOutOfRange(Calendar calendar) {
+ Toast.makeText(this, String.format("%s : LongClickOutOfRange", calendar), Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onCalendarLongClick(Calendar calendar) {
+ Toast.makeText(this, "长按不选择日期\n" + getCalendarText(calendar), Toast.LENGTH_SHORT).show();
+ }
+
+ private static String getCalendarText(Calendar calendar) {
+ return String.format("新历%s \n 农历%s \n 公历节日:%s \n 农历节日:%s \n 节气:%s \n 是否闰月:%s",
+ calendar.getMonth() + "月" + calendar.getDay() + "日",
+ calendar.getLunarCalendar().getMonth() + "月" + calendar.getLunarCalendar().getDay() + "日",
+ TextUtils.isEmpty(calendar.getGregorianFestival()) ? "无" : calendar.getGregorianFestival(),
+ TextUtils.isEmpty(calendar.getTraditionFestival()) ? "无" : calendar.getTraditionFestival(),
+ TextUtils.isEmpty(calendar.getSolarTerm()) ? "无" : calendar.getSolarTerm(),
+ calendar.getLeapMonth() == 0 ? "否" : String.format("闰%s月", calendar.getLeapMonth()));
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onMonthChange(int year, int month) {
+ Log.e("onMonthChange", " -- " + year + " -- " + month);
+ Calendar calendar = mCalendarView.getSelectedCalendar();
+ mTextLunar.setVisibility(View.VISIBLE);
+ mTextYear.setVisibility(View.VISIBLE);
+ mTextMonthDay.setText(calendar.getMonth() + "月" + calendar.getDay() + "日");
+ mTextYear.setText(String.valueOf(calendar.getYear()));
+ mTextLunar.setText(calendar.getLunar());
+ mYear = calendar.getYear();
+ }
+
+ @Override
+ public void onViewChange(boolean isMonthView) {
+ Log.e("onViewChange", " --- " + (isMonthView ? "月视图" : "周视图"));
+ }
+
+
+ @Override
+ public void onWeekChange(List weekCalendars) {
+ for (Calendar calendar : weekCalendars) {
+ Log.e("onWeekChange", calendar.toString());
+ }
+ }
+
+ @Override
+ public void onYearViewChange(boolean isClose) {
+ Log.e("onYearViewChange", "年视图 -- " + (isClose ? "关闭" : "打开"));
+ }
+
+ /**
+ * 屏蔽某些不可点击的日期,可根据自己的业务自行修改
+ *
+ * @param calendar calendar
+ * @return 是否屏蔽某些不可点击的日期,MonthView和WeekView有类似的API可调用
+ */
+ @Override
+ public boolean onCalendarIntercept(Calendar calendar) {
+ Log.e("onCalendarIntercept", calendar.toString());
+ int day = calendar.getDay();
+ return day == 1 || day == 3 || day == 6 || day == 11 || day == 12 || day == 15 || day == 20 || day == 26;
+ }
+
+ @Override
+ public void onCalendarInterceptClick(Calendar calendar, boolean isClick) {
+ Toast.makeText(this, calendar.toString() + "拦截不可点击", Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onYearChange(int year) {
+ mTextMonthDay.setText(String.valueOf(year));
+ Log.e("onYearChange", " 年份变化 " + year);
+ }
+
+}
+
+
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/TestActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/TestActivity.java
new file mode 100644
index 00000000..645d7fc6
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/TestActivity.java
@@ -0,0 +1,51 @@
+package com.haibin.calendarviewproject;
+
+import android.content.Context;
+import android.content.Intent;
+import android.view.View;
+
+import com.haibin.calendarview.CalendarView;
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+
+/**
+ * 测试界面
+ * Created by huanghaibin on 2018/8/7.
+ */
+
+public class TestActivity extends BaseActivity implements View.OnClickListener {
+
+ private CalendarView mCalendarView;
+ public static void show(Context context) {
+ context.startActivity(new Intent(context, TestActivity.class));
+ }
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_test;
+ }
+
+ @Override
+ protected void initView() {
+ setStatusBarDarkMode();
+ findViewById(R.id.iv_next).setOnClickListener(this);
+ findViewById(R.id.iv_pre).setOnClickListener(this);
+ mCalendarView = findViewById(R.id.calendar_view);
+ }
+
+ @Override
+ protected void initData() {
+
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.iv_next:
+ mCalendarView.scrollToNext(false);
+ break;
+ case R.id.iv_pre:
+ mCalendarView.scrollToPre(false);
+ break;
+ }
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/base/activity/BaseActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/base/activity/BaseActivity.java
new file mode 100644
index 00000000..931d7875
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/base/activity/BaseActivity.java
@@ -0,0 +1,147 @@
+package com.haibin.calendarviewproject.base.activity;
+
+import android.annotation.SuppressLint;
+import android.os.Build;
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * 基类
+ * Created by huanghaibin on 2017/11/16.
+ */
+
+public abstract class BaseActivity extends AppCompatActivity {
+
+ private static boolean isMiUi = false;
+
+ protected void initWindow() {
+
+ }
+
+ protected abstract int getLayoutId();
+
+ protected abstract void initView();
+
+ protected abstract void initData();
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ initWindow();
+ setContentView(getLayoutId());
+ initView();
+ initData();
+ }
+
+ /**
+ * 设置小米黑色状态栏字体
+ */
+ @SuppressLint("PrivateApi")
+ private void setMIUIStatusBarDarkMode() {
+ if (isMiUi) {
+ Class extends Window> clazz = getWindow().getClass();
+ try {
+ int darkModeFlag;
+ Class> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
+ Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
+ darkModeFlag = field.getInt(layoutParams);
+ Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
+ extraFlagField.invoke(getWindow(), darkModeFlag, darkModeFlag);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /*
+ * 静态域,获取系统版本是否基于MIUI
+ */
+
+ static {
+ try {
+ @SuppressLint("PrivateApi") Class> sysClass = Class.forName("android.os.SystemProperties");
+ Method getStringMethod = sysClass.getDeclaredMethod("get", String.class);
+ String version = (String) getStringMethod.invoke(sysClass, "ro.miui.ui.version.name");
+ isMiUi = version.compareTo("V6") >= 0 && Build.VERSION.SDK_INT < 24;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ /**
+ * 设置魅族手机状态栏图标颜色风格
+ * 可以用来判断是否为Flyme用户
+ *
+ * @param window 需要设置的窗口
+ * @param dark 是否把状态栏字体及图标颜色设置为深色
+ * @return boolean 成功执行返回true
+ */
+ @SuppressWarnings("JavaReflectionMemberAccess")
+ public static boolean setMeiZuDarkMode(Window window, boolean dark) {
+ boolean result = false;
+ if (Build.VERSION.SDK_INT >= 24) {
+ return false;
+ }
+ if (window != null) {
+ try {
+ WindowManager.LayoutParams lp = window.getAttributes();
+ Field darkFlag = WindowManager.LayoutParams.class
+ .getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
+ Field meizuFlags = WindowManager.LayoutParams.class
+ .getDeclaredField("meizuFlags");
+ darkFlag.setAccessible(true);
+ meizuFlags.setAccessible(true);
+ int bit = darkFlag.getInt(null);
+ int value = meizuFlags.getInt(lp);
+ if (dark) {
+ value |= bit;
+ } else {
+ value &= ~bit;
+ }
+ meizuFlags.setInt(lp, value);
+ window.setAttributes(lp);
+ result = true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return result;
+ }
+
+ @SuppressLint("InlinedApi")
+ private int getStatusBarLightMode() {
+ int result = 0;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ if (isMiUi) {
+ result = 1;
+ } else if (setMeiZuDarkMode(getWindow(), true)) {
+ result = 2;
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+ result = 3;
+ }
+ }
+ return result;
+ }
+
+
+ @SuppressLint("InlinedApi")
+ protected void setStatusBarDarkMode() {
+ int type = getStatusBarLightMode();
+ if (type == 1) {
+ setMIUIStatusBarDarkMode();
+ } else if (type == 2) {
+ setMeiZuDarkMode(getWindow(), true);
+ } else if (type == 3) {
+ getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+ }
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/base/adapter/BaseRecyclerAdapter.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/base/adapter/BaseRecyclerAdapter.java
new file mode 100644
index 00000000..13d91e98
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/base/adapter/BaseRecyclerAdapter.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarviewproject.base.adapter;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 基本的适配器
+ */
+@SuppressWarnings("unused")
+public abstract class BaseRecyclerAdapter extends RecyclerView.Adapter {
+
+ protected LayoutInflater mInflater;
+ protected List mItems;
+ private OnItemClickListener onItemClickListener;
+ private OnClickListener onClickListener;
+
+ public BaseRecyclerAdapter(Context context) {
+ this.mItems = new ArrayList<>();
+ mInflater = LayoutInflater.from(context);
+ onClickListener = new OnClickListener() {
+ @Override
+ public void onClick(int position, long itemId) {
+ if (onItemClickListener != null)
+ onItemClickListener.onItemClick(position, itemId);
+ }
+ };
+
+ }
+
+ @NonNull
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ final RecyclerView.ViewHolder holder = onCreateDefaultViewHolder(parent, viewType);
+ holder.itemView.setTag(holder);
+ holder.itemView.setOnClickListener(onClickListener);
+ return holder;
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
+ onBindViewHolder(holder, mItems.get(position), position);
+ }
+
+ protected abstract RecyclerView.ViewHolder onCreateDefaultViewHolder(ViewGroup parent, int type);
+
+ protected abstract void onBindViewHolder(RecyclerView.ViewHolder holder, T item, int position);
+
+ @Override
+ public int getItemCount() {
+ return mItems.size();
+ }
+
+ void setOnItemClickListener(OnItemClickListener onItemClickListener) {
+ this.onItemClickListener = onItemClickListener;
+ }
+
+ void addAll(List items) {
+ if (items != null && items.size() > 0) {
+ mItems.addAll(items);
+ notifyItemRangeInserted(mItems.size(), items.size());
+ }
+ }
+
+ final void addItem(T item) {
+ if (item != null) {
+ this.mItems.add(item);
+ notifyItemChanged(mItems.size());
+ }
+ }
+
+ final List getItems() {
+ return mItems;
+ }
+
+
+ final T getItem(int position) {
+ if (position < 0 || position >= mItems.size())
+ return null;
+ return mItems.get(position);
+ }
+
+ static abstract class OnClickListener implements View.OnClickListener {
+ @Override
+ public void onClick(View v) {
+ RecyclerView.ViewHolder holder = (RecyclerView.ViewHolder) v.getTag();
+ onClick(holder.getAdapterPosition(), holder.getItemId());
+ }
+
+ public abstract void onClick(int position, long itemId);
+ }
+
+
+ interface OnItemClickListener {
+ void onItemClick(int position, long itemId);
+ }
+
+ public final void removeItem(T item) {
+ if (this.mItems.contains(item)) {
+ int position = mItems.indexOf(item);
+ this.mItems.remove(item);
+ notifyItemRemoved(position);
+ }
+ }
+
+ protected final void removeItem(int position) {
+ if (this.getItemCount() > position) {
+ this.mItems.remove(position);
+ notifyItemRemoved(position);
+ }
+ }
+
+ protected final void clear(){
+ mItems.clear();
+ notifyDataSetChanged();
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/base/fragment/BaseFragment.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/base/fragment/BaseFragment.java
new file mode 100644
index 00000000..47fff120
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/base/fragment/BaseFragment.java
@@ -0,0 +1,52 @@
+package com.haibin.calendarviewproject.base.fragment;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+
+public abstract class BaseFragment extends Fragment {
+ protected View mRootView;
+ protected LayoutInflater mInflater;
+ protected Context mContext;
+
+ @Override
+ public void onAttach(Context context) {
+ mContext = context;
+ super.onAttach(context);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ if (mRootView != null) {
+ ViewGroup parent = (ViewGroup) mRootView.getParent();
+ if (parent != null)
+ parent.removeView(mRootView);
+ } else {
+ mRootView = inflater.inflate(getLayoutId(), container, false);
+ mInflater = inflater;
+ initView();
+ initData();
+ }
+ return mRootView;
+ }
+
+ @Override
+ public void onDetach() {
+ mContext = null;
+ super.onDetach();
+ }
+
+ protected abstract int getLayoutId();
+
+ protected abstract void initView();
+
+ protected abstract void initData();
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/base/fragment/FragmentAdapter.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/base/fragment/FragmentAdapter.java
new file mode 100644
index 00000000..55a59fc0
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/base/fragment/FragmentAdapter.java
@@ -0,0 +1,94 @@
+package com.haibin.calendarviewproject.base.fragment;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentPagerAdapter;
+import androidx.fragment.app.FragmentTransaction;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SuppressWarnings("unused")
+public class FragmentAdapter extends FragmentPagerAdapter {
+ private List mFragment = new ArrayList<>();
+ private final FragmentManager mFragmentManager;
+ private boolean mUpdateFlag;
+ private Fragment mCurFragment;
+ private String[] mTitles;
+
+ public FragmentAdapter(FragmentManager fm) {
+ super(fm);
+ this.mFragmentManager = fm;
+ }
+
+ public boolean isUpdateFlag() {
+ return mUpdateFlag;
+ }
+
+ public void setUpdateFlag(boolean mUpdateFlag) {
+ this.mUpdateFlag = mUpdateFlag;
+ }
+
+ @Override
+ public int getItemPosition(@NonNull Object object) {
+ return POSITION_NONE;
+ }
+
+
+ @NonNull
+ @Override
+ public Object instantiateItem(@NonNull ViewGroup container, int position) {
+ if (mUpdateFlag) {
+ Fragment fragment = (Fragment) super.instantiateItem(container, position);
+ String tag = fragment.getTag();
+ FragmentTransaction transaction = mFragmentManager.beginTransaction();
+ transaction.remove(fragment);
+ fragment = getItem(position);
+ if (!fragment.isAdded()) {
+ transaction.add(container.getId(), fragment, tag)
+ .attach(fragment)
+ .commitAllowingStateLoss();
+ }
+ return fragment;
+ }
+ return super.instantiateItem(container, position);
+ }
+
+ public void reset(List fragments) {
+ mFragment.clear();
+ mFragment.addAll(fragments);
+ }
+
+ @Override
+ public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
+ super.setPrimaryItem(container, position, object);
+ if (object instanceof Fragment) {
+ mCurFragment = (Fragment) object;
+ }
+ }
+
+ public Fragment getCurFragment() {
+ return mCurFragment;
+ }
+
+ public void reset(String[] titles) {
+ this.mTitles = titles;
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ return mFragment.get(position);
+ }
+
+ @Override
+ public int getCount() {
+ return mFragment.size();
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mTitles[position];
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/colorful/ColorfulActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/colorful/ColorfulActivity.java
new file mode 100644
index 00000000..5ce09b39
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/colorful/ColorfulActivity.java
@@ -0,0 +1,185 @@
+package com.haibin.calendarviewproject.colorful;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarLayout;
+import com.haibin.calendarview.CalendarView;
+import com.haibin.calendarviewproject.Article;
+import com.haibin.calendarviewproject.ArticleAdapter;
+import com.haibin.calendarviewproject.R;
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+import com.haibin.calendarviewproject.group.GroupItemDecoration;
+import com.haibin.calendarviewproject.group.GroupRecyclerView;
+import com.haibin.calendarviewproject.index.IndexActivity;
+import com.haibin.calendarviewproject.meizu.MeiZuActivity;
+import com.haibin.calendarviewproject.simple.SimpleActivity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ColorfulActivity extends BaseActivity implements
+ CalendarView.OnCalendarSelectListener,
+ CalendarView.OnYearChangeListener,
+ View.OnClickListener {
+
+ TextView mTextMonthDay;
+
+ TextView mTextYear;
+
+ TextView mTextLunar;
+
+ TextView mTextCurrentDay;
+
+ CalendarView mCalendarView;
+
+ RelativeLayout mRelativeTool;
+ private int mYear;
+ CalendarLayout mCalendarLayout;
+ GroupRecyclerView mRecyclerView;
+
+ public static void show(Context context) {
+ context.startActivity(new Intent(context, ColorfulActivity.class));
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_colorful;
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void initView() {
+ setStatusBarDarkMode();
+ mTextMonthDay = findViewById(R.id.tv_month_day);
+ mTextYear = findViewById(R.id.tv_year);
+ mTextLunar = findViewById(R.id.tv_lunar);
+ mRelativeTool = findViewById(R.id.rl_tool);
+ mCalendarView = findViewById(R.id.calendarView);
+ mTextCurrentDay = findViewById(R.id.tv_current_day);
+ mTextMonthDay.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!mCalendarLayout.isExpand()) {
+ mCalendarLayout.expand();
+ return;
+ }
+ mCalendarView.showYearSelectLayout(mYear);
+ mTextLunar.setVisibility(View.GONE);
+ mTextYear.setVisibility(View.GONE);
+ mTextMonthDay.setText(String.valueOf(mYear));
+ }
+ });
+ findViewById(R.id.fl_current).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCalendarView.scrollToCurrent();
+ }
+ });
+ mCalendarLayout = findViewById(R.id.calendarLayout);
+ mCalendarView.setOnCalendarSelectListener(this);
+ mCalendarView.setOnYearChangeListener(this);
+ mTextYear.setText(String.valueOf(mCalendarView.getCurYear()));
+ mYear = mCalendarView.getCurYear();
+ mTextMonthDay.setText(mCalendarView.getCurMonth() + "月" + mCalendarView.getCurDay() + "日");
+ mTextLunar.setText("今日");
+ mTextCurrentDay.setText(String.valueOf(mCalendarView.getCurDay()));
+ }
+
+ @Override
+ protected void initData() {
+
+ int year = mCalendarView.getCurYear();
+ int month = mCalendarView.getCurMonth();
+
+ Map map = new HashMap<>();
+ map.put(getSchemeCalendar(year, month, 3, 0xFF40db25, "假").toString(),
+ getSchemeCalendar(year, month, 3, 0xFF40db25, "假"));
+ map.put(getSchemeCalendar(year, month, 6, 0xFFe69138, "事").toString(),
+ getSchemeCalendar(year, month, 6, 0xFFe69138, "事"));
+ map.put(getSchemeCalendar(year, month, 9, 0xFFdf1356, "议").toString(),
+ getSchemeCalendar(year, month, 9, 0xFFdf1356, "议"));
+ map.put(getSchemeCalendar(year, month, 13, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 13, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 14, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 14, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 15, 0xFFaacc44, "假").toString(),
+ getSchemeCalendar(year, month, 15, 0xFFaacc44, "假"));
+ map.put(getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记").toString(),
+ getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记"));
+ map.put(getSchemeCalendar(year, month, 25, 0xFF13acf0, "假").toString(),
+ getSchemeCalendar(year, month, 25, 0xFF13acf0, "假"));
+ map.put(getSchemeCalendar(year, month, 27, 0xFF13acf0, "多").toString(),
+ getSchemeCalendar(year, month, 27, 0xFF13acf0, "多"));
+ //此方法在巨大的数据量上不影响遍历性能,推荐使用
+ mCalendarView.setSchemeDate(map);
+
+
+ mRecyclerView = findViewById(R.id.recyclerView);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mRecyclerView.addItemDecoration(new GroupItemDecoration());
+ mRecyclerView.setAdapter(new ArticleAdapter(this));
+ mRecyclerView.notifyDataSetChanged();
+ }
+
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.ll_flyme:
+ MeiZuActivity.show(this);
+ break;
+ case R.id.ll_simple:
+ SimpleActivity.show(this);
+ break;
+ case R.id.ll_colorful:
+ ColorfulActivity.show(this);
+ break;
+ case R.id.ll_index:
+ IndexActivity.show(this);
+ break;
+ }
+ }
+
+ private Calendar getSchemeCalendar(int year, int month, int day, int color, String text) {
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setSchemeColor(color);//如果单独标记颜色、则会使用这个颜色
+ calendar.setScheme(text);
+ return calendar;
+ }
+
+
+
+ @Override
+ public void onCalendarOutOfRange(Calendar calendar) {
+
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onCalendarSelect(Calendar calendar, boolean isClick) {
+ mTextLunar.setVisibility(View.VISIBLE);
+ mTextYear.setVisibility(View.VISIBLE);
+ mTextMonthDay.setText(calendar.getMonth() + "月" + calendar.getDay() + "日");
+ mTextYear.setText(String.valueOf(calendar.getYear()));
+ mTextLunar.setText(calendar.getLunar());
+ mYear = calendar.getYear();
+ }
+
+ @Override
+ public void onYearChange(int year) {
+ mTextMonthDay.setText(String.valueOf(year));
+ }
+
+
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/colorful/ColorfulMonthView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/colorful/ColorfulMonthView.java
new file mode 100644
index 00000000..b62e1715
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/colorful/ColorfulMonthView.java
@@ -0,0 +1,76 @@
+package com.haibin.calendarviewproject.colorful;
+
+import android.content.Context;
+import android.graphics.Canvas;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.MonthView;
+
+/**
+ * 高仿魅族日历布局
+ * Created by huanghaibin on 2017/11/15.
+ */
+
+public class ColorfulMonthView extends MonthView {
+
+ private int mRadius;
+
+ public ColorfulMonthView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onPreviewHook() {
+ mRadius = Math.min(mItemWidth, mItemHeight) / 5 * 2;
+ }
+
+ /**
+ * 如果需要点击Scheme没有效果,则return true
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @return false 则不绘制onDrawScheme,因为这里背景色是互斥的
+ */
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ return true;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSchemePaint);
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int top = y - mItemHeight / 8;
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint : mSelectTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint : mSelectedLunarTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mSchemeLunarTextPaint);
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint : mCurMonthLunarTextPaint);
+ }
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/colorful/ColorfulWeekView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/colorful/ColorfulWeekView.java
new file mode 100644
index 00000000..ccdad338
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/colorful/ColorfulWeekView.java
@@ -0,0 +1,85 @@
+package com.haibin.calendarviewproject.colorful;
+
+import android.content.Context;
+import android.graphics.BlurMaskFilter;
+import android.graphics.Canvas;
+import android.view.View;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.WeekView;
+
+/**
+ * 多彩周视图
+ * Created by huanghaibin on 2017/11/29.
+ */
+
+public class ColorfulWeekView extends WeekView {
+
+ private int mRadius;
+
+ public ColorfulWeekView(Context context) {
+ super(context);
+// //兼容硬件加速无效的代码
+// setLayerType(View.LAYER_TYPE_SOFTWARE, mSelectedPaint);
+// //4.0以上硬件加速会导致无效
+// mSelectedPaint.setMaskFilter(new BlurMaskFilter(30, BlurMaskFilter.Blur.SOLID));
+//
+// setLayerType(View.LAYER_TYPE_SOFTWARE, mSchemePaint);
+// mSchemePaint.setMaskFilter(new BlurMaskFilter(30, BlurMaskFilter.Blur.SOLID));
+ }
+
+ @Override
+ protected void onPreviewHook() {
+ mRadius = Math.min(mItemWidth, mItemHeight) / 5 * 2;
+ }
+
+ /**
+ * 如果需要点击Scheme没有效果,则return true
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @return false 则不绘制onDrawScheme,因为这里背景色是互斥的
+ */
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ return true;
+ }
+
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSchemePaint);
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int top = -mItemHeight / 8;
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint : mSelectTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint : mSelectedLunarTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mSchemeTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10, mSchemeLunarTextPaint);
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mCurMonthTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint : mCurMonthLunarTextPaint);
+ }
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomActivity.java
new file mode 100644
index 00000000..30611cda
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomActivity.java
@@ -0,0 +1,204 @@
+package com.haibin.calendarviewproject.custom;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import android.util.Log;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarLayout;
+import com.haibin.calendarview.CalendarView;
+import com.haibin.calendarviewproject.Article;
+import com.haibin.calendarviewproject.ArticleAdapter;
+import com.haibin.calendarviewproject.R;
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+import com.haibin.calendarviewproject.colorful.ColorfulActivity;
+import com.haibin.calendarviewproject.group.GroupItemDecoration;
+import com.haibin.calendarviewproject.group.GroupRecyclerView;
+import com.haibin.calendarviewproject.index.IndexActivity;
+import com.haibin.calendarviewproject.simple.SimpleActivity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class CustomActivity extends BaseActivity implements
+ CalendarView.OnCalendarSelectListener,
+ CalendarView.OnYearChangeListener,
+ View.OnClickListener {
+
+ TextView mTextMonthDay;
+
+ TextView mTextYear;
+
+ TextView mTextLunar;
+
+ TextView mTextCurrentDay;
+
+ CalendarView mCalendarView;
+
+ RelativeLayout mRelativeTool;
+ private int mYear;
+ CalendarLayout mCalendarLayout;
+ GroupRecyclerView mRecyclerView;
+
+ public static void show(Context context) {
+ context.startActivity(new Intent(context, CustomActivity.class));
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_custom;
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void initView() {
+ setStatusBarDarkMode();
+ mTextMonthDay = findViewById(R.id.tv_month_day);
+ mTextYear = findViewById(R.id.tv_year);
+ mTextLunar = findViewById(R.id.tv_lunar);
+ mRelativeTool = findViewById(R.id.rl_tool);
+ mCalendarView = findViewById(R.id.calendarView);
+ mTextCurrentDay = findViewById(R.id.tv_current_day);
+ mTextMonthDay.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!mCalendarLayout.isExpand()) {
+ mCalendarLayout.expand();
+ return;
+ }
+ mCalendarView.showYearSelectLayout(mYear);
+ mTextLunar.setVisibility(View.GONE);
+ mTextYear.setVisibility(View.GONE);
+ mTextMonthDay.setText(String.valueOf(mYear));
+ }
+ });
+ findViewById(R.id.fl_current).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCalendarView.scrollToCurrent();
+ //mCalendarView.addSchemeDate(getSchemeCalendar(2019, 6, 1, 0xFF40db25, "假"));
+// int year = 2019;
+// int month = 6;
+// Map map = new HashMap<>();
+// map.put(getSchemeCalendar(year, month, 3, 0xFF40db25, "假").toString(),
+// getSchemeCalendar(year, month, 3, 0xFF40db25, "假"));
+// map.put(getSchemeCalendar(year, month, 6, 0xFFe69138, "事").toString(),
+// getSchemeCalendar(year, month, 6, 0xFFe69138, "事"));
+// map.put(getSchemeCalendar(year, month, 9, 0xFFdf1356, "议").toString(),
+// getSchemeCalendar(year, month, 9, 0xFFdf1356, "议"));
+// map.put(getSchemeCalendar(year, month, 13, 0xFFedc56d, "记").toString(),
+// getSchemeCalendar(year, month, 13, 0xFFedc56d, "记"));
+// mCalendarView.addSchemeDate(map);
+ }
+ });
+ mCalendarLayout = findViewById(R.id.calendarLayout);
+ mCalendarView.setOnCalendarSelectListener(this);
+ mCalendarView.setOnYearChangeListener(this);
+ mTextYear.setText(String.valueOf(mCalendarView.getCurYear()));
+ mYear = mCalendarView.getCurYear();
+ mTextMonthDay.setText(mCalendarView.getCurMonth() + "月" + mCalendarView.getCurDay() + "日");
+ mTextLunar.setText("今日");
+ mTextCurrentDay.setText(String.valueOf(mCalendarView.getCurDay()));
+ }
+
+ @Override
+ protected void initData() {
+ int year = mCalendarView.getCurYear();
+ int month = mCalendarView.getCurMonth();
+
+ Map map = new HashMap<>();
+ map.put(getSchemeCalendar(year, month, 3, 0xFF40db25, "假").toString(),
+ getSchemeCalendar(year, month, 3, 0xFF40db25, "假"));
+ map.put(getSchemeCalendar(year, month, 6, 0xFFe69138, "事").toString(),
+ getSchemeCalendar(year, month, 6, 0xFFe69138, "事"));
+ map.put(getSchemeCalendar(year, month, 9, 0xFFdf1356, "议").toString(),
+ getSchemeCalendar(year, month, 9, 0xFFdf1356, "议"));
+ map.put(getSchemeCalendar(year, month, 13, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 13, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 14, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 14, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 15, 0xFFaacc44, "假").toString(),
+ getSchemeCalendar(year, month, 15, 0xFFaacc44, "假"));
+ map.put(getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记").toString(),
+ getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记"));
+ map.put(getSchemeCalendar(year, month, 25, 0xFF13acf0, "假").toString(),
+ getSchemeCalendar(year, month, 25, 0xFF13acf0, "假"));
+ map.put(getSchemeCalendar(year, month, 27, 0xFF13acf0, "多").toString(),
+ getSchemeCalendar(year, month, 27, 0xFF13acf0, "多"));
+ //此方法在巨大的数据量上不影响遍历性能,推荐使用
+ mCalendarView.setSchemeDate(map);
+
+
+ mRecyclerView = findViewById(R.id.recyclerView);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mRecyclerView.addItemDecoration(new GroupItemDecoration());
+ mRecyclerView.setAdapter(new ArticleAdapter(this));
+ mRecyclerView.notifyDataSetChanged();
+ }
+
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.ll_flyme:
+ CustomActivity.show(this);
+ break;
+ case R.id.ll_simple:
+ SimpleActivity.show(this);
+ break;
+ case R.id.ll_colorful:
+ ColorfulActivity.show(this);
+ break;
+ case R.id.ll_index:
+ IndexActivity.show(this);
+ break;
+ }
+ }
+
+ private Calendar getSchemeCalendar(int year, int month, int day, int color, String text) {
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setSchemeColor(color);//如果单独标记颜色、则会使用这个颜色
+ calendar.setScheme(text);
+ calendar.addScheme(new Calendar.Scheme());
+ calendar.addScheme(0xFF008800, "假");
+ calendar.addScheme(0xFF008800, "节");
+ return calendar;
+ }
+
+ @Override
+ public void onCalendarOutOfRange(Calendar calendar) {
+
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onCalendarSelect(Calendar calendar, boolean isClick) {
+ mTextLunar.setVisibility(View.VISIBLE);
+ mTextYear.setVisibility(View.VISIBLE);
+ mTextMonthDay.setText(calendar.getMonth() + "月" + calendar.getDay() + "日");
+ mTextYear.setText(String.valueOf(calendar.getYear()));
+ mTextLunar.setText(calendar.getLunar());
+ mYear = calendar.getYear();
+
+ Log.e("onDateSelected", " -- " + calendar.getYear() +
+ " -- " + calendar.getMonth() +
+ " -- " + calendar.getDay() +
+ " -- " + isClick + " -- " + calendar.getScheme());
+ }
+
+ @Override
+ public void onYearChange(int year) {
+ mTextMonthDay.setText(String.valueOf(year));
+ }
+
+
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomMonthView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomMonthView.java
new file mode 100644
index 00000000..1780dc5e
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomMonthView.java
@@ -0,0 +1,194 @@
+package com.haibin.calendarviewproject.custom;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.text.TextUtils;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.MonthView;
+
+/**
+ * 演示一个变态需求的月视图
+ * Created by huanghaibin on 2018/2/9.
+ */
+
+public class CustomMonthView extends MonthView {
+
+ private int mRadius;
+
+ /**
+ * 自定义魅族标记的文本画笔
+ */
+ private Paint mTextPaint = new Paint();
+
+
+ /**
+ * 24节气画笔
+ */
+ private Paint mSolarTermTextPaint = new Paint();
+
+ /**
+ * 背景圆点
+ */
+ private Paint mPointPaint = new Paint();
+
+ /**
+ * 今天的背景色
+ */
+ private Paint mCurrentDayPaint = new Paint();
+
+ /**
+ * 圆点半径
+ */
+ private float mPointRadius;
+
+ private int mPadding;
+
+ private float mCircleRadius;
+ /**
+ * 自定义魅族标记的圆形背景
+ */
+ private Paint mSchemeBasicPaint = new Paint();
+
+ private float mSchemeBaseLine;
+
+ public CustomMonthView(Context context) {
+ super(context);
+
+ mTextPaint.setTextSize(dipToPx(context, 8));
+ mTextPaint.setColor(0xffffffff);
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setFakeBoldText(true);
+
+
+ mSolarTermTextPaint.setColor(0xff489dff);
+ mSolarTermTextPaint.setAntiAlias(true);
+ mSolarTermTextPaint.setTextAlign(Paint.Align.CENTER);
+
+ mSchemeBasicPaint.setAntiAlias(true);
+ mSchemeBasicPaint.setStyle(Paint.Style.FILL);
+ mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);
+ mSchemeBasicPaint.setFakeBoldText(true);
+ mSchemeBasicPaint.setColor(Color.WHITE);
+
+
+ mCurrentDayPaint.setAntiAlias(true);
+ mCurrentDayPaint.setStyle(Paint.Style.FILL);
+ mCurrentDayPaint.setColor(0xFFeaeaea);
+
+ mPointPaint.setAntiAlias(true);
+ mPointPaint.setStyle(Paint.Style.FILL);
+ mPointPaint.setTextAlign(Paint.Align.CENTER);
+ mPointPaint.setColor(Color.RED);
+
+ mCircleRadius = dipToPx(getContext(), 7);
+
+ mPadding = dipToPx(getContext(), 3);
+
+ mPointRadius = dipToPx(context, 2);
+
+ Paint.FontMetrics metrics = mSchemeBasicPaint.getFontMetrics();
+ mSchemeBaseLine = mCircleRadius - metrics.descent + (metrics.bottom - metrics.top) / 2 + dipToPx(getContext(), 1);
+ }
+
+
+ @Override
+ protected void onPreviewHook() {
+ mSolarTermTextPaint.setTextSize(mCurMonthLunarTextPaint.getTextSize());
+ mRadius = Math.min(mItemWidth, mItemHeight) / 11 * 5;
+ }
+
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ return true;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+
+ boolean isSelected = isSelected(calendar);
+ if (isSelected) {
+ mPointPaint.setColor(Color.WHITE);
+ } else {
+ mPointPaint.setColor(Color.GRAY);
+ }
+
+ canvas.drawCircle(x + mItemWidth / 2, y + mItemHeight - 3 * mPadding, mPointRadius, mPointPaint);
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ int top = y - mItemHeight / 6;
+
+ if (calendar.isCurrentDay() && !isSelected) {
+ canvas.drawCircle(cx, cy, mRadius, mCurrentDayPaint);
+ }
+
+ if (hasScheme) {
+ canvas.drawCircle(x + mItemWidth - mPadding - mCircleRadius / 2, y + mPadding + mCircleRadius, mCircleRadius, mSchemeBasicPaint);
+ mTextPaint.setColor(calendar.getSchemeColor());
+ canvas.drawText(calendar.getScheme(), x + mItemWidth - mPadding - mCircleRadius, y + mPadding + mSchemeBaseLine, mTextPaint);
+ }
+
+ //当然可以换成其它对应的画笔就不麻烦,
+ if (calendar.isWeekend() && calendar.isCurrentMonth()) {
+ mCurMonthTextPaint.setColor(0xFF489dff);
+ mCurMonthLunarTextPaint.setColor(0xFF489dff);
+ mSchemeTextPaint.setColor(0xFF489dff);
+ mSchemeLunarTextPaint.setColor(0xFF489dff);
+ mOtherMonthLunarTextPaint.setColor(0xFF489dff);
+ mOtherMonthTextPaint.setColor(0xFF489dff);
+ } else {
+ mCurMonthTextPaint.setColor(0xff333333);
+ mCurMonthLunarTextPaint.setColor(0xffCFCFCF);
+ mSchemeTextPaint.setColor(0xff333333);
+ mSchemeLunarTextPaint.setColor(0xffCFCFCF);
+
+ mOtherMonthTextPaint.setColor(0xFFe1e1e1);
+ mOtherMonthLunarTextPaint.setColor(0xFFe1e1e1);
+ }
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ mSelectTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mSelectedLunarTextPaint);
+ } else if (hasScheme) {
+
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10,
+ !TextUtils.isEmpty(calendar.getSolarTerm()) ? mSolarTermTextPaint : mSchemeLunarTextPaint);
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint :
+ calendar.isCurrentMonth() ? !TextUtils.isEmpty(calendar.getSolarTerm()) ? mSolarTermTextPaint :
+ mCurMonthLunarTextPaint : mOtherMonthLunarTextPaint);
+ }
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomWeekBar.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomWeekBar.java
new file mode 100644
index 00000000..3c78fd5f
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomWeekBar.java
@@ -0,0 +1,64 @@
+package com.haibin.calendarviewproject.custom;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.view.LayoutInflater;
+import android.widget.TextView;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.WeekBar;
+import com.haibin.calendarviewproject.R;
+
+/**
+ * 自定义英文栏
+ * Created by huanghaibin on 2017/11/30.
+ */
+
+public class CustomWeekBar extends WeekBar {
+
+ private int mPreSelectedIndex;
+
+ public CustomWeekBar(Context context) {
+ super(context);
+ LayoutInflater.from(context).inflate(R.layout.custom_week_bar, this, true);
+ setBackgroundColor(Color.WHITE);
+ }
+
+ @Override
+ protected void onDateSelected(Calendar calendar, int weekStart, boolean isClick) {
+ getChildAt(mPreSelectedIndex).setSelected(false);
+ int viewIndex = getViewIndexByCalendar(calendar, weekStart);
+ getChildAt(viewIndex).setSelected(true);
+ mPreSelectedIndex = viewIndex;
+ }
+
+ /**
+ * 当周起始发生变化,使用自定义布局需要重写这个方法,避免出问题
+ *
+ * @param weekStart 周起始
+ */
+ @Override
+ protected void onWeekStartChange(int weekStart) {
+ for (int i = 0; i < getChildCount(); i++) {
+ ((TextView) getChildAt(i)).setText(getWeekString(i, weekStart));
+ }
+ }
+
+ /**
+ * 或者周文本,这个方法仅供父类使用
+ * @param index index
+ * @param weekStart weekStart
+ * @return 或者周文本
+ */
+ private String getWeekString(int index, int weekStart) {
+ String[] weeks = getContext().getResources().getStringArray(R.array.chinese_week_string_array);
+
+ if (weekStart == 1) {
+ return weeks[index];
+ }
+ if (weekStart == 2) {
+ return weeks[index == 6 ? 0 : index + 1];
+ }
+ return weeks[index == 0 ? 6 : index - 1];
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomWeekView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomWeekView.java
new file mode 100644
index 00000000..2d3735e9
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomWeekView.java
@@ -0,0 +1,200 @@
+package com.haibin.calendarviewproject.custom;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.text.TextUtils;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.WeekView;
+
+/**
+ * 演示一个变态需求的周视图
+ * Created by huanghaibin on 2018/2/9.
+ */
+
+public class CustomWeekView extends WeekView {
+
+
+ private int mRadius;
+
+ /**
+ * 自定义魅族标记的文本画笔
+ */
+ private Paint mTextPaint = new Paint();
+
+
+ /**
+ * 24节气画笔
+ */
+ private Paint mSolarTermTextPaint = new Paint();
+
+ /**
+ * 背景圆点
+ */
+ private Paint mPointPaint = new Paint();
+
+ /**
+ * 今天的背景色
+ */
+ private Paint mCurrentDayPaint = new Paint();
+
+
+ /**
+ * 圆点半径
+ */
+ private float mPointRadius;
+
+ private int mPadding;
+
+ private float mCircleRadius;
+ /**
+ * 自定义魅族标记的圆形背景
+ */
+ private Paint mSchemeBasicPaint = new Paint();
+
+ private float mSchemeBaseLine;
+
+ public CustomWeekView(Context context) {
+ super(context);
+ mTextPaint.setTextSize(dipToPx(context, 8));
+ mTextPaint.setColor(0xffffffff);
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setFakeBoldText(true);
+
+
+ mSolarTermTextPaint.setColor(0xff489dff);
+ mSolarTermTextPaint.setAntiAlias(true);
+ mSolarTermTextPaint.setTextAlign(Paint.Align.CENTER);
+
+ mSchemeBasicPaint.setAntiAlias(true);
+ mSchemeBasicPaint.setStyle(Paint.Style.FILL);
+ mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);
+ mSchemeBasicPaint.setFakeBoldText(true);
+ mSchemeBasicPaint.setColor(Color.WHITE);
+
+ mPointPaint.setAntiAlias(true);
+ mPointPaint.setStyle(Paint.Style.FILL);
+ mPointPaint.setTextAlign(Paint.Align.CENTER);
+ mPointPaint.setColor(Color.RED);
+
+
+ mCurrentDayPaint.setAntiAlias(true);
+ mCurrentDayPaint.setStyle(Paint.Style.FILL);
+ mCurrentDayPaint.setColor(0xFFeaeaea);
+
+
+ mCircleRadius = dipToPx(getContext(), 7);
+
+ mPadding = dipToPx(getContext(), 3);
+
+ mPointRadius = dipToPx(context, 2);
+
+ Paint.FontMetrics metrics = mSchemeBasicPaint.getFontMetrics();
+ mSchemeBaseLine = mCircleRadius - metrics.descent + (metrics.bottom - metrics.top) / 2 + dipToPx(getContext(), 1);
+
+ }
+
+
+ @Override
+ protected void onPreviewHook() {
+ mSolarTermTextPaint.setTextSize(mCurMonthLunarTextPaint.getTextSize());
+ mRadius = Math.min(mItemWidth, mItemHeight) / 11 * 5;
+ }
+
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ return true;
+ }
+
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x) {
+
+ boolean isSelected = isSelected(calendar);
+ if (isSelected) {
+ mPointPaint.setColor(Color.WHITE);
+ } else {
+ mPointPaint.setColor(Color.GRAY);
+ }
+
+ canvas.drawCircle(x + mItemWidth / 2, mItemHeight - 3 * mPadding, mPointRadius, mPointPaint);
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+ int top = -mItemHeight / 6;
+
+ if (calendar.isCurrentDay() && !isSelected) {
+ canvas.drawCircle(cx, cy, mRadius, mCurrentDayPaint);
+ }
+
+ if(hasScheme){
+ canvas.drawCircle(x + mItemWidth - mPadding - mCircleRadius / 2, mPadding + mCircleRadius, mCircleRadius, mSchemeBasicPaint);
+
+ mTextPaint.setColor(calendar.getSchemeColor());
+
+ canvas.drawText(calendar.getScheme(), x + mItemWidth - mPadding - mCircleRadius, mPadding + mSchemeBaseLine, mTextPaint);
+ }
+
+ if (calendar.isWeekend() && calendar.isCurrentMonth()) {
+ mCurMonthTextPaint.setColor(0xFF489dff);
+ mCurMonthLunarTextPaint.setColor(0xFF489dff);
+ mSchemeTextPaint.setColor(0xFF489dff);
+ mSchemeLunarTextPaint.setColor(0xFF489dff);
+ mOtherMonthLunarTextPaint.setColor(0xFF489dff);
+ mOtherMonthTextPaint.setColor(0xFF489dff);
+ } else {
+ mCurMonthTextPaint.setColor(0xff333333);
+ mCurMonthLunarTextPaint.setColor(0xffCFCFCF);
+ mSchemeTextPaint.setColor(0xff333333);
+ mSchemeLunarTextPaint.setColor(0xffCFCFCF);
+
+ mOtherMonthTextPaint.setColor(0xFFe1e1e1);
+ mOtherMonthLunarTextPaint.setColor(0xFFe1e1e1);
+ }
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ mSelectTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10, mSelectedLunarTextPaint);
+ } else if (hasScheme) {
+
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10,
+ !TextUtils.isEmpty(calendar.getSolarTerm()) ? mSolarTermTextPaint : mSchemeLunarTextPaint);
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint :
+ !TextUtils.isEmpty(calendar.getSolarTerm()) ? mSolarTermTextPaint :
+ calendar.isCurrentMonth() ?
+ mCurMonthLunarTextPaint : mOtherMonthLunarTextPaint);
+ }
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomYearView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomYearView.java
new file mode 100644
index 00000000..38fa8fc5
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/custom/CustomYearView.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarviewproject.custom;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.YearView;
+
+/**
+ * 自定义年视图
+ * Created by huanghaibin on 2018/10/9.
+ */
+@SuppressWarnings("unused")
+public class CustomYearView extends YearView {
+
+ private int mTextPadding;
+ /**
+ * 闰年字体
+ */
+ private Paint mLeapYearTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+ public CustomYearView(Context context) {
+ super(context);
+ mTextPadding = dipToPx(context, 3);
+
+ mLeapYearTextPaint.setTextSize(dipToPx(context, 12));
+ mLeapYearTextPaint.setColor(0xffd1d1d1);
+ mLeapYearTextPaint.setAntiAlias(true);
+ mLeapYearTextPaint.setFakeBoldText(true);
+
+ }
+
+ @Override
+ protected void onDrawMonth(Canvas canvas, int year, int month, int x, int y, int width, int height) {
+
+ String text = getContext()
+ .getResources()
+ .getStringArray(com.haibin.calendarview.R.array.month_string_array)[month - 1];
+ canvas.drawText(text,
+ x + mItemWidth / 2 - mTextPadding,
+ y + mMonthTextBaseLine,
+ mMonthTextPaint);
+ if (month == 2 && isLeapYear(year)) {
+ float w = getTextWidth(mMonthTextPaint, text);
+
+ canvas.drawText("闰年",
+ x + mItemWidth / 2 - mTextPadding + w + dipToPx(getContext(), 6),
+ y + mMonthTextBaseLine,
+ mLeapYearTextPaint);
+ }
+ }
+
+ private float getTextWidth(Paint paint, String text) {
+ return paint.measureText(text);
+ }
+
+ /**
+ * 是否是闰年
+ *
+ * @param year year
+ * @return 是否是闰年
+ */
+ private static boolean isLeapYear(int year) {
+ return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
+ }
+
+
+
+ @Override
+ protected void onDrawWeek(Canvas canvas, int week, int x, int y, int width, int height) {
+ String text = getContext().getResources().getStringArray(com.haibin.calendarview.R.array.year_view_week_string_array)[week];
+ canvas.drawText(text,
+ x + width / 2,
+ y + mWeekTextBaseLine,
+ mWeekTextPaint);
+ }
+
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ int radius = Math.min(mItemWidth, mItemHeight) / 8 * 5;
+ canvas.drawCircle(cx, cy, radius, mSelectedPaint);
+ return true;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+
+ }
+
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine + y;
+ int cx = x + mItemWidth / 2;
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ hasScheme ? mSchemeTextPaint : mSelectTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint : mSchemeTextPaint);
+
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint : mCurMonthTextPaint);
+ }
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/full/FullActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/full/FullActivity.java
new file mode 100644
index 00000000..7702da62
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/full/FullActivity.java
@@ -0,0 +1,175 @@
+package com.haibin.calendarviewproject.full;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarView;
+import com.haibin.calendarviewproject.R;
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+import com.haibin.calendarviewproject.colorful.ColorfulActivity;
+import com.haibin.calendarviewproject.custom.CustomActivity;
+import com.haibin.calendarviewproject.index.IndexActivity;
+import com.haibin.calendarviewproject.simple.SimpleActivity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 全屏日历,因为月份大小不同的关系,需要 app:month_view_show_mode="mode_all",
+ * 一般地,既然使用全屏日历,那也不必要
+ * Created by huanghaibin on 2019/3/7.
+ */
+
+public class FullActivity extends BaseActivity implements
+ CalendarView.OnCalendarSelectListener,
+ CalendarView.OnYearChangeListener,
+ View.OnClickListener {
+
+ TextView mTextMonthDay;
+
+ TextView mTextYear;
+
+ TextView mTextLunar;
+
+ TextView mTextCurrentDay;
+
+ private int mYear;
+
+ CalendarView mCalendarView;
+
+ public static void show(Context context) {
+ context.startActivity(new Intent(context, FullActivity.class));
+ }
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_full;
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void initView() {
+
+ setStatusBarDarkMode();
+ mTextMonthDay = findViewById(R.id.tv_month_day);
+ mTextYear = findViewById(R.id.tv_year);
+ mTextLunar = findViewById(R.id.tv_lunar);
+ mTextCurrentDay = findViewById(R.id.tv_current_day);
+ mCalendarView = findViewById(R.id.calendarView);
+ mTextMonthDay.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCalendarView.showYearSelectLayout(mYear);
+ mTextLunar.setVisibility(View.GONE);
+ mTextYear.setVisibility(View.GONE);
+ mTextMonthDay.setText(String.valueOf(mYear));
+ }
+ });
+ findViewById(R.id.fl_current).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCalendarView.scrollToCurrent();
+ }
+ });
+
+ mCalendarView.setOnCalendarSelectListener(this);
+ mCalendarView.setOnYearChangeListener(this);
+ mTextYear.setText(String.valueOf(mCalendarView.getCurYear()));
+ mYear = mCalendarView.getCurYear();
+ mTextMonthDay.setText(mCalendarView.getCurMonth() + "月" + mCalendarView.getCurDay() + "日");
+ mTextLunar.setText("今日");
+ mTextCurrentDay.setText(String.valueOf(mCalendarView.getCurDay()));
+ }
+
+ @Override
+ protected void initData() {
+ int year = mCalendarView.getCurYear();
+ int month = mCalendarView.getCurMonth();
+
+ Map map = new HashMap<>();
+ map.put(getSchemeCalendar(year, month, 3, 0xFF40db25, "假").toString(),
+ getSchemeCalendar(year, month, 3, 0xFF40db25, "假"));
+ map.put(getSchemeCalendar(year, month, 6, 0xFFe69138, "事").toString(),
+ getSchemeCalendar(year, month, 6, 0xFFe69138, "事"));
+ map.put(getSchemeCalendar(year, month, 9, 0xFFdf1356, "议").toString(),
+ getSchemeCalendar(year, month, 9, 0xFFdf1356, "议"));
+ map.put(getSchemeCalendar(year, month, 13, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 13, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 14, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 14, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 15, 0xFFaacc44, "假").toString(),
+ getSchemeCalendar(year, month, 15, 0xFFaacc44, "假"));
+ map.put(getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记").toString(),
+ getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记"));
+ map.put(getSchemeCalendar(year, month, 22, 0xFFdf1356, "议").toString(),
+ getSchemeCalendar(year, month, 22, 0xFFdf1356, "议"));
+ map.put(getSchemeCalendar(year, month, 25, 0xFF13acf0, "假").toString(),
+ getSchemeCalendar(year, month, 25, 0xFF13acf0, "假"));
+ map.put(getSchemeCalendar(year, month, 27, 0xFF13acf0, "多").toString(),
+ getSchemeCalendar(year, month, 27, 0xFF13acf0, "多"));
+ //此方法在巨大的数据量上不影响遍历性能,推荐使用
+ mCalendarView.setSchemeDate(map);
+ }
+
+ private Calendar getSchemeCalendar(int year, int month, int day, int color, String text) {
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setSchemeColor(color);//如果单独标记颜色、则会使用这个颜色
+ calendar.setScheme(text);
+ calendar.addScheme(color, "假");
+ calendar.addScheme(day%2 == 0 ? 0xFF00CD00 : 0xFFD15FEE, "节");
+ calendar.addScheme(day%2 == 0 ? 0xFF660000 : 0xFF4169E1, "记");
+ return calendar;
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.ll_flyme:
+ CustomActivity.show(this);
+ break;
+ case R.id.ll_simple:
+ SimpleActivity.show(this);
+ break;
+ case R.id.ll_colorful:
+ ColorfulActivity.show(this);
+ break;
+ case R.id.ll_index:
+ IndexActivity.show(this);
+ break;
+ }
+ }
+
+ @Override
+ public void onCalendarOutOfRange(Calendar calendar) {
+
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onCalendarSelect(Calendar calendar, boolean isClick) {
+ mTextLunar.setVisibility(View.VISIBLE);
+ mTextYear.setVisibility(View.VISIBLE);
+ mTextMonthDay.setText(calendar.getMonth() + "月" + calendar.getDay() + "日");
+ mTextYear.setText(String.valueOf(calendar.getYear()));
+ mTextLunar.setText(calendar.getLunar());
+ mYear = calendar.getYear();
+
+ Log.e("onDateSelected", " -- " + calendar.getYear() +
+ " -- " + calendar.getMonth() +
+ " -- " + calendar.getDay() +
+ " -- " + isClick + " -- " + calendar.getScheme());
+ }
+
+ @Override
+ public void onYearChange(int year) {
+ mTextMonthDay.setText(String.valueOf(year));
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/full/FullMonthView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/full/FullMonthView.java
new file mode 100644
index 00000000..e429bcee
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/full/FullMonthView.java
@@ -0,0 +1,142 @@
+package com.haibin.calendarviewproject.full;
+
+import android.content.Context;
+import android.graphics.BlurMaskFilter;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.view.View;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.MonthView;
+
+import java.util.List;
+
+/**
+ * 高仿魅族日历布局
+ * Created by huanghaibin on 2017/11/15.
+ */
+
+public class FullMonthView extends MonthView {
+
+ private Paint mRectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+ /**
+ * 自定义魅族标记的圆形背景
+ */
+ private Paint mSchemeBasicPaint = new Paint();
+
+ public FullMonthView(Context context) {
+ super(context);
+
+ mRectPaint.setStyle(Paint.Style.STROKE);
+ mRectPaint.setStrokeWidth(dipToPx(context, 0.5f));
+ mRectPaint.setColor(0x88efefef);
+
+ mSchemeBasicPaint.setAntiAlias(true);
+ mSchemeBasicPaint.setStyle(Paint.Style.FILL);
+ mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);
+ mSchemeBasicPaint.setFakeBoldText(true);
+
+ //兼容硬件加速无效的代码
+ setLayerType(View.LAYER_TYPE_SOFTWARE, mSchemeBasicPaint);
+ //4.0以上硬件加速会导致无效
+ mSelectedPaint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.SOLID));
+ }
+
+ /**
+ * 绘制选中的日子
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @return true 则绘制onDrawScheme,因为这里背景色不是是互斥的
+ */
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+ canvas.drawRect(x, y , x + mItemWidth, y + mItemHeight, mSelectedPaint);
+ return true;
+ }
+
+ /**
+ * 绘制标记的事件日子
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ */
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+ mSchemeBasicPaint.setColor(calendar.getSchemeColor());
+ List schemes = calendar.getSchemes();
+ if (schemes == null || schemes.size() == 0) {
+ return;
+ }
+ int space = dipToPx(getContext(), 2);
+ int indexY = y + mItemHeight - 2 * space;
+ int sw = dipToPx(getContext(), mItemWidth / 10);
+ int sh = dipToPx(getContext(), 4);
+ for (Calendar.Scheme scheme : schemes) {
+
+ mSchemePaint.setColor(scheme.getShcemeColor());
+
+ canvas.drawRect(x + mItemWidth - sw - 2 * space,
+
+ indexY - sh, x + mItemWidth - 2 * space, indexY, mSchemePaint);
+ indexY = indexY - space -sh;
+ }
+ }
+
+ /**
+ * 绘制文本
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme 是否是标记的日期
+ * @param isSelected 是否选中
+ */
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ canvas.drawRect(x, y, x + mItemWidth, y + mItemHeight, mRectPaint);
+ int cx = x + mItemWidth / 2;
+ int top = y - mItemHeight / 6;
+
+ boolean isInRange = isInRange(calendar);
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ mSelectTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mSelectedLunarTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentMonth() && isInRange ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mCurMonthLunarTextPaint);
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() && isInRange ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10,
+ calendar.isCurrentDay() && isInRange ? mCurDayLunarTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthLunarTextPaint : mOtherMonthLunarTextPaint);
+ }
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/full/FullWeekView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/full/FullWeekView.java
new file mode 100644
index 00000000..a062db7b
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/full/FullWeekView.java
@@ -0,0 +1,119 @@
+package com.haibin.calendarviewproject.full;
+
+import android.content.Context;
+import android.graphics.BlurMaskFilter;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.view.View;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.WeekView;
+
+import java.util.List;
+
+/**
+ * 魅族周视图
+ * Created by huanghaibin on 2017/11/29.
+ */
+
+public class FullWeekView extends WeekView {
+ private Paint mRectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private Paint mSchemeBasicPaint = new Paint();
+
+
+ public FullWeekView(Context context) {
+ super(context);
+
+ mRectPaint.setStyle(Paint.Style.STROKE);
+ mRectPaint.setStrokeWidth(dipToPx(context, 0.5f));
+ mRectPaint.setColor(0x88efefef);
+
+ mSchemeBasicPaint.setAntiAlias(true);
+ mSchemeBasicPaint.setStyle(Paint.Style.FILL);
+ mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);
+ mSchemeBasicPaint.setColor(0xffed5353);
+ mSchemeBasicPaint.setFakeBoldText(true);
+
+ //兼容硬件加速无效的代码
+ setLayerType(View.LAYER_TYPE_SOFTWARE, mSchemeBasicPaint);
+ //4.0以上硬件加速会导致无效
+ mSelectedPaint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.SOLID));
+ }
+
+ /**
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @return true 则绘制onDrawScheme,因为这里背景色不是是互斥的
+ */
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme) {
+ mSelectedPaint.setStyle(Paint.Style.FILL);
+ canvas.drawRect(x,0, x + mItemWidth, mItemHeight , mSelectedPaint);
+ return true;
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x) {
+ mSchemeBasicPaint.setColor(calendar.getSchemeColor());
+ List schemes = calendar.getSchemes();
+ if (schemes == null || schemes.size() == 0) {
+ return;
+ }
+ int space = dipToPx(getContext(), 2);
+ int indexY = mItemHeight - 2 * space;
+ int sw = dipToPx(getContext(), mItemWidth / 10);
+ int sh = dipToPx(getContext(), 4);
+ for (Calendar.Scheme scheme : schemes) {
+
+ mSchemePaint.setColor(scheme.getShcemeColor());
+
+ canvas.drawRect(x + mItemWidth - sw - 2 * space,
+
+ indexY - sh, x + mItemWidth - 2 * space, indexY, mSchemePaint);
+ indexY = indexY - space -sh;
+ }
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected) {
+ canvas.drawRect(x, 0, x + mItemWidth, mItemHeight, mRectPaint);
+ int cx = x + mItemWidth / 2;
+ int top = - mItemHeight / 6;
+
+ boolean isInRange = isInRange(calendar);
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ mSelectTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10, mSelectedLunarTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentMonth() && isInRange ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10, mCurMonthLunarTextPaint);
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() && isInRange ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10,
+ calendar.isCurrentDay() && isInRange ? mCurDayLunarTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthLunarTextPaint : mOtherMonthLunarTextPaint);
+ }
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/group/GroupItemDecoration.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/group/GroupItemDecoration.java
new file mode 100644
index 00000000..a71765ab
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/group/GroupItemDecoration.java
@@ -0,0 +1,282 @@
+package com.haibin.calendarviewproject.group;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.View;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 分组浮动的ItemDecoration
+ * Created by haibin on 2017/5/15.
+ */
+@SuppressWarnings("all")
+public class GroupItemDecoration extends RecyclerView.ItemDecoration {
+ protected int mGroupHeight;
+ protected int mGroutBackground;
+ protected Paint mBackgroundPaint;
+ protected Paint mTextPaint;
+ protected float mTextBaseLine;
+ protected int mPaddingLeft, mPaddingRight;
+ protected boolean isCenter;
+ protected boolean isHasHeader;
+ protected int mChildItemOffset;
+ @SuppressLint("UseSparseArrays")
+ protected Map mGroup = new HashMap<>();
+
+ public GroupItemDecoration() {
+ super();
+ init();
+ }
+
+ private void init() {
+ mBackgroundPaint = new Paint();
+ mBackgroundPaint.setColor(0xFFf5f7f8);
+ mBackgroundPaint.setStyle(Paint.Style.FILL);
+ mBackgroundPaint.setAntiAlias(true);
+
+ mTextPaint = new Paint();
+ mTextPaint.setColor(0xFF353535);
+ mTextPaint.setAntiAlias(true);
+ }
+
+ /**
+ * 先于RecyclerView的Item onDraw调用
+ *
+ * @param c RecyclerView canvas
+ * @param parent RecyclerView
+ * @param state stare
+ */
+ @Override
+ public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+ super.onDraw(c, parent, state);
+ onDrawGroup(c, parent);
+ }
+
+ /**
+ * 绘制分组Group
+ *
+ * @param c Canvas
+ * @param parent RecyclerView
+ */
+ protected void onDrawGroup(Canvas c, RecyclerView parent) {
+ int paddingLeft = parent.getPaddingLeft();
+ int right = parent.getWidth() - parent.getPaddingRight();
+ int top, bottom;
+ int count = parent.getChildCount();
+ for (int i = 0; i < parent.getChildCount(); i++) {
+ View child = parent.getChildAt(i);
+ final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
+ int key = params.getViewLayoutPosition();
+ if (mGroup.containsKey(key)) {
+ top = child.getTop() - params.topMargin - mGroupHeight;
+ bottom = top + mGroupHeight;
+ c.drawRect(paddingLeft, top, right, bottom, mBackgroundPaint);
+ String group = mGroup.get(params.getViewLayoutPosition()).toString();
+ float x;
+ float y = top + mTextBaseLine;
+ if (isCenter) {
+ x = parent.getMeasuredWidth() / 2 - getTextX(group);
+ } else {
+ x = mPaddingLeft;
+ }
+ c.drawText(group, x, y, mTextPaint);
+ }
+ }
+ }
+
+ /**
+ * 后于RecyclerView的Item onDraw调用
+ *
+ * @param c RecyclerView canvas
+ * @param parent RecyclerView
+ * @param state stare
+ */
+ @Override
+ public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
+ super.onDrawOver(c, parent, state);
+ onDrawOverGroup(c, parent);
+ }
+
+ /**
+ * 绘制悬浮组
+ *
+ * @param c Canvas
+ * @param parent RecyclerView
+ */
+ protected void onDrawOverGroup(Canvas c, RecyclerView parent) {
+ int firstVisiblePosition = ((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition();
+ if (firstVisiblePosition == RecyclerView.NO_POSITION) {
+ return;
+ }
+ Group group = getCroup(firstVisiblePosition);
+ if (group == null)
+ return;
+ String groupTitle = group.toString();
+ if (TextUtils.isEmpty(groupTitle)) {
+ return;
+ }
+ boolean isRestore = false;
+ Group nextGroup = getCroup(firstVisiblePosition + 1);
+ if (nextGroup != null && !group.equals(nextGroup)) {
+ //说明是当前组最后一个元素,但不一定碰撞了
+ View child = parent.findViewHolderForAdapterPosition(firstVisiblePosition).itemView;
+ if (child.getTop() + child.getMeasuredHeight() < mGroupHeight) {
+ //进一步检测碰撞
+ c.save();//保存画布当前的状态
+ isRestore = true;
+ c.translate(0, child.getTop() + child.getMeasuredHeight() - mGroupHeight);
+ }
+ }
+ int left = parent.getPaddingLeft();
+ int right = parent.getWidth() - parent.getPaddingRight();
+ int top = parent.getPaddingTop();
+ int bottom = top + mGroupHeight;
+ c.drawRect(left, top, right, bottom, mBackgroundPaint);
+ float x;
+ float y = top + mTextBaseLine;
+ if (isCenter) {
+ x = parent.getMeasuredWidth() / 2 - getTextX(groupTitle);
+ } else {
+ x = mPaddingLeft;
+ }
+ c.drawText(groupTitle, x, y, mTextPaint);
+ if (isRestore) {
+ //还原画布为初始状态
+ c.restore();
+ }
+ }
+
+ /**
+ * 设置item的上下左右偏移量
+ *
+ * @param outRect rect
+ * @param view item
+ * @param parent RecyclerView
+ * @param state stare
+ */
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+ super.getItemOffsets(outRect, view, parent, state);
+ getItemOffsets(outRect, view, parent, parent.getChildViewHolder(view).getAdapterPosition());
+ }
+
+ /**
+ * 设置item的上下左右偏移量,不做任何处理就是默认状态
+ *
+ * @param outRect outRect
+ * @param view view
+ * @param parent RecyclerView
+ * @param adapterPosition position
+ */
+ protected void getItemOffsets(Rect outRect, View view, RecyclerView parent, int adapterPosition) {
+ if (mGroup.containsKey(adapterPosition)) {
+ outRect.set(0, mGroupHeight, 0, mGroup.containsKey(adapterPosition + 1) ? 0 : mChildItemOffset);
+ } else {
+ outRect.set(0, 0, 0, mGroup.containsKey(adapterPosition + 1) ? 0 : mChildItemOffset);
+ }
+ }
+
+ /**
+ * 获得当前ViewPosition所在的组
+ *
+ * @param position 当前View的position
+ * @return 当前ViewPosition所在的组
+ */
+ protected Group getCroup(int position) {
+ while (position >= 0) {
+ if (mGroup.containsKey(position)) {
+ return mGroup.get(position);
+ }
+ position--;
+ }
+ return null;
+ }
+
+ /**
+ * 通知更新分组信息
+ *
+ * @param adapter GroupRecyclerAdapter
+ */
+ public void notifyDataSetChanged(GroupRecyclerAdapter adapter) {
+ mGroup.clear();
+ if (adapter == null) return;
+ int key = 0;
+ for (int i = 0; i < adapter.getGroupCount(); i++) {
+ if (i == 0) {
+ mGroup.put(isHasHeader ? 1 : 0, adapter.getGroup(i));
+ key += adapter.getChildCount(i) + (isHasHeader ? 1 : 0);
+ ;
+ } else {
+ mGroup.put(key, adapter.getGroup(i));
+ key += adapter.getChildCount(i);
+ }
+ }
+ }
+
+ public void setChildItemOffset(int childItemOffset){
+ this.mChildItemOffset = childItemOffset;
+ }
+
+ public void setBackground(int groupBackground) {
+ mBackgroundPaint.setColor(groupBackground);
+ }
+
+ public void setTextColor(int textColor) {
+ mTextPaint.setColor(textColor);
+ }
+
+ public void setTextSize(float textSize) {
+ mTextPaint.setTextSize(textSize);
+ Paint.FontMetrics metrics = mTextPaint.getFontMetrics();
+ mTextBaseLine = mGroupHeight / 2 - metrics.descent + (metrics.bottom - metrics.top) / 2;
+ }
+
+ public void setGroupHeight(int groupHeight) {
+ mGroupHeight = groupHeight;
+ Paint.FontMetrics metrics = mTextPaint.getFontMetrics();
+ mTextBaseLine = mGroupHeight / 2 - metrics.descent + (metrics.bottom - metrics.top) / 2;
+ }
+
+ public void setPadding(int mPaddingLeft, int mPaddingRight) {
+ this.mPaddingLeft = mPaddingLeft;
+ this.mPaddingRight = mPaddingRight;
+ }
+
+ public void setCenter(boolean isCenter) {
+ this.isCenter = isCenter;
+ }
+
+ public void setHasHeader(boolean hasHeader) {
+ isHasHeader = hasHeader;
+ }
+
+ /**
+ * 获取文本的x坐标起点
+ *
+ * @param str 文本
+ * @return x
+ */
+ protected float getTextX(String str) {
+ Rect bounds = new Rect();
+ mTextPaint.getTextBounds(str, 0, str.length(), bounds);
+ return bounds.width() / 2;
+ }
+
+ /**
+ * 获取文本的长度像素
+ * @param str 文本
+ * @return px
+ */
+ protected float getTextLenghtPx(String str) {
+ Rect bounds = new Rect();
+ mTextPaint.getTextBounds(str, 0, str.length(), bounds);
+ return bounds.width();
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/group/GroupRecyclerAdapter.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/group/GroupRecyclerAdapter.java
new file mode 100644
index 00000000..e710332f
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/group/GroupRecyclerAdapter.java
@@ -0,0 +1,135 @@
+package com.haibin.calendarviewproject.group;
+
+import android.content.Context;
+
+
+import com.haibin.calendarviewproject.base.adapter.BaseRecyclerAdapter;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+
+/**
+ * 分组的RecyclerAdapter
+ * Created by haibin on 2017/5/15.
+ */
+@SuppressWarnings("unused")
+public abstract class GroupRecyclerAdapter extends BaseRecyclerAdapter {
+ private LinkedHashMap> mGroups;
+ private List mGroupTitles;
+
+ public GroupRecyclerAdapter(Context context) {
+ super(context);
+ mGroups = new LinkedHashMap<>();
+ mGroupTitles = new ArrayList<>();
+ }
+
+ /**
+ * 返回特定的标题
+ */
+ Parent getGroup(int groupPosition) {
+ return mGroupTitles.get(groupPosition);
+ }
+
+ /**
+ * 获得分组的数量
+ *
+ * @return 组的数量
+ */
+ int getGroupCount() {
+ return mGroupTitles.size();
+ }
+
+ /**
+ * 获取某一组的数量
+ *
+ * @param groupPosition groupPosition
+ * @return 某一组的数量
+ */
+ int getChildCount(int groupPosition) {
+ if (mGroupTitles == null || mGroups.size() == 0)
+ return 0;
+ if (mGroups.get(mGroupTitles.get(groupPosition)) == null)
+ return 0;
+ return mGroups.get(mGroupTitles.get(groupPosition)).size();
+ }
+
+ /**
+ * 重置分组数据
+ *
+ * @param groups groups
+ * @param titles titles
+ */
+ protected void resetGroups(LinkedHashMap> groups, List titles) {
+ if (groups == null || titles == null) {
+ return;
+ }
+ mGroups.clear();
+ mGroupTitles.clear();
+ mGroups.putAll(groups);
+ mGroupTitles.addAll(titles);
+ mItems.clear();
+ for (Parent key : mGroups.keySet()) {
+ mItems.addAll(mGroups.get(key));
+ }
+ notifyDataSetChanged();
+ }
+
+ /**
+ * 清除分组数据
+ */
+ public final void clearGroup() {
+ mGroupTitles.clear();
+ mGroups.clear();
+ clear();
+ }
+
+ /**
+ * 从分组移除数据
+ *
+ * @param position 下标
+ * @return 分组是否为空,要移除分组
+ */
+ public boolean removeGroupItem(int position) {
+ int group = getGroupIndex(position);
+ removeGroupChildren(group);
+ int count = getChildCount(group);
+ removeItem(position);
+ if (count <= 0) {
+ mGroupTitles.remove(group);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 获取所在分组
+ *
+ * @param position 下标
+ * @return 获取所在分组
+ */
+ private int getGroupIndex(int position) {
+ int count = 0;
+ if (position <= count)
+ return 0;
+ int i = 0;
+ for (Parent parent : mGroups.keySet()) {
+ count += mGroups.get(parent).size();
+ if (position < count) {
+ return i;
+ }
+ i++;
+ }
+ return 0;
+ }
+
+ private void removeGroupChildren(int groupPosition) {
+ if (groupPosition >= mGroupTitles.size())
+ return;
+ List childList = mGroups.get(mGroupTitles.get(groupPosition));
+ if (childList != null && childList.size() != 0) {
+ childList.remove(childList.size() - 1);
+ }
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/group/GroupRecyclerView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/group/GroupRecyclerView.java
new file mode 100644
index 00000000..e1b7b0e8
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/group/GroupRecyclerView.java
@@ -0,0 +1,91 @@
+package com.haibin.calendarviewproject.group;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+import android.util.AttributeSet;
+
+import com.haibin.calendarviewproject.R;
+
+
+/**
+ * 带分组浮动的RecyclerView
+ * Created by haibin on 2017/5/15.
+ */
+@SuppressWarnings("all")
+public class GroupRecyclerView extends RecyclerView {
+ private GroupItemDecoration mItemDecoration;
+ private int mGroupHeight;
+ private int mGroutBackground, mTextColor;
+ private int mTextSize;
+ private int mPaddingLeft, mPaddingRight;
+ private boolean isCenter;
+ protected int mChildItemOffset;
+ private boolean isHasHeader;
+ private OnGroupChangeListener mListener;
+
+ public GroupRecyclerView(Context context) {
+ super(context);
+ }
+
+ public GroupRecyclerView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.GroupRecyclerView);
+ mTextSize = array.getDimensionPixelSize(R.styleable.GroupRecyclerView_group_text_size, 16);
+ mGroupHeight = (int) array.getDimension(R.styleable.GroupRecyclerView_group_height, 52);
+ mChildItemOffset = (int) array.getDimension(R.styleable.GroupRecyclerView_group_child_offset, 20);
+ mTextColor = array.getColor(R.styleable.GroupRecyclerView_group_text_color, 0xFFFFFFFF);
+ mGroutBackground = array.getColor(R.styleable.GroupRecyclerView_group_background, 0x80000000);
+ isCenter = array.getBoolean(R.styleable.GroupRecyclerView_group_center, false);
+ isHasHeader = array.getBoolean(R.styleable.GroupRecyclerView_group_has_header, true);
+ mPaddingLeft = (int) array.getDimension(R.styleable.GroupRecyclerView_group_padding_left, 16);
+ mPaddingRight = (int) array.getDimension(R.styleable.GroupRecyclerView_group_padding_right, 16);
+ array.recycle();
+ }
+
+
+ @Override
+ public void setAdapter(Adapter adapter) {
+ if (adapter instanceof GroupRecyclerAdapter) {
+ super.setAdapter(adapter);
+ } else {
+ throw new IllegalStateException("Adapter must instanceof " +
+ "GroupRecyclerAdapter or extends GroupRecyclerAdapter");
+ }
+ }
+
+ @Override
+ public void addItemDecoration(ItemDecoration decor) {
+ if (decor instanceof GroupItemDecoration)
+ super.addItemDecoration(decor);
+ else
+ throw new IllegalStateException("ItemDecoration must instanceof " +
+ "GroupItemDecoration or extends GroupItemDecoration");
+ mItemDecoration = (GroupItemDecoration) decor;
+ mItemDecoration.setTextSize(mTextSize);
+ mItemDecoration.setBackground(mGroutBackground);
+ mItemDecoration.setTextColor(mTextColor);
+ mItemDecoration.setGroupHeight(mGroupHeight);
+ mItemDecoration.setPadding(mPaddingLeft, mPaddingRight);
+ mItemDecoration.setCenter(isCenter);
+ mItemDecoration.setHasHeader(isHasHeader);
+ mItemDecoration.setChildItemOffset(mChildItemOffset);
+ //mItemDecoration.notifyDataSetChanged((GroupRecyclerAdapter) getAdapter());
+ }
+
+ public void notifyDataSetChanged() {
+ mItemDecoration.notifyDataSetChanged((GroupRecyclerAdapter) getAdapter());
+ }
+
+ public void setOnGroupChangeListener(OnGroupChangeListener listener) {
+ this.mListener = listener;
+ }
+
+ /**
+ * 分组最上面改变通知
+ */
+ public interface OnGroupChangeListener {
+ void onGroupChange(int groupPosition, String group);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/index/IndexActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/index/IndexActivity.java
new file mode 100644
index 00000000..87cc05b4
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/index/IndexActivity.java
@@ -0,0 +1,196 @@
+package com.haibin.calendarviewproject.index;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import android.util.Log;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarLayout;
+import com.haibin.calendarview.CalendarView;
+import com.haibin.calendarviewproject.Article;
+import com.haibin.calendarviewproject.ArticleAdapter;
+import com.haibin.calendarviewproject.R;
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+import com.haibin.calendarviewproject.colorful.ColorfulActivity;
+import com.haibin.calendarviewproject.group.GroupItemDecoration;
+import com.haibin.calendarviewproject.group.GroupRecyclerView;
+import com.haibin.calendarviewproject.meizu.MeiZuActivity;
+import com.haibin.calendarviewproject.simple.SimpleActivity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class IndexActivity extends BaseActivity implements
+ CalendarView.OnCalendarSelectListener,
+ CalendarView.OnCalendarLongClickListener,
+ CalendarView.OnYearChangeListener,
+ View.OnClickListener {
+
+ TextView mTextMonthDay;
+
+ TextView mTextYear;
+
+ TextView mTextLunar;
+
+ TextView mTextCurrentDay;
+
+ CalendarView mCalendarView;
+
+ RelativeLayout mRelativeTool;
+ private int mYear;
+ CalendarLayout mCalendarLayout;
+ GroupRecyclerView mRecyclerView;
+
+ public static void show(Context context) {
+ context.startActivity(new Intent(context, IndexActivity.class));
+ }
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_index;
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void initView() {
+ setStatusBarDarkMode();
+ mTextMonthDay = findViewById(R.id.tv_month_day);
+ mTextYear = findViewById(R.id.tv_year);
+ mTextLunar = findViewById(R.id.tv_lunar);
+ mRelativeTool = findViewById(R.id.rl_tool);
+ mCalendarView = findViewById(R.id.calendarView);
+ mTextCurrentDay = findViewById(R.id.tv_current_day);
+ mTextMonthDay.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!mCalendarLayout.isExpand()) {
+ mCalendarLayout.expand();
+ return;
+ }
+ mCalendarView.showYearSelectLayout(mYear);
+ mTextLunar.setVisibility(View.GONE);
+ mTextYear.setVisibility(View.GONE);
+ mTextMonthDay.setText(String.valueOf(mYear));
+ }
+ });
+ findViewById(R.id.fl_current).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCalendarView.scrollToCurrent();
+ }
+ });
+ mCalendarLayout = findViewById(R.id.calendarLayout);
+ mCalendarView.setOnCalendarSelectListener(this);
+ mCalendarView.setOnYearChangeListener(this);
+ mCalendarView.setOnCalendarLongClickListener(this,false);
+ mTextYear.setText(String.valueOf(mCalendarView.getCurYear()));
+ mYear = mCalendarView.getCurYear();
+ mTextMonthDay.setText(mCalendarView.getCurMonth() + "月" + mCalendarView.getCurDay() + "日");
+ mTextLunar.setText("今日");
+ mTextCurrentDay.setText(String.valueOf(mCalendarView.getCurDay()));
+ }
+
+ @Override
+ protected void initData() {
+
+ int year = mCalendarView.getCurYear();
+ int month = mCalendarView.getCurMonth();
+
+ Map map = new HashMap<>();
+ map.put(getSchemeCalendar(year, month, 3, 0xFF40db25, "假").toString(),
+ getSchemeCalendar(year, month, 3, 0xFF40db25, "假"));
+ map.put(getSchemeCalendar(year, month, 6, 0xFFe69138, "事").toString(),
+ getSchemeCalendar(year, month, 6, 0xFFe69138, "事"));
+ map.put(getSchemeCalendar(year, month, 9, 0xFFdf1356, "议").toString(),
+ getSchemeCalendar(year, month, 9, 0xFFdf1356, "议"));
+ map.put(getSchemeCalendar(year, month, 13, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 13, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 14, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 14, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 15, 0xFFaacc44, "假").toString(),
+ getSchemeCalendar(year, month, 15, 0xFFaacc44, "假"));
+ map.put(getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记").toString(),
+ getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记"));
+ map.put(getSchemeCalendar(year, month, 25, 0xFF13acf0, "假").toString(),
+ getSchemeCalendar(year, month, 25, 0xFF13acf0, "假"));
+ map.put(getSchemeCalendar(year, month, 27, 0xFF13acf0, "多").toString(),
+ getSchemeCalendar(year, month, 27, 0xFF13acf0, "多"));
+ //此方法在巨大的数据量上不影响遍历性能,推荐使用
+ mCalendarView.setSchemeDate(map);
+
+
+
+ mRecyclerView = findViewById(R.id.recyclerView);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mRecyclerView.addItemDecoration(new GroupItemDecoration());
+ mRecyclerView.setAdapter(new ArticleAdapter(this));
+ mRecyclerView.notifyDataSetChanged();
+ }
+
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.ll_flyme:
+ MeiZuActivity.show(this);
+ break;
+ case R.id.ll_simple:
+ SimpleActivity.show(this);
+ break;
+ case R.id.ll_colorful:
+ ColorfulActivity.show(this);
+ break;
+ case R.id.ll_index:
+ IndexActivity.show(this);
+ break;
+ }
+ }
+
+ private Calendar getSchemeCalendar(int year, int month, int day, int color, String text) {
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setSchemeColor(color);//如果单独标记颜色、则会使用这个颜色
+ calendar.setScheme(text);
+ return calendar;
+ }
+
+ @Override
+ public void onCalendarOutOfRange(Calendar calendar) {
+
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onCalendarSelect(Calendar calendar, boolean isClick) {
+ mTextLunar.setVisibility(View.VISIBLE);
+ mTextYear.setVisibility(View.VISIBLE);
+ mTextMonthDay.setText(calendar.getMonth() + "月" + calendar.getDay() + "日");
+ mTextYear.setText(String.valueOf(calendar.getYear()));
+ mTextLunar.setText(calendar.getLunar());
+ mYear = calendar.getYear();
+ }
+
+ @Override
+ public void onCalendarLongClickOutOfRange(Calendar calendar) {
+
+ }
+
+ @Override
+ public void onCalendarLongClick(Calendar calendar) {
+ Log.e("onDateLongClick", " -- " + calendar.getDay() + " -- " + calendar.getMonth());
+ }
+
+ @Override
+ public void onYearChange(int year) {
+ mTextMonthDay.setText(String.valueOf(year));
+ }
+
+
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/index/IndexMonthView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/index/IndexMonthView.java
new file mode 100644
index 00000000..d46b2bfc
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/index/IndexMonthView.java
@@ -0,0 +1,91 @@
+package com.haibin.calendarviewproject.index;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.MonthView;
+
+/**
+ * 下标标记的日历控件
+ * Created by huanghaibin on 2017/11/15.
+ */
+
+public class IndexMonthView extends MonthView {
+ private Paint mSchemeBasicPaint = new Paint();
+ private int mPadding;
+ private int mH, mW;
+
+ public IndexMonthView(Context context) {
+ super(context);
+
+ mSchemeBasicPaint.setAntiAlias(true);
+ mSchemeBasicPaint.setStyle(Paint.Style.FILL);
+ mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);
+ mSchemeBasicPaint.setColor(0xff333333);
+ mSchemeBasicPaint.setFakeBoldText(true);
+ mPadding = dipToPx(getContext(), 4);
+ mH = dipToPx(getContext(), 2);
+ mW = dipToPx(getContext(), 8);
+ }
+
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+ mSelectedPaint.setStyle(Paint.Style.FILL);
+ canvas.drawRect(x + mPadding, y + mPadding, x + mItemWidth - mPadding, y + mItemHeight - mPadding, mSelectedPaint);
+ return true;
+ }
+
+ /**
+ * onDrawSelected
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ */
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+ mSchemeBasicPaint.setColor(calendar.getSchemeColor());
+ canvas.drawRect(x + mItemWidth / 2 - mW / 2,
+ y + mItemHeight - mH * 2 - mPadding,
+ x + mItemWidth / 2 + mW / 2,
+ y + mItemHeight - mH - mPadding, mSchemeBasicPaint);
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int top = y - mItemHeight / 6;
+ if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint :
+ mCurMonthLunarTextPaint);
+
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mCurMonthLunarTextPaint);
+ }
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/index/IndexWeekView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/index/IndexWeekView.java
new file mode 100644
index 00000000..c8f8258f
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/index/IndexWeekView.java
@@ -0,0 +1,103 @@
+package com.haibin.calendarviewproject.index;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.WeekView;
+
+/**
+ * 下标周视图
+ * Created by huanghaibin on 2017/11/29.
+ */
+
+public class IndexWeekView extends WeekView {
+ private Paint mSchemeBasicPaint = new Paint();
+ private int mPadding;
+ private int mH, mW;
+
+ public IndexWeekView(Context context) {
+ super(context);
+ mSchemeBasicPaint.setAntiAlias(true);
+ mSchemeBasicPaint.setStyle(Paint.Style.FILL);
+ mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);
+ mSchemeBasicPaint.setColor(0xff333333);
+ mSchemeBasicPaint.setFakeBoldText(true);
+ mPadding = dipToPx(getContext(), 4);
+ mH = dipToPx(getContext(), 2);
+ mW = dipToPx(getContext(), 8);
+ }
+
+ @Override
+ protected void onPreviewHook() {
+
+ }
+
+ /**
+ * 如果这里和 onDrawScheme 是互斥的,则 return false,
+ * return true 会先绘制 onDrawSelected,再绘制onDrawSelected
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ */
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme) {
+ mSelectedPaint.setStyle(Paint.Style.FILL);
+ canvas.drawRect(x + mPadding, mPadding, x + mItemWidth - mPadding, mItemHeight - mPadding, mSelectedPaint);
+ return true;
+ }
+
+ /**
+ * 绘制下标标记
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ */
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x) {
+ mSchemeBasicPaint.setColor(calendar.getSchemeColor());
+ canvas.drawRect(x + mItemWidth / 2 - mW / 2,
+ mItemHeight - mH * 2 - mPadding,
+ x + mItemWidth / 2 + mW / 2,
+ mItemHeight - mH - mPadding, mSchemeBasicPaint);
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int top = -mItemHeight / 6;
+ if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mCurMonthTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint :
+ mCurMonthLunarTextPaint);
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mCurMonthTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint :
+ mCurMonthLunarTextPaint);
+ }
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/meizu/MeiZuActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/meizu/MeiZuActivity.java
new file mode 100644
index 00000000..f88f0120
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/meizu/MeiZuActivity.java
@@ -0,0 +1,171 @@
+package com.haibin.calendarviewproject.meizu;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarLayout;
+import com.haibin.calendarview.CalendarView;
+import com.haibin.calendarviewproject.Article;
+import com.haibin.calendarviewproject.ArticleAdapter;
+import com.haibin.calendarviewproject.R;
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+import com.haibin.calendarviewproject.group.GroupItemDecoration;
+import com.haibin.calendarviewproject.group.GroupRecyclerView;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MeiZuActivity extends BaseActivity implements
+ CalendarView.OnCalendarSelectListener,
+ CalendarView.OnYearChangeListener,
+ View.OnClickListener {
+
+ TextView mTextMonthDay;
+
+ TextView mTextYear;
+
+ TextView mTextLunar;
+
+ TextView mTextCurrentDay;
+
+ CalendarView mCalendarView;
+
+ RelativeLayout mRelativeTool;
+ private int mYear;
+ CalendarLayout mCalendarLayout;
+ GroupRecyclerView mRecyclerView;
+
+ public static void show(Context context) {
+ context.startActivity(new Intent(context, MeiZuActivity.class));
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_meizu;
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void initView() {
+ setStatusBarDarkMode();
+ mTextMonthDay = findViewById(R.id.tv_month_day);
+ mTextYear = findViewById(R.id.tv_year);
+ mTextLunar = findViewById(R.id.tv_lunar);
+ mRelativeTool = findViewById(R.id.rl_tool);
+ mCalendarView = findViewById(R.id.calendarView);
+ mTextCurrentDay = findViewById(R.id.tv_current_day);
+ mTextMonthDay.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!mCalendarLayout.isExpand()) {
+ mCalendarLayout.expand();
+ return;
+ }
+ mCalendarView.showYearSelectLayout(mYear);
+ mTextLunar.setVisibility(View.GONE);
+ mTextYear.setVisibility(View.GONE);
+ mTextMonthDay.setText(String.valueOf(mYear));
+ }
+ });
+ findViewById(R.id.fl_current).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCalendarView.scrollToCurrent();
+ }
+ });
+ mCalendarLayout = findViewById(R.id.calendarLayout);
+ mCalendarView.setOnCalendarSelectListener(this);
+ mCalendarView.setOnYearChangeListener(this);
+ mTextYear.setText(String.valueOf(mCalendarView.getCurYear()));
+ mYear = mCalendarView.getCurYear();
+ mTextMonthDay.setText(mCalendarView.getCurMonth() + "月" + mCalendarView.getCurDay() + "日");
+ mTextLunar.setText("今日");
+ mTextCurrentDay.setText(String.valueOf(mCalendarView.getCurDay()));
+ }
+
+ @Override
+ protected void initData() {
+ int year = mCalendarView.getCurYear();
+ int month = mCalendarView.getCurMonth();
+
+ Map map = new HashMap<>();
+ map.put(getSchemeCalendar(year, month, 3, 0xFF40db25, "假").toString(),
+ getSchemeCalendar(year, month, 3, 0xFF40db25, "假"));
+ map.put(getSchemeCalendar(year, month, 6, 0xFFe69138, "事").toString(),
+ getSchemeCalendar(year, month, 6, 0xFFe69138, "事"));
+ map.put(getSchemeCalendar(year, month, 9, 0xFFdf1356, "议").toString(),
+ getSchemeCalendar(year, month, 9, 0xFFdf1356, "议"));
+ map.put(getSchemeCalendar(year, month, 13, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 13, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 14, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 14, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 15, 0xFFaacc44, "假").toString(),
+ getSchemeCalendar(year, month, 15, 0xFFaacc44, "假"));
+ map.put(getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记").toString(),
+ getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记"));
+ map.put(getSchemeCalendar(year, month, 25, 0xFF13acf0, "假").toString(),
+ getSchemeCalendar(year, month, 25, 0xFF13acf0, "假"));
+ map.put(getSchemeCalendar(year, month, 27, 0xFF13acf0, "多").toString(),
+ getSchemeCalendar(year, month, 27, 0xFF13acf0, "多"));
+ //此方法在巨大的数据量上不影响遍历性能,推荐使用
+ mCalendarView.setSchemeDate(map);
+
+
+ mRecyclerView = findViewById(R.id.recyclerView);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mRecyclerView.addItemDecoration(new GroupItemDecoration());
+ mRecyclerView.setAdapter(new ArticleAdapter(this));
+ mRecyclerView.notifyDataSetChanged();
+ }
+
+
+ @Override
+ public void onClick(View v) {
+
+ }
+
+ private Calendar getSchemeCalendar(int year, int month, int day, int color, String text) {
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setSchemeColor(color);//如果单独标记颜色、则会使用这个颜色
+ calendar.setScheme(text);
+ calendar.addScheme(new Calendar.Scheme());
+ calendar.addScheme(0xFF008800, "假");
+ calendar.addScheme(0xFF008800, "节");
+ return calendar;
+ }
+
+
+ @Override
+ public void onCalendarOutOfRange(Calendar calendar) {
+
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onCalendarSelect(Calendar calendar, boolean isClick) {
+ mTextLunar.setVisibility(View.VISIBLE);
+ mTextYear.setVisibility(View.VISIBLE);
+ mTextMonthDay.setText(calendar.getMonth() + "月" + calendar.getDay() + "日");
+ mTextYear.setText(String.valueOf(calendar.getYear()));
+ mTextLunar.setText(calendar.getLunar());
+ mYear = calendar.getYear();
+ }
+
+ @Override
+ public void onYearChange(int year) {
+ mTextMonthDay.setText(String.valueOf(year));
+ }
+
+
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/meizu/MeiZuMonthView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/meizu/MeiZuMonthView.java
new file mode 100644
index 00000000..9e9d9227
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/meizu/MeiZuMonthView.java
@@ -0,0 +1,141 @@
+package com.haibin.calendarviewproject.meizu;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.MonthView;
+
+/**
+ * 高仿魅族日历布局
+ * Created by huanghaibin on 2017/11/15.
+ */
+
+public class MeiZuMonthView extends MonthView {
+
+ /**
+ * 自定义魅族标记的文本画笔
+ */
+ private Paint mTextPaint = new Paint();
+
+ /**
+ * 自定义魅族标记的圆形背景
+ */
+ private Paint mSchemeBasicPaint = new Paint();
+ private float mRadio;
+ private int mPadding;
+ private float mSchemeBaseLine;
+
+ public MeiZuMonthView(Context context) {
+ super(context);
+
+ mTextPaint.setTextSize(dipToPx(context, 8));
+ mTextPaint.setColor(0xffffffff);
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setFakeBoldText(true);
+
+ mSchemeBasicPaint.setAntiAlias(true);
+ mSchemeBasicPaint.setStyle(Paint.Style.FILL);
+ mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);
+ mSchemeBasicPaint.setFakeBoldText(true);
+ mRadio = dipToPx(getContext(), 7);
+ mPadding = dipToPx(getContext(), 4);
+ Paint.FontMetrics metrics = mSchemeBasicPaint.getFontMetrics();
+ mSchemeBaseLine = mRadio - metrics.descent + (metrics.bottom - metrics.top) / 2 + dipToPx(getContext(), 1);
+
+// //兼容硬件加速无效的代码
+// setLayerType(View.LAYER_TYPE_SOFTWARE, mSchemeBasicPaint);
+// //4.0以上硬件加速会导致无效
+// mSchemeBasicPaint.setMaskFilter(new BlurMaskFilter(25, BlurMaskFilter.Blur.SOLID));
+ }
+
+ /**
+ * 绘制选中的日子
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @return true 则绘制onDrawScheme,因为这里背景色不是是互斥的
+ */
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+ mSelectedPaint.setStyle(Paint.Style.FILL);
+ canvas.drawRect(x + mPadding, y + mPadding, x + mItemWidth - mPadding, y + mItemHeight - mPadding, mSelectedPaint);
+ return true;
+ }
+
+ /**
+ * 绘制标记的事件日子
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ */
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+ mSchemeBasicPaint.setColor(calendar.getSchemeColor());
+
+ canvas.drawCircle(x + mItemWidth - mPadding - mRadio / 2, y + mPadding + mRadio, mRadio, mSchemeBasicPaint);
+
+ canvas.drawText(calendar.getScheme(),
+ x + mItemWidth - mPadding - mRadio / 2 - getTextWidth(calendar.getScheme()) / 2,
+ y + mPadding + mSchemeBaseLine, mTextPaint);
+ }
+
+ private float getTextWidth(String text) {
+ return mTextPaint.measureText(text);
+ }
+
+ /**
+ * 绘制文本
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme 是否是标记的日期
+ * @param isSelected 是否选中
+ */
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int top = y - mItemHeight / 6;
+
+ boolean isInRange = isInRange(calendar);
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ mSelectTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mSelectedLunarTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentMonth() && isInRange ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mCurMonthLunarTextPaint);
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() && isInRange ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10,
+ calendar.isCurrentDay() && isInRange ? mCurDayLunarTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthLunarTextPaint : mOtherMonthLunarTextPaint);
+ }
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/meizu/MeizuWeekView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/meizu/MeizuWeekView.java
new file mode 100644
index 00000000..4ae3ed41
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/meizu/MeizuWeekView.java
@@ -0,0 +1,109 @@
+package com.haibin.calendarviewproject.meizu;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.WeekView;
+
+/**
+ * 魅族周视图
+ * Created by huanghaibin on 2017/11/29.
+ */
+
+public class MeizuWeekView extends WeekView {
+ private Paint mTextPaint = new Paint();
+
+ private Paint mSchemeBasicPaint = new Paint();
+ private float mRadio;
+ private int mPadding;
+ private float mSchemeBaseLine;
+
+ public MeizuWeekView(Context context) {
+ super(context);
+
+ mTextPaint.setTextSize(dipToPx(context, 8));
+ mTextPaint.setColor(0xffffffff);
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setFakeBoldText(true);
+
+ mSchemeBasicPaint.setAntiAlias(true);
+ mSchemeBasicPaint.setStyle(Paint.Style.FILL);
+ mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);
+ mSchemeBasicPaint.setColor(0xffed5353);
+ mSchemeBasicPaint.setFakeBoldText(true);
+ mRadio = dipToPx(getContext(), 7);
+ mPadding = dipToPx(getContext(), 4);
+ Paint.FontMetrics metrics = mSchemeBasicPaint.getFontMetrics();
+ mSchemeBaseLine = mRadio - metrics.descent + (metrics.bottom - metrics.top) / 2 + dipToPx(getContext(), 1);
+ }
+
+ /**
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @return true 则绘制onDrawScheme,因为这里背景色不是是互斥的
+ */
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme) {
+ mSelectedPaint.setStyle(Paint.Style.FILL);
+ canvas.drawRect(x + mPadding, mPadding, x + mItemWidth - mPadding, mItemHeight - mPadding, mSelectedPaint);
+ return true;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x) {
+ mSchemeBasicPaint.setColor(calendar.getSchemeColor());
+
+ canvas.drawCircle(x + mItemWidth - mPadding - mRadio / 2, mPadding + mRadio, mRadio, mSchemeBasicPaint);
+
+ canvas.drawText(calendar.getScheme(),
+ x + mItemWidth - mPadding - mRadio / 2 - getTextWidth(calendar.getScheme()) / 2,
+ mPadding + mSchemeBaseLine, mTextPaint);
+ }
+
+ private float getTextWidth(String text) {
+ return mTextPaint.measureText(text);
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int top = -mItemHeight / 6;
+
+ boolean isInRange = isInRange(calendar);
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ mSelectTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10, mSelectedLunarTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentMonth() && isInRange ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10, mCurMonthLunarTextPaint);
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() && isInRange ? mCurDayTextPaint :
+ calendar.isCurrentMonth() && isInRange ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10,
+ calendar.isCurrentDay() && isInRange ? mCurDayLunarTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthLunarTextPaint : mOtherMonthLunarTextPaint);
+ }
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/mix/MixActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/mix/MixActivity.java
new file mode 100644
index 00000000..d420e30a
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/mix/MixActivity.java
@@ -0,0 +1,203 @@
+package com.haibin.calendarviewproject.mix;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarLayout;
+import com.haibin.calendarview.CalendarView;
+import com.haibin.calendarviewproject.Article;
+import com.haibin.calendarviewproject.ArticleAdapter;
+import com.haibin.calendarviewproject.R;
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+import com.haibin.calendarviewproject.colorful.ColorfulActivity;
+import com.haibin.calendarviewproject.group.GroupItemDecoration;
+import com.haibin.calendarviewproject.group.GroupRecyclerView;
+import com.haibin.calendarviewproject.index.IndexActivity;
+import com.haibin.calendarviewproject.simple.SimpleActivity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MixActivity extends BaseActivity implements
+ CalendarView.OnCalendarSelectListener,
+ CalendarView.OnYearChangeListener,
+ View.OnClickListener {
+
+ TextView mTextMonthDay;
+
+ TextView mTextYear;
+
+ TextView mTextLunar;
+
+ TextView mTextCurrentDay;
+
+ CalendarView mCalendarView;
+
+ RelativeLayout mRelativeTool;
+ private int mYear;
+ CalendarLayout mCalendarLayout;
+ GroupRecyclerView mRecyclerView;
+
+ public static void show(Context context) {
+ context.startActivity(new Intent(context, MixActivity.class));
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_mix;
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void initView() {
+ setStatusBarDarkMode();
+ mTextMonthDay = findViewById(R.id.tv_month_day);
+ mTextYear = findViewById(R.id.tv_year);
+ mTextLunar = findViewById(R.id.tv_lunar);
+ mRelativeTool = findViewById(R.id.rl_tool);
+ mCalendarView = findViewById(R.id.calendarView);
+ mTextCurrentDay = findViewById(R.id.tv_current_day);
+ mTextMonthDay.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!mCalendarLayout.isExpand()) {
+ mCalendarLayout.expand();
+ return;
+ }
+ mCalendarView.showYearSelectLayout(mYear);
+ mTextLunar.setVisibility(View.GONE);
+ mTextYear.setVisibility(View.GONE);
+ mTextMonthDay.setText(String.valueOf(mYear));
+ }
+ });
+ findViewById(R.id.fl_current).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCalendarView.scrollToCurrent();
+ }
+ });
+ mCalendarLayout = findViewById(R.id.calendarLayout);
+ mCalendarView.setOnCalendarSelectListener(this);
+ mCalendarView.setOnYearChangeListener(this);
+ mCalendarView.setOnClickCalendarPaddingListener(new CalendarView.OnClickCalendarPaddingListener() {
+ @Override
+ public void onClickCalendarPadding(float x, float y, boolean isMonthView,
+ Calendar adjacentCalendar, Object obj) {
+ Log.e("onClickCalendarPadding", " -- " + x + " " + y + " " + obj + " " + adjacentCalendar);
+ Toast.makeText(MixActivity.this,
+ adjacentCalendar.getYear() + "年,第" + obj + "周",
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+ mTextYear.setText(String.valueOf(mCalendarView.getCurYear()));
+ mYear = mCalendarView.getCurYear();
+ mTextMonthDay.setText(mCalendarView.getCurMonth() + "月" + mCalendarView.getCurDay() + "日");
+ mTextLunar.setText("今日");
+ mTextCurrentDay.setText(String.valueOf(mCalendarView.getCurDay()));
+ }
+
+ @Override
+ protected void initData() {
+ int year = mCalendarView.getCurYear();
+ int month = mCalendarView.getCurMonth();
+
+ Map map = new HashMap<>();
+ map.put(getSchemeCalendar(year, month, 3, 0xFF40db25, "假").toString(),
+ getSchemeCalendar(year, month, 3, 0xFF40db25, "假"));
+ map.put(getSchemeCalendar(year, month, 6, 0xFFe69138, "事").toString(),
+ getSchemeCalendar(year, month, 6, 0xFFe69138, "事"));
+ map.put(getSchemeCalendar(year, month, 9, 0xFFdf1356, "议").toString(),
+ getSchemeCalendar(year, month, 9, 0xFFdf1356, "议"));
+ map.put(getSchemeCalendar(year, month, 13, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 13, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 14, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 14, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 15, 0xFFaacc44, "假").toString(),
+ getSchemeCalendar(year, month, 15, 0xFFaacc44, "假"));
+ map.put(getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记").toString(),
+ getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记"));
+ map.put(getSchemeCalendar(year, month, 25, 0xFF13acf0, "假").toString(),
+ getSchemeCalendar(year, month, 25, 0xFF13acf0, "假"));
+ map.put(getSchemeCalendar(year, month, 27, 0xFF13acf0, "多").toString(),
+ getSchemeCalendar(year, month, 27, 0xFF13acf0, "多"));
+ //此方法在巨大的数据量上不影响遍历性能,推荐使用
+ mCalendarView.setSchemeDate(map);
+
+
+ mRecyclerView = findViewById(R.id.recyclerView);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mRecyclerView.addItemDecoration(new GroupItemDecoration());
+ mRecyclerView.setAdapter(new ArticleAdapter(this));
+ mRecyclerView.notifyDataSetChanged();
+ }
+
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.ll_flyme:
+ MixActivity.show(this);
+ break;
+ case R.id.ll_simple:
+ SimpleActivity.show(this);
+ break;
+ case R.id.ll_colorful:
+ ColorfulActivity.show(this);
+ break;
+ case R.id.ll_index:
+ IndexActivity.show(this);
+ break;
+ }
+ }
+
+ private Calendar getSchemeCalendar(int year, int month, int day, int color, String text) {
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setSchemeColor(color);//如果单独标记颜色、则会使用这个颜色
+ calendar.setScheme(text);
+ calendar.addScheme(new Calendar.Scheme());
+ calendar.addScheme(0xFF008800, "假");
+ calendar.addScheme(0xFF008800, "节");
+ return calendar;
+ }
+
+ @Override
+ public void onCalendarOutOfRange(Calendar calendar) {
+
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onCalendarSelect(Calendar calendar, boolean isClick) {
+ mTextLunar.setVisibility(View.VISIBLE);
+ mTextYear.setVisibility(View.VISIBLE);
+ mTextMonthDay.setText(calendar.getMonth() + "月" + calendar.getDay() + "日");
+ mTextYear.setText(String.valueOf(calendar.getYear()));
+ mTextLunar.setText(calendar.getLunar());
+ mYear = calendar.getYear();
+
+ Log.e("onDateSelected", " -- " + calendar.getYear() +
+ " -- " + calendar.getMonth() +
+ " -- " + calendar.getDay() +
+ " -- " + isClick + " -- " + calendar.getScheme());
+ }
+
+ @Override
+ public void onYearChange(int year) {
+ mTextMonthDay.setText(String.valueOf(year));
+ }
+
+
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/mix/MixMonthView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/mix/MixMonthView.java
new file mode 100644
index 00000000..8555f478
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/mix/MixMonthView.java
@@ -0,0 +1,219 @@
+package com.haibin.calendarviewproject.mix;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.text.TextUtils;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarUtil;
+import com.haibin.calendarview.MonthView;
+
+/**
+ * 演示一个变态需求的月视图
+ * Created by huanghaibin on 2018/2/9.
+ */
+
+public class MixMonthView extends MonthView {
+
+ private int mRadius;
+
+ /**
+ * 自定义魅族标记的文本画笔
+ */
+ private Paint mTextPaint = new Paint();
+
+
+ /**
+ * 24节气画笔
+ */
+ private Paint mSolarTermTextPaint = new Paint();
+
+ /**
+ * 背景圆点
+ */
+ private Paint mPointPaint = new Paint();
+
+ /**
+ * 今天的背景色
+ */
+ private Paint mCurrentDayPaint = new Paint();
+
+ /**
+ * 圆点半径
+ */
+ private float mPointRadius;
+
+ private int mPadding;
+
+ private float mCircleRadius;
+ /**
+ * 自定义魅族标记的圆形背景
+ */
+ private Paint mSchemeBasicPaint = new Paint();
+
+ private float mSchemeBaseLine;
+
+ public MixMonthView(Context context) {
+ super(context);
+
+ mTextPaint.setTextSize(dipToPx(context, 8));
+ mTextPaint.setColor(0xffffffff);
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setFakeBoldText(true);
+
+
+ mSolarTermTextPaint.setColor(0xff489dff);
+ mSolarTermTextPaint.setAntiAlias(true);
+ mSolarTermTextPaint.setTextAlign(Paint.Align.CENTER);
+
+ mSchemeBasicPaint.setAntiAlias(true);
+ mSchemeBasicPaint.setStyle(Paint.Style.FILL);
+ mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);
+ mSchemeBasicPaint.setFakeBoldText(true);
+ mSchemeBasicPaint.setColor(Color.WHITE);
+
+
+ mCurrentDayPaint.setAntiAlias(true);
+ mCurrentDayPaint.setStyle(Paint.Style.FILL);
+ mCurrentDayPaint.setColor(0xFFeaeaea);
+
+ mPointPaint.setAntiAlias(true);
+ mPointPaint.setStyle(Paint.Style.FILL);
+ mPointPaint.setTextAlign(Paint.Align.CENTER);
+ mPointPaint.setColor(Color.RED);
+
+ mCircleRadius = dipToPx(getContext(), 7);
+
+ mPadding = dipToPx(getContext(), 3);
+
+ mPointRadius = dipToPx(context, 2);
+
+ Paint.FontMetrics metrics = mSchemeBasicPaint.getFontMetrics();
+ mSchemeBaseLine = mCircleRadius - metrics.descent + (metrics.bottom - metrics.top) / 2 + dipToPx(getContext(), 1);
+ }
+
+ @Override
+ protected void onPreviewHook() {
+ mSolarTermTextPaint.setTextSize(mCurMonthLunarTextPaint.getTextSize());
+ mRadius = Math.min(mItemWidth, mItemHeight) / 11 * 5;
+ }
+
+ @Override
+ protected Object getClickCalendarPaddingObject(float x, float y,Calendar adjacentCalendar) {
+ return CalendarUtil.getWeekCountBetweenBothCalendar(mYear, 1, 1,
+ mYear, adjacentCalendar.getMonth(), adjacentCalendar.getDay(), getWeekStartWith());
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+
+ int weekStart = CalendarUtil.getWeekCountBetweenBothCalendar(mYear, 1, 1,
+ mYear, mMonth, 1, getWeekStartWith());
+ int weekEnd = CalendarUtil.getWeekCountBetweenBothCalendar(mYear, 1, 1,
+ mYear, mMonth, CalendarUtil.getMonthDaysCount(mYear, mMonth), getWeekStartWith());
+ int width = dipToPx(getContext(), 52);//left-padding
+ int cx = width / 2;
+ int cy = 0;
+
+ for (int w = weekStart; w <= weekEnd; w++) {
+
+ canvas.drawText(String.valueOf(w), cx, mTextBaseLine + cy,
+ mOtherMonthTextPaint);
+ cy += mItemHeight;
+ }
+ super.onDraw(canvas);
+ }
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ return true;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+
+ boolean isSelected = isSelected(calendar);
+ if (isSelected) {
+ mPointPaint.setColor(Color.WHITE);
+ } else {
+ mPointPaint.setColor(Color.GRAY);
+ }
+
+ canvas.drawCircle(x + mItemWidth / 2, y + mItemHeight - 3 * mPadding, mPointRadius, mPointPaint);
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ int top = y - mItemHeight / 6;
+
+ if (calendar.isCurrentDay() && !isSelected) {
+ canvas.drawCircle(cx, cy, mRadius, mCurrentDayPaint);
+ }
+
+ if (hasScheme) {
+ canvas.drawCircle(x + mItemWidth - mPadding - mCircleRadius / 2, y + mPadding + mCircleRadius, mCircleRadius, mSchemeBasicPaint);
+ mTextPaint.setColor(calendar.getSchemeColor());
+ canvas.drawText(calendar.getScheme(), x + mItemWidth - mPadding - mCircleRadius, y + mPadding + mSchemeBaseLine, mTextPaint);
+ }
+
+ //当然可以换成其它对应的画笔就不麻烦,
+ if (calendar.isWeekend() && calendar.isCurrentMonth()) {
+ mCurMonthTextPaint.setColor(0xFF489dff);
+ mCurMonthLunarTextPaint.setColor(0xFF489dff);
+ mSchemeTextPaint.setColor(0xFF489dff);
+ mSchemeLunarTextPaint.setColor(0xFF489dff);
+ mOtherMonthLunarTextPaint.setColor(0xFF489dff);
+ mOtherMonthTextPaint.setColor(0xFF489dff);
+ } else {
+ mCurMonthTextPaint.setColor(0xff333333);
+ mCurMonthLunarTextPaint.setColor(0xffCFCFCF);
+ mSchemeTextPaint.setColor(0xff333333);
+ mSchemeLunarTextPaint.setColor(0xffCFCFCF);
+
+ mOtherMonthTextPaint.setColor(0xFFe1e1e1);
+ mOtherMonthLunarTextPaint.setColor(0xFFe1e1e1);
+ }
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ mSelectTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mSelectedLunarTextPaint);
+ } else if (hasScheme) {
+
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10,
+ !TextUtils.isEmpty(calendar.getSolarTerm()) ? mSolarTermTextPaint : mSchemeLunarTextPaint);
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint :
+ calendar.isCurrentMonth() ? !TextUtils.isEmpty(calendar.getSolarTerm()) ? mSolarTermTextPaint :
+ mCurMonthLunarTextPaint : mOtherMonthLunarTextPaint);
+ }
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/mix/MixWeekBar.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/mix/MixWeekBar.java
new file mode 100644
index 00000000..1dd1efa3
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/mix/MixWeekBar.java
@@ -0,0 +1,64 @@
+package com.haibin.calendarviewproject.mix;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.view.LayoutInflater;
+import android.widget.TextView;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.WeekBar;
+import com.haibin.calendarviewproject.R;
+
+/**
+ * 自定义英文栏
+ * Created by huanghaibin on 2017/11/30.
+ */
+
+public class MixWeekBar extends WeekBar {
+
+ private int mPreSelectedIndex;
+
+ public MixWeekBar(Context context) {
+ super(context);
+ LayoutInflater.from(context).inflate(R.layout.custom_week_bar, this, true);
+ setBackgroundColor(Color.WHITE);
+ }
+
+ @Override
+ protected void onDateSelected(Calendar calendar, int weekStart, boolean isClick) {
+ getChildAt(mPreSelectedIndex).setSelected(false);
+ int viewIndex = getViewIndexByCalendar(calendar, weekStart);
+ getChildAt(viewIndex).setSelected(true);
+ mPreSelectedIndex = viewIndex;
+ }
+
+ /**
+ * 当周起始发生变化,使用自定义布局需要重写这个方法,避免出问题
+ *
+ * @param weekStart 周起始
+ */
+ @Override
+ protected void onWeekStartChange(int weekStart) {
+ for (int i = 0; i < getChildCount(); i++) {
+ ((TextView) getChildAt(i)).setText(getWeekString(i, weekStart));
+ }
+ }
+
+ /**
+ * 或者周文本,这个方法仅供父类使用
+ * @param index index
+ * @param weekStart weekStart
+ * @return 或者周文本
+ */
+ private String getWeekString(int index, int weekStart) {
+ String[] weeks = getContext().getResources().getStringArray(R.array.chinese_week_string_array);
+
+ if (weekStart == 1) {
+ return weeks[index];
+ }
+ if (weekStart == 2) {
+ return weeks[index == 6 ? 0 : index + 1];
+ }
+ return weeks[index == 0 ? 6 : index - 1];
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/mix/MixWeekView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/mix/MixWeekView.java
new file mode 100644
index 00000000..5b73547b
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/mix/MixWeekView.java
@@ -0,0 +1,224 @@
+package com.haibin.calendarviewproject.mix;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.text.TextUtils;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarUtil;
+import com.haibin.calendarview.WeekView;
+
+/**
+ * 演示一个变态需求的周视图
+ * Created by huanghaibin on 2018/2/9.
+ */
+
+public class MixWeekView extends WeekView {
+
+
+ private int mRadius;
+
+ /**
+ * 自定义魅族标记的文本画笔
+ */
+ private Paint mTextPaint = new Paint();
+
+
+ /**
+ * 24节气画笔
+ */
+ private Paint mSolarTermTextPaint = new Paint();
+
+ /**
+ * 背景圆点
+ */
+ private Paint mPointPaint = new Paint();
+
+ /**
+ * 今天的背景色
+ */
+ private Paint mCurrentDayPaint = new Paint();
+
+
+ /**
+ * 圆点半径
+ */
+ private float mPointRadius;
+
+ private int mPadding;
+
+ private float mCircleRadius;
+ /**
+ * 自定义魅族标记的圆形背景
+ */
+ private Paint mSchemeBasicPaint = new Paint();
+
+ private float mSchemeBaseLine;
+
+ public MixWeekView(Context context) {
+ super(context);
+ mTextPaint.setTextSize(dipToPx(context, 8));
+ mTextPaint.setColor(0xffffffff);
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setFakeBoldText(true);
+
+
+ mSolarTermTextPaint.setColor(0xff489dff);
+ mSolarTermTextPaint.setAntiAlias(true);
+ mSolarTermTextPaint.setTextAlign(Paint.Align.CENTER);
+
+ mSchemeBasicPaint.setAntiAlias(true);
+ mSchemeBasicPaint.setStyle(Paint.Style.FILL);
+ mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);
+ mSchemeBasicPaint.setFakeBoldText(true);
+ mSchemeBasicPaint.setColor(Color.WHITE);
+
+ mPointPaint.setAntiAlias(true);
+ mPointPaint.setStyle(Paint.Style.FILL);
+ mPointPaint.setTextAlign(Paint.Align.CENTER);
+ mPointPaint.setColor(Color.RED);
+
+
+ mCurrentDayPaint.setAntiAlias(true);
+ mCurrentDayPaint.setStyle(Paint.Style.FILL);
+ mCurrentDayPaint.setColor(0xFFeaeaea);
+
+
+ mCircleRadius = dipToPx(getContext(), 7);
+
+ mPadding = dipToPx(getContext(), 3);
+
+ mPointRadius = dipToPx(context, 2);
+
+ Paint.FontMetrics metrics = mSchemeBasicPaint.getFontMetrics();
+ mSchemeBaseLine = mCircleRadius - metrics.descent + (metrics.bottom - metrics.top) / 2 + dipToPx(getContext(), 1);
+
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+
+ if (mItems == null || mItems.size() == 0) {
+ return;
+ }
+ Calendar calendar = mItems.get(0);
+ int weekStart = CalendarUtil.getWeekCountBetweenBothCalendar(calendar.getYear(), 1, 1,
+ calendar.getYear(), calendar.getMonth(), calendar.getDay(), getWeekStartWith());
+ int width = dipToPx(getContext(), 52);
+ int cx = width / 2;
+ int cy = 0;
+
+ canvas.drawText(String.valueOf(weekStart), cx, mTextBaseLine + cy,
+ mOtherMonthTextPaint);
+ super.onDraw(canvas);
+ }
+
+ @Override
+ protected Object getClickCalendarPaddingObject(float x, float y, Calendar adjacentCalendar) {
+ return CalendarUtil.getWeekCountBetweenBothCalendar(adjacentCalendar.getYear(), 1, 1,
+ adjacentCalendar.getYear(), adjacentCalendar.getMonth(), adjacentCalendar.getDay(), getWeekStartWith());
+ }
+
+ @Override
+ protected void onPreviewHook() {
+ mSolarTermTextPaint.setTextSize(mCurMonthLunarTextPaint.getTextSize());
+ mRadius = Math.min(mItemWidth, mItemHeight) / 11 * 5;
+ }
+
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ return true;
+ }
+
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x) {
+
+ boolean isSelected = isSelected(calendar);
+ if (isSelected) {
+ mPointPaint.setColor(Color.WHITE);
+ } else {
+ mPointPaint.setColor(Color.GRAY);
+ }
+
+ canvas.drawCircle(x + mItemWidth / 2, mItemHeight - 3 * mPadding, mPointRadius, mPointPaint);
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+ int top = -mItemHeight / 6;
+
+ if (calendar.isCurrentDay() && !isSelected) {
+ canvas.drawCircle(cx, cy, mRadius, mCurrentDayPaint);
+ }
+
+ if (hasScheme) {
+ canvas.drawCircle(x + mItemWidth - mPadding - mCircleRadius / 2, mPadding + mCircleRadius, mCircleRadius, mSchemeBasicPaint);
+
+ mTextPaint.setColor(calendar.getSchemeColor());
+
+ canvas.drawText(calendar.getScheme(), x + mItemWidth - mPadding - mCircleRadius, mPadding + mSchemeBaseLine, mTextPaint);
+ }
+
+ if (calendar.isWeekend() && calendar.isCurrentMonth()) {
+ mCurMonthTextPaint.setColor(0xFF489dff);
+ mCurMonthLunarTextPaint.setColor(0xFF489dff);
+ mSchemeTextPaint.setColor(0xFF489dff);
+ mSchemeLunarTextPaint.setColor(0xFF489dff);
+ mOtherMonthLunarTextPaint.setColor(0xFF489dff);
+ mOtherMonthTextPaint.setColor(0xFF489dff);
+ } else {
+ mCurMonthTextPaint.setColor(0xff333333);
+ mCurMonthLunarTextPaint.setColor(0xffCFCFCF);
+ mSchemeTextPaint.setColor(0xff333333);
+ mSchemeLunarTextPaint.setColor(0xffCFCFCF);
+
+ mOtherMonthTextPaint.setColor(0xFFe1e1e1);
+ mOtherMonthLunarTextPaint.setColor(0xFFe1e1e1);
+ }
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ mSelectTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10, mSelectedLunarTextPaint);
+ } else if (hasScheme) {
+
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10,
+ !TextUtils.isEmpty(calendar.getSolarTerm()) ? mSolarTermTextPaint : mSchemeLunarTextPaint);
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint :
+ !TextUtils.isEmpty(calendar.getSolarTerm()) ? mSolarTermTextPaint :
+ calendar.isCurrentMonth() ?
+ mCurMonthLunarTextPaint : mOtherMonthLunarTextPaint);
+ }
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/multi/CustomMultiMonthView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/multi/CustomMultiMonthView.java
new file mode 100644
index 00000000..d3dcd2c9
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/multi/CustomMultiMonthView.java
@@ -0,0 +1,86 @@
+package com.haibin.calendarviewproject.multi;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.MultiMonthView;
+
+/**
+ * 高仿魅族日历布局
+ * Created by huanghaibin on 2017/11/15.
+ */
+
+public class CustomMultiMonthView extends MultiMonthView {
+
+ private int mRadius;
+
+ public CustomMultiMonthView(Context context) {
+ super(context);
+ }
+
+
+ @Override
+ protected void onPreviewHook() {
+ mRadius = Math.min(mItemWidth, mItemHeight) / 5 * 2;
+ mSchemePaint.setStyle(Paint.Style.STROKE);
+ }
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme,
+ boolean isSelectedPre, boolean isSelectedNext) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ if (isSelectedPre) {
+ if (isSelectedNext) {
+ canvas.drawRect(x, cy - mRadius, x + mItemWidth, cy + mRadius, mSelectedPaint);
+ } else {//最后一个,the last
+ canvas.drawRect(x, cy - mRadius, cx, cy + mRadius, mSelectedPaint);
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ }
+ } else {
+ if(isSelectedNext){
+ canvas.drawRect(cx, cy - mRadius, x + mItemWidth, cy + mRadius, mSelectedPaint);
+ }
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ //
+ }
+
+ return false;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSchemePaint);
+ }
+
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine + y;
+ int cx = x + mItemWidth / 2;
+
+ boolean isInRange = isInRange(calendar);
+ boolean isEnable = !onCalendarIntercept(calendar);
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ mSelectTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() && isInRange && isEnable? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() && isInRange && isEnable? mCurMonthTextPaint : mOtherMonthTextPaint);
+ }
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/multi/CustomMultiWeekView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/multi/CustomMultiWeekView.java
new file mode 100644
index 00000000..6dd7c74e
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/multi/CustomMultiWeekView.java
@@ -0,0 +1,84 @@
+package com.haibin.calendarviewproject.multi;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.MultiWeekView;
+
+/**
+ * 魅族周视图
+ * Created by huanghaibin on 2017/11/29.
+ */
+
+public class CustomMultiWeekView extends MultiWeekView {
+ private int mRadius;
+
+ public CustomMultiWeekView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onPreviewHook() {
+ mRadius = Math.min(mItemWidth, mItemHeight) / 5 * 2;
+ mSchemePaint.setStyle(Paint.Style.STROKE);
+ }
+
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme,
+ boolean isSelectedPre, boolean isSelectedNext) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+
+ if (isSelectedPre) {
+ if (isSelectedNext) {
+ canvas.drawRect(x, cy - mRadius, x + mItemWidth, cy + mRadius, mSelectedPaint);
+ } else {//最后一个,the last
+ canvas.drawRect(x, cy - mRadius, cx, cy + mRadius, mSelectedPaint);
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ }
+ } else {
+ if (isSelectedNext) {
+ canvas.drawRect(cx, cy - mRadius, x + mItemWidth, cy + mRadius, mSelectedPaint);
+ }
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+
+ }
+ return false;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSchemePaint);
+ }
+
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine;
+ int cx = x + mItemWidth / 2;
+ boolean isInRange = isInRange(calendar);
+ boolean isEnable = !onCalendarIntercept(calendar);
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ mSelectTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() && isInRange && isEnable? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() && isInRange && isEnable? mCurMonthTextPaint : mOtherMonthTextPaint);
+ }
+ }
+
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/multi/MultiActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/multi/MultiActivity.java
new file mode 100644
index 00000000..38869513
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/multi/MultiActivity.java
@@ -0,0 +1,246 @@
+package com.haibin.calendarviewproject.multi;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import android.util.Log;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarLayout;
+import com.haibin.calendarview.CalendarView;
+import com.haibin.calendarviewproject.Article;
+import com.haibin.calendarviewproject.ArticleAdapter;
+import com.haibin.calendarviewproject.R;
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+import com.haibin.calendarviewproject.group.GroupItemDecoration;
+import com.haibin.calendarviewproject.group.GroupRecyclerView;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MultiActivity extends BaseActivity implements
+ CalendarView.OnCalendarMultiSelectListener,
+ CalendarView.OnCalendarInterceptListener,
+ CalendarView.OnYearChangeListener,
+ CalendarView.OnMonthChangeListener,
+ CalendarView.OnCalendarSelectListener,
+ View.OnClickListener {
+
+ TextView mTextMonthDay;
+
+ TextView mTextYear;
+
+ TextView mTextLunar;
+
+ TextView mTextCurrentDay;
+
+ CalendarView mCalendarView;
+
+ RelativeLayout mRelativeTool;
+ private int mYear;
+ CalendarLayout mCalendarLayout;
+ GroupRecyclerView mRecyclerView;
+
+ public static void show(Context context) {
+ context.startActivity(new Intent(context, MultiActivity.class));
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_multi;
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void initView() {
+ setStatusBarDarkMode();
+ mTextMonthDay = findViewById(R.id.tv_month_day);
+ mTextYear = findViewById(R.id.tv_year);
+ mTextLunar = findViewById(R.id.tv_lunar);
+ mRelativeTool = findViewById(R.id.rl_tool);
+ mCalendarView = findViewById(R.id.calendarView);
+ mTextCurrentDay = findViewById(R.id.tv_current_day);
+ mTextMonthDay.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!mCalendarLayout.isExpand()) {
+ mCalendarLayout.expand();
+ return;
+ }
+ mCalendarView.showYearSelectLayout(mYear);
+ mTextLunar.setVisibility(View.GONE);
+ mTextYear.setVisibility(View.GONE);
+ mTextMonthDay.setText(String.valueOf(mYear));
+ }
+ });
+ findViewById(R.id.fl_current).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCalendarView.scrollToCurrent();
+ //mCalendarView.setMaxMultiSelectSize(9);
+// Calendar calendar = new Calendar();
+// calendar.setYear(mCalendarView.getCurYear());
+// calendar.setMonth(mCalendarView.getCurMonth());
+// calendar.setDay(mCalendarView.getCurDay());
+// mCalendarView.putMultiSelect(calendar);
+// mCalendarView.removeMultiSelect(calendar);
+// mCalendarView.getMultiSelectCalendars();
+ }
+ });
+ mCalendarLayout = findViewById(R.id.calendarLayout);
+ mCalendarView.setOnCalendarMultiSelectListener(this);
+ mCalendarView.setOnYearChangeListener(this);
+ mCalendarView.setOnCalendarSelectListener(this);
+ //设置日期拦截事件,当前有效
+ mCalendarView.setOnCalendarInterceptListener(this);
+ mCalendarView.setOnMonthChangeListener(this);
+ mTextYear.setText(String.valueOf(mCalendarView.getCurYear()));
+ mYear = mCalendarView.getCurYear();
+ mTextMonthDay.setText(mCalendarView.getCurMonth() + "月" + mCalendarView.getCurDay() + "日");
+ mTextLunar.setText("今日");
+ mTextCurrentDay.setText(String.valueOf(mCalendarView.getCurDay()));
+ findViewById(R.id.iv_clear).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCalendarView.clearMultiSelect();
+ }
+ });
+ }
+
+ @Override
+ protected void initData() {
+ int year = mCalendarView.getCurYear();
+ int month = mCalendarView.getCurMonth();
+
+ Map map = new HashMap<>();
+ map.put(getSchemeCalendar(year, month, 3, 0xFF40db25, "假").toString(),
+ getSchemeCalendar(year, month, 3, 0xFF40db25, "假"));
+ map.put(getSchemeCalendar(year, month, 6, 0xFFe69138, "事").toString(),
+ getSchemeCalendar(year, month, 6, 0xFFe69138, "事"));
+ map.put(getSchemeCalendar(year, month, 9, 0xFFdf1356, "议").toString(),
+ getSchemeCalendar(year, month, 9, 0xFFdf1356, "议"));
+ map.put(getSchemeCalendar(year, month, 13, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 13, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 14, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 14, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 15, 0xFFaacc44, "假").toString(),
+ getSchemeCalendar(year, month, 15, 0xFFaacc44, "假"));
+ map.put(getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记").toString(),
+ getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记"));
+ map.put(getSchemeCalendar(year, month, 25, 0xFF13acf0, "假").toString(),
+ getSchemeCalendar(year, month, 25, 0xFF13acf0, "假"));
+ map.put(getSchemeCalendar(year, month, 27, 0xFF13acf0, "多").toString(),
+ getSchemeCalendar(year, month, 27, 0xFF13acf0, "多"));
+ //此方法在巨大的数据量上不影响遍历性能,推荐使用
+ mCalendarView.setSchemeDate(map);
+
+
+ mRecyclerView = findViewById(R.id.recyclerView);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mRecyclerView.addItemDecoration(new GroupItemDecoration());
+ mRecyclerView.setAdapter(new ArticleAdapter(this));
+ mRecyclerView.notifyDataSetChanged();
+ }
+
+
+ @Override
+ public void onClick(View v) {
+
+ }
+
+ private Calendar getSchemeCalendar(int year, int month, int day, int color, String text) {
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setSchemeColor(color);//如果单独标记颜色、则会使用这个颜色
+ calendar.setScheme(text);
+ calendar.addScheme(new Calendar.Scheme());
+ calendar.addScheme(0xFF008800, "假");
+ calendar.addScheme(0xFF008800, "节");
+ return calendar;
+ }
+
+
+ @Override
+ public void onCalendarMultiSelectOutOfRange(Calendar calendar) {
+ Log.e("OutOfRange", "OutOfRange" + calendar);
+ }
+
+ @Override
+ public void onMultiSelectOutOfSize(Calendar calendar, int maxSize) {
+ Toast.makeText(this, "超过最大选择数量 :" + maxSize, Toast.LENGTH_SHORT).show();
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onCalendarMultiSelect(Calendar calendar, int curSize, int maxSize) {
+ mTextLunar.setVisibility(View.VISIBLE);
+ mTextYear.setVisibility(View.VISIBLE);
+ mTextMonthDay.setText(calendar.getMonth() + "月" + calendar.getDay() + "日");
+ mTextYear.setText(String.valueOf(calendar.getYear()));
+ mTextLunar.setText(calendar.getLunar());
+ mYear = calendar.getYear();
+ Log.e("onDateSelected", " -- " + calendar.getYear() +
+ " -- " + calendar.getMonth() +
+ " -- " + calendar.getDay() +
+ " -- " + " -- " + calendar.getScheme());
+ }
+
+
+ @Override
+ public void onYearChange(int year) {
+ mTextMonthDay.setText(String.valueOf(year));
+ }
+
+
+ @Override
+ public void onMonthChange(int year, int month) {
+ Log.e("onMonthChange", " -- " + year + " -- " + month);
+ }
+
+ /**
+ * 屏蔽某些不可点击的日期,可根据自己的业务自行修改
+ * 如 calendar > 2018年1月1日 && calendar <= 2020年12月31日
+ *
+ * @param calendar calendar
+ * @return 是否屏蔽某些不可点击的日期,MonthView和WeekView有类似的API可调用
+ */
+ @Override
+ public boolean onCalendarIntercept(Calendar calendar) {
+ //Log.e("onCalendarIntercept", calendar.toString());
+ int day = calendar.getDay();
+// return day == 1 || day == 3 || day == 6 || day == 11 ||
+// day == 12 || day == 15 || day == 20 || day == 26;
+// return calendar.hasScheme();
+ return calendar.getYear()<= mCalendarView.getCurYear() &&
+ calendar.getMonth() <= mCalendarView.getCurMonth() &&
+ calendar.getDay() < mCalendarView.getCurDay();
+ }
+
+ @Override
+ public void onCalendarInterceptClick(Calendar calendar, boolean isClick) {
+ Toast.makeText(this,
+ calendar.toString() + (isClick ? "拦截不可点击" : "拦截设定为无效日期"),
+ Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onCalendarOutOfRange(Calendar calendar) {
+
+ }
+
+ @Override
+ public void onCalendarSelect(Calendar calendar, boolean isClick) {
+ Log.e("onDateSelected", " -- " + calendar.getYear() +
+ " -- " + calendar.getMonth() +
+ " -- " + calendar.getDay() +
+ " -- " + isClick + " -- " + calendar.getScheme());
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/pager/CalendarLinearLayout.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/pager/CalendarLinearLayout.java
new file mode 100644
index 00000000..bbc5831e
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/pager/CalendarLinearLayout.java
@@ -0,0 +1,43 @@
+package com.haibin.calendarviewproject.pager;
+
+import android.content.Context;
+import androidx.annotation.Nullable;
+import androidx.viewpager.widget.ViewPager;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+import com.haibin.calendarview.CalendarLayout;
+import com.haibin.calendarviewproject.base.fragment.FragmentAdapter;
+
+/**
+ * 如果嵌套各种View出现事件冲突,可以实现这个方法即可
+ */
+public class CalendarLinearLayout extends LinearLayout implements CalendarLayout.CalendarScrollView {
+
+ private FragmentAdapter mAdapter;
+
+ public CalendarLinearLayout(Context context) {
+ super(context);
+ }
+
+ public CalendarLinearLayout(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ /**
+ * 如果你想让下拉无效,return false
+ *
+ * @return isScrollToTop
+ */
+ @Override
+ public boolean isScrollToTop() {
+ if(mAdapter == null){
+ if (getChildCount() > 1 && getChildAt(1) instanceof ViewPager) {
+ ViewPager viewPager = (ViewPager) getChildAt(1);
+ mAdapter = (FragmentAdapter) viewPager.getAdapter();
+ }
+ }
+ return mAdapter != null && ((PagerFragment) mAdapter.getCurFragment()).isScrollTop();
+ }
+
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/pager/PagerFragment.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/pager/PagerFragment.java
new file mode 100644
index 00000000..ca0d1b0c
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/pager/PagerFragment.java
@@ -0,0 +1,43 @@
+package com.haibin.calendarviewproject.pager;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.haibin.calendarviewproject.Article;
+import com.haibin.calendarviewproject.ArticleAdapter;
+import com.haibin.calendarviewproject.R;
+import com.haibin.calendarviewproject.base.fragment.BaseFragment;
+import com.haibin.calendarviewproject.group.GroupItemDecoration;
+import com.haibin.calendarviewproject.group.GroupRecyclerView;
+
+public class PagerFragment extends BaseFragment {
+
+ private GroupRecyclerView mRecyclerView;
+
+
+ static PagerFragment newInstance() {
+ return new PagerFragment();
+ }
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.fragment_pager;
+ }
+
+ @Override
+ protected void initView() {
+ mRecyclerView = mRootView.findViewById(R.id.recyclerView);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
+ mRecyclerView.addItemDecoration(new GroupItemDecoration());
+ mRecyclerView.setAdapter(new ArticleAdapter(mContext));
+ mRecyclerView.notifyDataSetChanged();
+ }
+
+ @Override
+ protected void initData() {
+
+ }
+
+ boolean isScrollTop() {
+ return mRecyclerView != null && mRecyclerView.computeVerticalScrollOffset() == 0;
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/pager/ViewPagerActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/pager/ViewPagerActivity.java
new file mode 100644
index 00000000..17063273
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/pager/ViewPagerActivity.java
@@ -0,0 +1,191 @@
+package com.haibin.calendarviewproject.pager;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import androidx.fragment.app.Fragment;
+import androidx.viewpager.widget.ViewPager;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.google.android.material.tabs.TabLayout;
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarLayout;
+import com.haibin.calendarview.CalendarView;
+import com.haibin.calendarviewproject.R;
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+import com.haibin.calendarviewproject.base.fragment.FragmentAdapter;
+import com.haibin.calendarviewproject.colorful.ColorfulActivity;
+import com.haibin.calendarviewproject.index.IndexActivity;
+import com.haibin.calendarviewproject.meizu.MeiZuActivity;
+import com.haibin.calendarviewproject.simple.SimpleActivity;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ViewPagerActivity extends BaseActivity implements
+ View.OnClickListener,
+ CalendarView.OnCalendarSelectListener,
+ CalendarView.OnYearChangeListener {
+
+ TextView mTextMonthDay;
+
+ TextView mTextYear;
+
+ TextView mTextLunar;
+
+ TextView mTextCurrentDay;
+
+ CalendarView mCalendarView;
+
+ RelativeLayout mRelativeTool;
+ private int mYear;
+ CalendarLayout mCalendarLayout;
+
+ public static void show(Context context) {
+ context.startActivity(new Intent(context, ViewPagerActivity.class));
+ }
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_view_pager;
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void initView() {
+ setStatusBarDarkMode();
+ mTextMonthDay = findViewById(R.id.tv_month_day);
+ mTextYear = findViewById(R.id.tv_year);
+ mTextLunar = findViewById(R.id.tv_lunar);
+ mRelativeTool = findViewById(R.id.rl_tool);
+ mCalendarView = findViewById(R.id.calendarView);
+ mTextCurrentDay = findViewById(R.id.tv_current_day);
+ mTextMonthDay.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!mCalendarLayout.isExpand()) {
+ mCalendarLayout.expand();
+ return;
+ }
+ mCalendarView.showYearSelectLayout(mYear);
+ mTextLunar.setVisibility(View.GONE);
+ mTextYear.setVisibility(View.GONE);
+ mTextMonthDay.setText(String.valueOf(mYear));
+ }
+ });
+ findViewById(R.id.fl_current).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCalendarView.scrollToCurrent();
+ }
+ });
+ mCalendarLayout = findViewById(R.id.calendarLayout);
+ mCalendarView.setOnCalendarSelectListener(this);
+ mCalendarView.setOnYearChangeListener(this);
+ mTextYear.setText(String.valueOf(mCalendarView.getCurYear()));
+ mYear = mCalendarView.getCurYear();
+ mTextMonthDay.setText(mCalendarView.getCurMonth() + "月" + mCalendarView.getCurDay() + "日");
+ mTextLunar.setText("今日");
+ mTextCurrentDay.setText(String.valueOf(mCalendarView.getCurDay()));
+
+ ViewPager viewPager = findViewById(R.id.viewPager);
+ TabLayout tabLayout = findViewById(R.id.tabLayout);
+ FragmentAdapter adapter = new FragmentAdapter(getSupportFragmentManager());
+ adapter.reset(new String[]{"热门", "头条", "时尚"});
+ List fragments = new ArrayList<>();
+ fragments.add(PagerFragment.newInstance());
+ fragments.add(PagerFragment.newInstance());
+ fragments.add(PagerFragment.newInstance());
+ adapter.reset(fragments);
+ viewPager.setAdapter(adapter);
+ tabLayout.setupWithViewPager(viewPager);
+ }
+
+ @Override
+ protected void initData() {
+ int year = mCalendarView.getCurYear();
+ int month = mCalendarView.getCurMonth();
+
+ Map map = new HashMap<>();
+ map.put(getSchemeCalendar(year, month, 3, 0xFF40db25, "假").toString(),
+ getSchemeCalendar(year, month, 3, 0xFF40db25, "假"));
+ map.put(getSchemeCalendar(year, month, 6, 0xFFe69138, "事").toString(),
+ getSchemeCalendar(year, month, 6, 0xFFe69138, "事"));
+ map.put(getSchemeCalendar(year, month, 9, 0xFFdf1356, "议").toString(),
+ getSchemeCalendar(year, month, 9, 0xFFdf1356, "议"));
+ map.put(getSchemeCalendar(year, month, 13, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 13, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 14, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 14, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 15, 0xFFaacc44, "假").toString(),
+ getSchemeCalendar(year, month, 15, 0xFFaacc44, "假"));
+ map.put(getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记").toString(),
+ getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记"));
+ map.put(getSchemeCalendar(year, month, 25, 0xFF13acf0, "假").toString(),
+ getSchemeCalendar(year, month, 25, 0xFF13acf0, "假"));
+ map.put(getSchemeCalendar(year, month, 27, 0xFF13acf0, "多").toString(),
+ getSchemeCalendar(year, month, 27, 0xFF13acf0, "多"));
+ //此方法在巨大的数据量上不影响遍历性能,推荐使用
+ mCalendarView.setSchemeDate(map);
+
+
+ }
+
+ private Calendar getSchemeCalendar(int year, int month, int day, int color, String text) {
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setSchemeColor(color);//如果单独标记颜色、则会使用这个颜色
+ calendar.setScheme(text);
+ calendar.addScheme(new Calendar.Scheme());
+ calendar.addScheme(0xFF008800, "假");
+ calendar.addScheme(0xFF008800, "节");
+ return calendar;
+ }
+
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.ll_flyme:
+ MeiZuActivity.show(this);
+ break;
+ case R.id.ll_simple:
+ SimpleActivity.show(this);
+ break;
+ case R.id.ll_colorful:
+ ColorfulActivity.show(this);
+ break;
+ case R.id.ll_index:
+ IndexActivity.show(this);
+ break;
+ }
+ }
+
+ @Override
+ public void onYearChange(int year) {
+ mTextMonthDay.setText(String.valueOf(year));
+ }
+
+
+ @Override
+ public void onCalendarOutOfRange(Calendar calendar) {
+
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onCalendarSelect(Calendar calendar, boolean isClick) {
+ mTextLunar.setVisibility(View.VISIBLE);
+ mTextYear.setVisibility(View.VISIBLE);
+ mTextMonthDay.setText(calendar.getMonth() + "月" + calendar.getDay() + "日");
+ mTextYear.setText(String.valueOf(calendar.getYear()));
+ mTextLunar.setText(calendar.getLunar());
+ mYear = calendar.getYear();
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/progress/ProgressActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/progress/ProgressActivity.java
new file mode 100644
index 00000000..bfcb7da1
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/progress/ProgressActivity.java
@@ -0,0 +1,185 @@
+package com.haibin.calendarviewproject.progress;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarLayout;
+import com.haibin.calendarview.CalendarView;
+import com.haibin.calendarviewproject.Article;
+import com.haibin.calendarviewproject.ArticleAdapter;
+import com.haibin.calendarviewproject.R;
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+import com.haibin.calendarviewproject.colorful.ColorfulActivity;
+import com.haibin.calendarviewproject.group.GroupItemDecoration;
+import com.haibin.calendarviewproject.group.GroupRecyclerView;
+import com.haibin.calendarviewproject.index.IndexActivity;
+import com.haibin.calendarviewproject.simple.SimpleActivity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ProgressActivity extends BaseActivity implements
+ CalendarView.OnCalendarSelectListener,
+ CalendarView.OnYearChangeListener,
+ View.OnClickListener {
+
+ TextView mTextMonthDay;
+
+ TextView mTextYear;
+
+ TextView mTextLunar;
+
+ TextView mTextCurrentDay;
+
+ CalendarView mCalendarView;
+
+ RelativeLayout mRelativeTool;
+ private int mYear;
+ CalendarLayout mCalendarLayout;
+ GroupRecyclerView mRecyclerView;
+
+ public static void show(Context context) {
+ context.startActivity(new Intent(context, ProgressActivity.class));
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_progress;
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void initView() {
+ setStatusBarDarkMode();
+ mTextMonthDay = findViewById(R.id.tv_month_day);
+ mTextYear = findViewById(R.id.tv_year);
+ mTextLunar = findViewById(R.id.tv_lunar);
+ mRelativeTool = findViewById(R.id.rl_tool);
+ mCalendarView = findViewById(R.id.calendarView);
+ mTextCurrentDay = findViewById(R.id.tv_current_day);
+ mTextMonthDay.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!mCalendarLayout.isExpand()) {
+ mCalendarLayout.expand();
+ return;
+ }
+ mCalendarView.showYearSelectLayout(mYear);
+ mTextLunar.setVisibility(View.GONE);
+ mTextYear.setVisibility(View.GONE);
+ mTextMonthDay.setText(String.valueOf(mYear));
+ }
+ });
+ findViewById(R.id.fl_current).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCalendarView.scrollToCurrent();
+ }
+ });
+ mCalendarLayout = findViewById(R.id.calendarLayout);
+ mCalendarView.setOnCalendarSelectListener(this);
+ mCalendarView.setOnYearChangeListener(this);
+ mTextYear.setText(String.valueOf(mCalendarView.getCurYear()));
+ mYear = mCalendarView.getCurYear();
+ mTextMonthDay.setText(mCalendarView.getCurMonth() + "月" + mCalendarView.getCurDay() + "日");
+ mTextLunar.setText("今日");
+ mTextCurrentDay.setText(String.valueOf(mCalendarView.getCurDay()));
+ }
+
+ @Override
+ protected void initData() {
+ int year = mCalendarView.getCurYear();
+ int month = mCalendarView.getCurMonth();
+
+
+ Map map = new HashMap<>();
+ map.put(getSchemeCalendar(year, month, 3, 0xFF40db25, "20").toString(),
+ getSchemeCalendar(year, month, 3, 0xFF40db25, "20"));
+ map.put(getSchemeCalendar(year, month, 6, 0xFFe69138, "33").toString(),
+ getSchemeCalendar(year, month, 6, 0xFFe69138, "33"));
+ map.put(getSchemeCalendar(year, month, 9, 0xFFdf1356, "25").toString(),
+ getSchemeCalendar(year, month, 9, 0xFFdf1356, "25"));
+ map.put(getSchemeCalendar(year, month, 13, 0xFFedc56d, "50").toString(),
+ getSchemeCalendar(year, month, 13, 0xFFedc56d, "50"));
+ map.put(getSchemeCalendar(year, month, 14, 0xFFedc56d, "80").toString(),
+ getSchemeCalendar(year, month, 14, 0xFFedc56d, "80"));
+ map.put(getSchemeCalendar(year, month, 15, 0xFFaacc44, "20").toString(),
+ getSchemeCalendar(year, month, 15, 0xFFaacc44, "20"));
+ map.put(getSchemeCalendar(year, month, 18, 0xFFbc13f0, "70").toString(),
+ getSchemeCalendar(year, month, 18, 0xFFbc13f0, "70"));
+ map.put(getSchemeCalendar(year, month, 25, 0xFF13acf0, "36").toString(),
+ getSchemeCalendar(year, month, 25, 0xFF13acf0, "36"));
+ map.put(getSchemeCalendar(year, month, 27, 0xFF13acf0, "95").toString(),
+ getSchemeCalendar(year, month, 27, 0xFF13acf0, "95"));
+ //此方法在巨大的数据量上不影响遍历性能,推荐使用
+ mCalendarView.setSchemeDate(map);
+
+
+
+ mRecyclerView = findViewById(R.id.recyclerView);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mRecyclerView.addItemDecoration(new GroupItemDecoration());
+ mRecyclerView.setAdapter(new ArticleAdapter(this));
+ mRecyclerView.notifyDataSetChanged();
+ }
+
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.ll_flyme:
+ ProgressActivity.show(this);
+ break;
+ case R.id.ll_simple:
+ SimpleActivity.show(this);
+ break;
+ case R.id.ll_colorful:
+ ColorfulActivity.show(this);
+ break;
+ case R.id.ll_index:
+ IndexActivity.show(this);
+ break;
+ }
+ }
+
+ private Calendar getSchemeCalendar(int year, int month, int day, int color, String text) {
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setSchemeColor(color);//如果单独标记颜色、则会使用这个颜色
+ calendar.setScheme(text);
+ return calendar;
+ }
+
+
+ @Override
+ public void onCalendarOutOfRange(Calendar calendar) {
+
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onCalendarSelect(Calendar calendar, boolean isClick) {
+ mTextLunar.setVisibility(View.VISIBLE);
+ mTextYear.setVisibility(View.VISIBLE);
+ mTextMonthDay.setText(calendar.getMonth() + "月" + calendar.getDay() + "日");
+ mTextYear.setText(String.valueOf(calendar.getYear()));
+ mTextLunar.setText(calendar.getLunar());
+ mYear = calendar.getYear();
+ }
+
+ @Override
+ public void onYearChange(int year) {
+ mTextMonthDay.setText(String.valueOf(year));
+ }
+
+
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/progress/ProgressMonthView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/progress/ProgressMonthView.java
new file mode 100644
index 00000000..1f047fb7
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/progress/ProgressMonthView.java
@@ -0,0 +1,110 @@
+package com.haibin.calendarviewproject.progress;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.MonthView;
+
+/**
+ * 精美进度风格
+ * Created by huanghaibin on 2018/2/8.
+ */
+
+public class ProgressMonthView extends MonthView {
+
+ private Paint mProgressPaint = new Paint();
+ private Paint mNoneProgressPaint = new Paint();
+ private int mRadius;
+
+ public ProgressMonthView(Context context) {
+ super(context);
+ mProgressPaint.setAntiAlias(true);
+ mProgressPaint.setStyle(Paint.Style.STROKE);
+ mProgressPaint.setStrokeWidth(dipToPx(context, 2.2f));
+ mProgressPaint.setColor(0xBBf54a00);
+
+ mNoneProgressPaint.setAntiAlias(true);
+ mNoneProgressPaint.setStyle(Paint.Style.STROKE);
+ mNoneProgressPaint.setStrokeWidth(dipToPx(context, 2.2f));
+ mNoneProgressPaint.setColor(0x90CfCfCf);
+ }
+
+ @Override
+ protected void onPreviewHook() {
+ mRadius = Math.min(mItemWidth, mItemHeight) / 11 * 4;
+
+ }
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ return false;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+
+ int angle = getAngle(Integer.parseInt(calendar.getScheme()));
+
+ RectF progressRectF = new RectF(cx - mRadius, cy - mRadius, cx + mRadius, cy + mRadius);
+ canvas.drawArc(progressRectF, -90, angle, false, mProgressPaint);
+
+ RectF noneRectF = new RectF(cx - mRadius, cy - mRadius, cx + mRadius, cy + mRadius);
+ canvas.drawArc(noneRectF, angle - 90, 360 - angle, false, mNoneProgressPaint);
+
+ }
+
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine + y;
+ int cx = x + mItemWidth / 2;
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ mSelectTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ }
+ }
+
+ /**
+ * 获取角度
+ *
+ * @param progress 进度
+ * @return 获取角度
+ */
+ private static int getAngle(int progress) {
+ return (int) (progress * 3.6);
+ }
+
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/progress/ProgressWeekView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/progress/ProgressWeekView.java
new file mode 100644
index 00000000..9b42318a
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/progress/ProgressWeekView.java
@@ -0,0 +1,109 @@
+package com.haibin.calendarviewproject.progress;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.WeekView;
+
+/**
+ * 精美进度风格
+ * Created by huanghaibin on 2018/2/8.
+ */
+
+public class ProgressWeekView extends WeekView {
+
+ private Paint mProgressPaint = new Paint();
+ private Paint mNoneProgressPaint = new Paint();
+ private int mRadius;
+
+ public ProgressWeekView(Context context) {
+ super(context);
+ mProgressPaint.setAntiAlias(true);
+ mProgressPaint.setStyle(Paint.Style.STROKE);
+ mProgressPaint.setStrokeWidth(dipToPx(context, 2.2f));
+ mProgressPaint.setColor(0xBBf54a00);
+
+ mNoneProgressPaint.setAntiAlias(true);
+ mNoneProgressPaint.setStyle(Paint.Style.STROKE);
+ mNoneProgressPaint.setStrokeWidth(dipToPx(context, 2.2f));
+ mNoneProgressPaint.setColor(0x90CfCfCf);
+ }
+
+ @Override
+ protected void onPreviewHook() {
+ mRadius = Math.min(mItemWidth, mItemHeight) / 11 * 4;
+ }
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ return false;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+
+ int angle = getAngle(Integer.parseInt(calendar.getScheme()));
+
+ RectF progressRectF = new RectF(cx - mRadius, cy - mRadius, cx + mRadius, cy + mRadius);
+ canvas.drawArc(progressRectF, -90, angle, false, mProgressPaint);
+
+ RectF noneRectF = new RectF(cx - mRadius, cy - mRadius, cx + mRadius, cy + mRadius);
+ canvas.drawArc(noneRectF, angle - 90, 360 - angle, false, mNoneProgressPaint);
+ }
+
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine;
+ int cx = x + mItemWidth / 2;
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ mSelectTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ }
+ }
+
+ /**
+ * 获取角度
+ *
+ * @param progress 进度
+ * @return 获取角度
+ */
+ private static int getAngle(int progress) {
+ return (int) (progress * 3.6);
+ }
+
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ @SuppressWarnings("all")
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/range/CustomRangeMonthView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/range/CustomRangeMonthView.java
new file mode 100644
index 00000000..3df278d7
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/range/CustomRangeMonthView.java
@@ -0,0 +1,86 @@
+package com.haibin.calendarviewproject.range;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.RangeMonthView;
+
+/**
+ * 范围选择月视图
+ * Created by huanghaibin on 2018/9/13.
+ */
+
+public class CustomRangeMonthView extends RangeMonthView {
+
+ private int mRadius;
+
+ public CustomRangeMonthView(Context context) {
+ super(context);
+ }
+
+
+ @Override
+ protected void onPreviewHook() {
+ mRadius = Math.min(mItemWidth, mItemHeight) / 5 * 2;
+ mSchemePaint.setStyle(Paint.Style.STROKE);
+ }
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme,
+ boolean isSelectedPre, boolean isSelectedNext) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ if (isSelectedPre) {
+ if (isSelectedNext) {
+ canvas.drawRect(x, cy - mRadius, x + mItemWidth, cy + mRadius, mSelectedPaint);
+ } else {//最后一个,the last
+ canvas.drawRect(x, cy - mRadius, cx, cy + mRadius, mSelectedPaint);
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ }
+ } else {
+ if(isSelectedNext){
+ canvas.drawRect(cx, cy - mRadius, x + mItemWidth, cy + mRadius, mSelectedPaint);
+ }
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ //
+ }
+
+ return false;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSchemePaint);
+ }
+
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine + y;
+ int cx = x + mItemWidth / 2;
+
+ boolean isInRange = isInRange(calendar);
+ boolean isEnable = !onCalendarIntercept(calendar);
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ mSelectTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() && isInRange && isEnable? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() && isInRange && isEnable? mCurMonthTextPaint : mOtherMonthTextPaint);
+ }
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/range/CustomRangeWeekView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/range/CustomRangeWeekView.java
new file mode 100644
index 00000000..df86001e
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/range/CustomRangeWeekView.java
@@ -0,0 +1,84 @@
+package com.haibin.calendarviewproject.range;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.RangeWeekView;
+
+/**
+ * 范围选择周视图
+ * Created by huanghaibin on 2018/9/13.
+ */
+
+public class CustomRangeWeekView extends RangeWeekView {
+
+ private int mRadius;
+
+ public CustomRangeWeekView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onPreviewHook() {
+ mRadius = Math.min(mItemWidth, mItemHeight) / 5 * 2;
+ mSchemePaint.setStyle(Paint.Style.STROKE);
+ }
+
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme,
+ boolean isSelectedPre, boolean isSelectedNext) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+
+ if (isSelectedPre) {
+ if (isSelectedNext) {
+ canvas.drawRect(x, cy - mRadius, x + mItemWidth, cy + mRadius, mSelectedPaint);
+ } else {//最后一个,the last
+ canvas.drawRect(x, cy - mRadius, cx, cy + mRadius, mSelectedPaint);
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ }
+ } else {
+ if (isSelectedNext) {
+ canvas.drawRect(cx, cy - mRadius, x + mItemWidth, cy + mRadius, mSelectedPaint);
+ }
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+
+ }
+ return false;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSchemePaint);
+ }
+
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine;
+ int cx = x + mItemWidth / 2;
+ boolean isInRange = isInRange(calendar);
+ boolean isEnable = !onCalendarIntercept(calendar);
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ mSelectTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() && isInRange && isEnable? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() && isInRange && isEnable? mCurMonthTextPaint : mOtherMonthTextPaint);
+ }
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/range/RangeActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/range/RangeActivity.java
new file mode 100644
index 00000000..522e1ef1
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/range/RangeActivity.java
@@ -0,0 +1,252 @@
+package com.haibin.calendarviewproject.range;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarView;
+import com.haibin.calendarviewproject.R;
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RangeActivity extends BaseActivity implements
+ CalendarView.OnCalendarInterceptListener,
+ CalendarView.OnCalendarRangeSelectListener,
+ CalendarView.OnMonthChangeListener,
+ View.OnClickListener {
+
+ TextView mTextLeftDate;
+ TextView mTextLeftWeek;
+
+ TextView mTextRightDate;
+ TextView mTextRightWeek;
+
+ TextView mTextMinRange;
+ TextView mTextMaxRange;
+
+ CalendarView mCalendarView;
+
+ private int mCalendarHeight;
+
+ public static void show(Context context) {
+ context.startActivity(new Intent(context, RangeActivity.class));
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_range;
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void initView() {
+ setStatusBarDarkMode();
+ mTextLeftDate = findViewById(R.id.tv_left_date);
+ mTextLeftWeek = findViewById(R.id.tv_left_week);
+ mTextRightDate = findViewById(R.id.tv_right_date);
+ mTextRightWeek = findViewById(R.id.tv_right_week);
+
+ mTextMinRange = findViewById(R.id.tv_min_range);
+ mTextMaxRange = findViewById(R.id.tv_max_range);
+
+ mCalendarView = findViewById(R.id.calendarView);
+ mCalendarView.setOnCalendarRangeSelectListener(this);
+ mCalendarView.setOnMonthChangeListener(this);
+ //设置日期拦截事件,当前有效
+ mCalendarView.setOnCalendarInterceptListener(this);
+
+ findViewById(R.id.iv_clear).setOnClickListener(this);
+ findViewById(R.id.iv_reduce).setOnClickListener(this);
+ findViewById(R.id.iv_increase).setOnClickListener(this);
+ findViewById(R.id.tv_commit).setOnClickListener(this);
+ findViewById(R.id.tv_title).setOnClickListener(this);
+
+ mCalendarHeight = dipToPx(this, 46);
+
+ mCalendarView.setRange(2000, 1, 1,
+
+ mCalendarView.getCurYear(), mCalendarView.getCurMonth(), mCalendarView.getCurDay()
+
+ );
+ mCalendarView.post(new Runnable() {
+ @Override
+ public void run() {
+ mCalendarView.scrollToCurrent();
+ }
+ });
+ }
+
+ @Override
+ protected void initData() {
+
+ int year = mCalendarView.getCurYear();
+ int month = mCalendarView.getCurMonth();
+ Map map = new HashMap<>();
+ map.put(getSchemeCalendar(year, month, 3, 0xFF40db25, "假").toString(),
+ getSchemeCalendar(year, month, 3, 0xFF40db25, "假"));
+ map.put(getSchemeCalendar(year, month, 6, 0xFFe69138, "事").toString(),
+ getSchemeCalendar(year, month, 6, 0xFFe69138, "事"));
+ map.put(getSchemeCalendar(year, month, 9, 0xFFdf1356, "议").toString(),
+ getSchemeCalendar(year, month, 9, 0xFFdf1356, "议"));
+ map.put(getSchemeCalendar(year, month, 13, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 13, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 14, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 14, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 15, 0xFFaacc44, "假").toString(),
+ getSchemeCalendar(year, month, 15, 0xFFaacc44, "假"));
+ map.put(getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记").toString(),
+ getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记"));
+ map.put(getSchemeCalendar(year, month, 25, 0xFF13acf0, "假").toString(),
+ getSchemeCalendar(year, month, 25, 0xFF13acf0, "假"));
+ map.put(getSchemeCalendar(year, month, 27, 0xFF13acf0, "多").toString(),
+ getSchemeCalendar(year, month, 27, 0xFF13acf0, "多"));
+ //此方法在巨大的数据量上不影响遍历性能,推荐使用
+ mCalendarView.setSchemeDate(map);
+
+ mTextMinRange.setText(String.format("min range = %s", mCalendarView.getMinSelectRange()));
+ mTextMaxRange.setText(String.format("max range = %s", mCalendarView.getMaxSelectRange()));
+ }
+
+ private Calendar getSchemeCalendar(int year, int month, int day, int color, String text) {
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setSchemeColor(color);//如果单独标记颜色、则会使用这个颜色
+ calendar.setScheme(text);
+ calendar.addScheme(new Calendar.Scheme());
+ calendar.addScheme(0xFF008800, "假");
+ calendar.addScheme(0xFF008800, "节");
+ return calendar;
+ }
+
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.tv_title:
+
+ break;
+ case R.id.iv_clear:
+ mCalendarView.clearSelectRange();
+ mTextLeftWeek.setText("开始日期");
+ mTextRightWeek.setText("结束日期");
+ mTextLeftDate.setText("");
+ mTextRightDate.setText("");
+ //mCalendarView.setSelectCalendarRange(2018,10,13,2018,10,13);
+ break;
+ case R.id.iv_reduce:
+
+ mCalendarHeight -= dipToPx(this, 8);
+ if (mCalendarHeight <= dipToPx(this, 46)) {
+ mCalendarHeight = dipToPx(this, 46);
+ }
+ mCalendarView.setCalendarItemHeight(mCalendarHeight);
+ break;
+ case R.id.iv_increase:
+ mCalendarHeight += dipToPx(this, 8);
+ if (mCalendarHeight >= dipToPx(this, 90)) {
+ mCalendarHeight = dipToPx(this, 90);
+ }
+ mCalendarView.setCalendarItemHeight(mCalendarHeight);
+ break;
+ case R.id.tv_commit:
+ List calendars = mCalendarView.getSelectCalendarRange();
+ if (calendars == null || calendars.size() == 0) {
+ return;
+ }
+ for (Calendar c : calendars) {
+ Log.e("SelectCalendarRange", c.toString()
+ + " -- " + c.getScheme()
+ + " -- " + c.getLunar());
+ }
+ Toast.makeText(this, String.format("选择了%s个日期: %s —— %s", calendars.size(),
+ calendars.get(0).toString(), calendars.get(calendars.size() - 1).toString()),
+ Toast.LENGTH_SHORT).show();
+ break;
+ }
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+
+ /**
+ * 屏蔽某些不可点击的日期,可根据自己的业务自行修改
+ * 如 calendar > 2018年1月1日 && calendar <= 2020年12月31日
+ *
+ * @param calendar calendar
+ * @return 是否屏蔽某些不可点击的日期,MonthView和WeekView有类似的API可调用
+ */
+ @Override
+ public boolean onCalendarIntercept(Calendar calendar) {
+ return false;
+ //return calendar.getTimeInMillis()());
+ mRecyclerView.setAdapter(new ArticleAdapter(this));
+ mRecyclerView.notifyDataSetChanged();
+ }
+
+ @Override
+ protected void initData() {
+
+ int year = mCalendarView.getCurYear();
+ int month = mCalendarView.getCurMonth();
+
+ Map map = new HashMap<>();
+ map.put(getSchemeCalendar(year, month, 3, 0xFF40db25, "假").toString(),
+ getSchemeCalendar(year, month, 3, 0xFF40db25, "假"));
+ map.put(getSchemeCalendar(year, month, 6, 0xFFe69138, "事").toString(),
+ getSchemeCalendar(year, month, 6, 0xFFe69138, "事"));
+ map.put(getSchemeCalendar(year, month, 9, 0xFFdf1356, "议").toString(),
+ getSchemeCalendar(year, month, 9, 0xFFdf1356, "议"));
+ map.put(getSchemeCalendar(year, month, 13, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 13, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 14, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 14, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 15, 0xFFaacc44, "假").toString(),
+ getSchemeCalendar(year, month, 15, 0xFFaacc44, "假"));
+ map.put(getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记").toString(),
+ getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记"));
+ map.put(getSchemeCalendar(year, month, 25, 0xFF13acf0, "假").toString(),
+ getSchemeCalendar(year, month, 25, 0xFF13acf0, "假"));
+ map.put(getSchemeCalendar(year, month, 27, 0xFF13acf0, "多").toString(),
+ getSchemeCalendar(year, month, 27, 0xFF13acf0, "多"));
+ //此方法在巨大的数据量上不影响遍历性能,推荐使用
+ mCalendarView.setSchemeDate(map);
+
+ }
+
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.ll_flyme:
+ MeiZuActivity.show(this);
+ break;
+ case R.id.ll_simple:
+ SimpleActivity.show(this);
+ break;
+ case R.id.ll_colorful:
+ ColorfulActivity.show(this);
+ break;
+ case R.id.ll_index:
+ IndexActivity.show(this);
+ break;
+ }
+ }
+
+ private Calendar getSchemeCalendar(int year, int month, int day, int color, String text) {
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setSchemeColor(color);//如果单独标记颜色、则会使用这个颜色
+ calendar.setScheme(text);
+ return calendar;
+ }
+
+ @Override
+ public void onCalendarOutOfRange(Calendar calendar) {
+
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onCalendarSelect(Calendar calendar, boolean isClick) {
+ mTextLunar.setVisibility(View.VISIBLE);
+ mTextYear.setVisibility(View.VISIBLE);
+ mTextMonthDay.setText(calendar.getMonth() + "月" + calendar.getDay() + "日");
+ mTextYear.setText(String.valueOf(calendar.getYear()));
+ mTextLunar.setText(calendar.getLunar());
+ mYear = calendar.getYear();
+ }
+
+ @Override
+ public void onYearChange(int year) {
+ mTextMonthDay.setText(String.valueOf(year));
+ }
+
+
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/simple/SimpleMonthView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/simple/SimpleMonthView.java
new file mode 100644
index 00000000..d4fb782c
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/simple/SimpleMonthView.java
@@ -0,0 +1,72 @@
+package com.haibin.calendarviewproject.simple;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.MonthView;
+
+/**
+ * 高仿魅族日历布局
+ * Created by huanghaibin on 2017/11/15.
+ */
+
+public class SimpleMonthView extends MonthView {
+
+ private int mRadius;
+
+ public SimpleMonthView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onPreviewHook() {
+ mRadius = Math.min(mItemWidth, mItemHeight) / 5 * 2;
+ mSchemePaint.setStyle(Paint.Style.STROKE);
+ }
+
+ @Override
+ protected void onLoopStart(int x, int y) {
+
+ }
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ return false;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSchemePaint);
+ }
+
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine + y;
+ int cx = x + mItemWidth / 2;
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ mSelectTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ }
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/simple/SimpleWeekView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/simple/SimpleWeekView.java
new file mode 100644
index 00000000..f39f9488
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/simple/SimpleWeekView.java
@@ -0,0 +1,66 @@
+package com.haibin.calendarviewproject.simple;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.WeekView;
+
+/**
+ * 简单周视图
+ * Created by huanghaibin on 2017/11/29.
+ */
+
+public class SimpleWeekView extends WeekView {
+ private int mRadius;
+
+
+ public SimpleWeekView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onPreviewHook() {
+ mRadius = Math.min(mItemWidth, mItemHeight) / 5 * 2;
+ mSchemePaint.setStyle(Paint.Style.STROKE);
+ }
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ return false;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSchemePaint);
+ }
+
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine;
+ int cx = x + mItemWidth / 2;
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ mSelectTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mSchemeTextPaint);
+
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mCurMonthTextPaint);
+ }
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/single/SingleActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/single/SingleActivity.java
new file mode 100644
index 00000000..91ea93e4
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/single/SingleActivity.java
@@ -0,0 +1,238 @@
+package com.haibin.calendarviewproject.single;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import android.util.Log;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarLayout;
+import com.haibin.calendarview.CalendarView;
+import com.haibin.calendarviewproject.Article;
+import com.haibin.calendarviewproject.ArticleAdapter;
+import com.haibin.calendarviewproject.R;
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+import com.haibin.calendarviewproject.group.GroupItemDecoration;
+import com.haibin.calendarviewproject.group.GroupRecyclerView;
+import com.haibin.calendarviewproject.index.IndexActivity;
+import com.haibin.calendarviewproject.meizu.MeiZuActivity;
+import com.haibin.calendarviewproject.simple.SimpleActivity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class SingleActivity extends BaseActivity implements
+ CalendarView.OnCalendarSelectListener,
+ CalendarView.OnYearChangeListener,
+ CalendarView.OnMonthChangeListener,
+ CalendarView.OnCalendarInterceptListener,
+ View.OnClickListener {
+
+ TextView mTextMonthDay;
+
+ TextView mTextYear;
+
+ TextView mTextLunar;
+
+ TextView mTextCurrentDay;
+
+ CalendarView mCalendarView;
+
+ RelativeLayout mRelativeTool;
+ private int mYear;
+ CalendarLayout mCalendarLayout;
+ GroupRecyclerView mRecyclerView;
+
+ public static void show(Context context) {
+ context.startActivity(new Intent(context, SingleActivity.class));
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_single;
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void initView() {
+ setStatusBarDarkMode();
+ mTextMonthDay = findViewById(R.id.tv_month_day);
+ mTextYear = findViewById(R.id.tv_year);
+ mTextLunar = findViewById(R.id.tv_lunar);
+ mRelativeTool = findViewById(R.id.rl_tool);
+ mCalendarView = findViewById(R.id.calendarView);
+ mTextCurrentDay = findViewById(R.id.tv_current_day);
+ mTextMonthDay.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mYear == 0) {
+ mYear = mCalendarView.getCurYear();
+ }
+ if (!mCalendarLayout.isExpand()) {
+ mCalendarLayout.expand();
+ return;
+ }
+ mCalendarView.showYearSelectLayout(mYear);
+ mTextLunar.setVisibility(View.GONE);
+ mTextYear.setVisibility(View.GONE);
+ mTextMonthDay.setText(String.valueOf(mYear));
+ }
+ });
+ findViewById(R.id.fl_current).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ //mCalendarView.clearSingleSelect();
+ mCalendarView.scrollToCurrent();
+ Log.e("onDateSelected",
+ " -- " + mCalendarView.getSelectedCalendar().getScheme() +
+ " -- " + mCalendarView.getSelectedCalendar().toString() +
+ " -- " + mCalendarView.getSelectedCalendar().hasScheme());
+ }
+ });
+ mCalendarLayout = findViewById(R.id.calendarLayout);
+ mCalendarView.setOnCalendarSelectListener(this);
+ mCalendarView.setOnYearChangeListener(this);
+ mCalendarView.setOnMonthChangeListener(this);
+
+ //设置日期拦截事件,仅适用单选模式,当前有效
+ mCalendarView.setOnCalendarInterceptListener(this);
+
+ mTextYear.setText(String.valueOf(mCalendarView.getCurYear()));
+ mYear = mCalendarView.getCurYear();
+ mTextMonthDay.setText(mCalendarView.getCurMonth() + "月" + mCalendarView.getCurDay() + "日");
+ mTextLunar.setText("今日");
+ mTextCurrentDay.setText(String.valueOf(mCalendarView.getCurDay()));
+ }
+
+ @Override
+ protected void initData() {
+ int year = mCalendarView.getCurYear();
+ int month = mCalendarView.getCurMonth();
+
+ Map map = new HashMap<>();
+ map.put(getSchemeCalendar(year, month, 3, 0xFF40db25, "假").toString(),
+ getSchemeCalendar(year, month, 3, 0xFF40db25, "假"));
+ map.put(getSchemeCalendar(year, month, 6, 0xFFe69138, "事").toString(),
+ getSchemeCalendar(year, month, 6, 0xFFe69138, "事"));
+ map.put(getSchemeCalendar(year, month, 9, 0xFFdf1356, "议").toString(),
+ getSchemeCalendar(year, month, 9, 0xFFdf1356, "议"));
+ map.put(getSchemeCalendar(year, month, 13, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 13, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 14, 0xFFedc56d, "记").toString(),
+ getSchemeCalendar(year, month, 14, 0xFFedc56d, "记"));
+ map.put(getSchemeCalendar(year, month, 15, 0xFFaacc44, "假").toString(),
+ getSchemeCalendar(year, month, 15, 0xFFaacc44, "假"));
+ map.put(getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记").toString(),
+ getSchemeCalendar(year, month, 18, 0xFFbc13f0, "记"));
+ map.put(getSchemeCalendar(year, month, 25, 0xFF13acf0, "假").toString(),
+ getSchemeCalendar(year, month, 25, 0xFF13acf0, "假"));
+ map.put(getSchemeCalendar(year, month, 27, 0xFF13acf0, "多").toString(),
+ getSchemeCalendar(year, month, 27, 0xFF13acf0, "多"));
+ //此方法在巨大的数据量上不影响遍历性能,推荐使用
+ mCalendarView.setSchemeDate(map);
+
+
+ mRecyclerView = findViewById(R.id.recyclerView);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mRecyclerView.addItemDecoration(new GroupItemDecoration());
+ mRecyclerView.setAdapter(new ArticleAdapter(this));
+ mRecyclerView.notifyDataSetChanged();
+ }
+
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.ll_flyme:
+ MeiZuActivity.show(this);
+ break;
+ case R.id.ll_simple:
+ SimpleActivity.show(this);
+ break;
+ case R.id.ll_colorful:
+ SingleActivity.show(this);
+ break;
+ case R.id.ll_index:
+ IndexActivity.show(this);
+ break;
+ }
+ }
+
+ private Calendar getSchemeCalendar(int year, int month, int day, int color, String text) {
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setSchemeColor(color);//如果单独标记颜色、则会使用这个颜色
+ calendar.setScheme(text);
+ return calendar;
+ }
+
+
+ @Override
+ public void onCalendarOutOfRange(Calendar calendar) {
+
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onCalendarSelect(Calendar calendar, boolean isClick) {
+ mTextLunar.setVisibility(View.VISIBLE);
+ mTextYear.setVisibility(View.VISIBLE);
+ mTextMonthDay.setText(calendar.getMonth() + "月" + calendar.getDay() + "日");
+ mTextYear.setText(String.valueOf(calendar.getYear()));
+ mTextLunar.setText(calendar.getLunar());
+ mYear = calendar.getYear();
+ Log.e("onDateSelected", " -- " + calendar.getYear() +
+ " -- " + calendar.getMonth() +
+ " -- " + calendar.getDay() +
+ " -- " + isClick);
+ if (!calendar.isAvailable()) {
+ mTextLunar.setText("");
+ mTextYear.setText("");
+ mTextMonthDay.setText("无效日期");
+ }
+ }
+
+ @Override
+ public void onMonthChange(int year, int month) {
+ Log.e("onMonthChange", " -- " + year + " -- " + month);
+ }
+
+ /**
+ * 屏蔽某些不可点击的日期,可根据自己的业务自行修改
+ * 如 calendar > 2018年1月1日 && calendar <= 2020年12月31日
+ *
+ * @param calendar calendar
+ * @return 是否屏蔽某些不可点击的日期,MonthView和WeekView有类似的API可调用
+ */
+ @Override
+ public boolean onCalendarIntercept(Calendar calendar) {
+ //Log.e("onCalendarIntercept", calendar.toString());
+ int day = calendar.getDay();
+ return day == 1 || day == 3 || day == 6 || day == 11 ||
+ day == 12 || day == 15 || day == 20 || day == 26;
+ }
+
+ @Override
+ public void onCalendarInterceptClick(Calendar calendar, boolean isClick) {
+ Toast.makeText(this,
+ calendar.toString() + (isClick ? "拦截不可点击" : "拦截滚动到无效日期"),
+ Toast.LENGTH_SHORT).show();
+ }
+
+
+ @Override
+ public void onYearChange(int year) {
+ mTextMonthDay.setText(String.valueOf(year));
+ Log.e("onYearChange", " 年份变化 " + year);
+ }
+
+
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/single/SingleMonthView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/single/SingleMonthView.java
new file mode 100644
index 00000000..721987c5
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/single/SingleMonthView.java
@@ -0,0 +1,128 @@
+package com.haibin.calendarviewproject.single;
+
+import android.content.Context;
+import android.graphics.BlurMaskFilter;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.view.View;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.MonthView;
+
+/**
+ * 高仿魅族日历布局
+ * Created by huanghaibin on 2017/11/15.
+ */
+
+public class SingleMonthView extends MonthView {
+
+ private int mRadius;
+ private Paint mRingPaint = new Paint();
+ private int mRingRadius;
+
+ /**
+ * 不可用画笔
+ */
+ private Paint mDisablePaint = new Paint();
+
+ private int mH;
+
+ public SingleMonthView(Context context) {
+ super(context);
+
+ mRingPaint.setAntiAlias(true);
+ mRingPaint.setColor(mSchemePaint.getColor());
+ mRingPaint.setStyle(Paint.Style.STROKE);
+ mRingPaint.setStrokeWidth(dipToPx(context, 1));
+ setLayerType(View.LAYER_TYPE_SOFTWARE, mRingPaint);
+ mRingPaint.setMaskFilter(new BlurMaskFilter(30, BlurMaskFilter.Blur.SOLID));
+
+ mDisablePaint.setColor(0xFF9f9f9f);
+ mDisablePaint.setAntiAlias(true);
+ mDisablePaint.setStrokeWidth(dipToPx(context,2));
+ mDisablePaint.setFakeBoldText(true);
+
+ mH = dipToPx(context, 18);
+
+ }
+
+ @Override
+ protected void onPreviewHook() {
+ mRadius = Math.min(mItemWidth, mItemHeight) / 6 * 2;
+ mRingRadius = Math.min(mItemWidth, mItemHeight) / 5 * 2;
+ mSelectTextPaint.setTextSize(dipToPx(getContext(),17));
+ }
+
+ /**
+ * 如果需要点击Scheme没有效果,则return true
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @return false 则不绘制onDrawScheme,因为这里背景色是互斥的
+ */
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ canvas.drawCircle(cx, cy, mRingRadius, mRingPaint);
+
+ return true;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+// int cx = x + mItemWidth / 2;
+// int cy = y + mItemHeight / 2;
+ //canvas.drawCircle(cx, cy, mRadius, mSchemePaint);
+ }
+
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine + y - dipToPx(getContext(),1);
+ int cx = x + mItemWidth / 2;
+ if (isSelected) {
+ canvas.drawText(calendar.isCurrentDay() ? "今" : "选",
+ cx,
+ baselineY,
+ mSelectTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(calendar.isCurrentDay() ? "今" : String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ } else {
+ canvas.drawText(calendar.isCurrentDay() ? "今" : String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ }
+
+ //日期是否可用?拦截
+ if (onCalendarIntercept(calendar)) {
+ canvas.drawLine(x + mH, y + mH, x + mItemWidth - mH, y + mItemHeight - mH, mDisablePaint);
+ }
+
+ }
+
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/single/SingleWeekView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/single/SingleWeekView.java
new file mode 100644
index 00000000..eee75ddc
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/single/SingleWeekView.java
@@ -0,0 +1,123 @@
+package com.haibin.calendarviewproject.single;
+
+import android.content.Context;
+import android.graphics.BlurMaskFilter;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.view.View;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.WeekView;
+
+/**
+ * 多彩周视图
+ * Created by huanghaibin on 2017/11/29.
+ */
+
+public class SingleWeekView extends WeekView {
+
+ private int mRadius;
+ private Paint mRingPaint = new Paint();
+ private int mRingRadius;
+
+ /**
+ * 不可用画笔
+ */
+ private Paint mDisablePaint = new Paint();
+
+ private int mH;
+
+ public SingleWeekView(Context context) {
+ super(context);
+
+ mRingPaint.setAntiAlias(true);
+ mRingPaint.setColor(mSchemePaint.getColor());
+ mRingPaint.setStyle(Paint.Style.STROKE);
+ mRingPaint.setStrokeWidth(dipToPx(context, 1));
+ setLayerType(View.LAYER_TYPE_SOFTWARE, mRingPaint);
+ mRingPaint.setMaskFilter(new BlurMaskFilter(30, BlurMaskFilter.Blur.SOLID));
+
+ mDisablePaint.setColor(0xFF9f9f9f);
+ mDisablePaint.setAntiAlias(true);
+ mDisablePaint.setStrokeWidth(dipToPx(context,2));
+ mDisablePaint.setFakeBoldText(true);
+
+ mH = dipToPx(context, 18);
+ }
+
+ @Override
+ protected void onPreviewHook() {
+ mRadius = Math.min(mItemWidth, mItemHeight) / 6 * 2;
+ mRingRadius = Math.min(mItemWidth, mItemHeight) / 5 * 2;
+ mSelectTextPaint.setTextSize(dipToPx(getContext(),17));
+ }
+
+ /**
+ * 如果需要点击Scheme没有效果,则return true
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @return false 则不绘制onDrawScheme,因为这里背景色是互斥的
+ */
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ canvas.drawCircle(cx, cy, mRingRadius, mRingPaint);
+ return true;
+ }
+
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x) {
+// int cx = x + mItemWidth / 2;
+// int cy = mItemHeight / 2;
+ //canvas.drawCircle(cx, cy, mRadius, mSchemePaint);
+ }
+
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine - dipToPx(getContext(), 1);
+ int cx = x + mItemWidth / 2;
+ if (isSelected) {
+ canvas.drawText(calendar.isCurrentDay() ? "今" : "选",
+ cx,
+ baselineY,
+ mSelectTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(calendar.isCurrentDay() ? "今" : String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ } else {
+ canvas.drawText(calendar.isCurrentDay() ? "今" : String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ }
+
+ //日期是否可用?拦截
+ if (onCalendarIntercept(calendar)) {
+ canvas.drawLine(x + mH, mH, x + mItemWidth - mH, mItemHeight - mH, mDisablePaint);
+ }
+ }
+
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/solay/SolarActivity.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/solay/SolarActivity.java
new file mode 100644
index 00000000..3cef2783
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/solay/SolarActivity.java
@@ -0,0 +1,190 @@
+package com.haibin.calendarviewproject.solay;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.os.Build;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.CalendarLayout;
+import com.haibin.calendarview.CalendarView;
+import com.haibin.calendarviewproject.Article;
+import com.haibin.calendarviewproject.ArticleAdapter;
+import com.haibin.calendarviewproject.R;
+import com.haibin.calendarviewproject.base.activity.BaseActivity;
+import com.haibin.calendarviewproject.colorful.ColorfulActivity;
+import com.haibin.calendarviewproject.group.GroupItemDecoration;
+import com.haibin.calendarviewproject.group.GroupRecyclerView;
+import com.haibin.calendarviewproject.index.IndexActivity;
+import com.haibin.calendarviewproject.simple.SimpleActivity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class SolarActivity extends BaseActivity implements
+ CalendarView.OnCalendarSelectListener,
+ CalendarView.OnYearChangeListener,
+ View.OnClickListener {
+
+ TextView mTextMonthDay;
+
+ TextView mTextYear;
+
+ TextView mTextLunar;
+
+ TextView mTextCurrentDay;
+
+ CalendarView mCalendarView;
+
+ RelativeLayout mRelativeTool;
+ private int mYear;
+ CalendarLayout mCalendarLayout;
+ GroupRecyclerView mRecyclerView;
+
+ public static void show(Context context) {
+ context.startActivity(new Intent(context, SolarActivity.class));
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_solay;
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void initView() {
+ if (Build.VERSION.SDK_INT >= 21) {
+ getWindow().setStatusBarColor(getResources().getColor(R.color.solar_background));
+ }
+ mTextMonthDay = findViewById(R.id.tv_month_day);
+ mTextYear = findViewById(R.id.tv_year);
+ mTextLunar = findViewById(R.id.tv_lunar);
+ mRelativeTool = findViewById(R.id.rl_tool);
+ mCalendarView = findViewById(R.id.calendarView);
+ mTextCurrentDay = findViewById(R.id.tv_current_day);
+ mTextMonthDay.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!mCalendarLayout.isExpand()) {
+ mCalendarLayout.expand();
+ return;
+ }
+ mCalendarView.showYearSelectLayout(mYear);
+ mTextLunar.setVisibility(View.GONE);
+ mTextYear.setVisibility(View.GONE);
+ mTextMonthDay.setText(String.valueOf(mYear));
+ }
+ });
+ findViewById(R.id.fl_current).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCalendarView.scrollToCurrent();
+ }
+ });
+ mCalendarLayout = findViewById(R.id.calendarLayout);
+ mCalendarView.setOnCalendarSelectListener(this);
+ mCalendarView.setOnYearChangeListener(this);
+ mTextYear.setText(String.valueOf(mCalendarView.getCurYear()));
+ mYear = mCalendarView.getCurYear();
+ mTextMonthDay.setText(mCalendarView.getCurMonth() + "月" + mCalendarView.getCurDay() + "日");
+ mTextLunar.setText("今日");
+ mTextCurrentDay.setText(String.valueOf(mCalendarView.getCurDay()));
+ }
+
+ @Override
+ protected void initData() {
+ int year = mCalendarView.getCurYear();
+ int month = mCalendarView.getCurMonth();
+
+ Map map = new HashMap<>();
+ map.put(getSchemeCalendar(year, month, 3, "假").toString(),
+ getSchemeCalendar(year, month, 3, "假"));
+ map.put(getSchemeCalendar(year, month, 6, "事").toString(),
+ getSchemeCalendar(year, month, 6, "事"));
+ map.put(getSchemeCalendar(year, month, 9, "议").toString(),
+ getSchemeCalendar(year, month, 9, "议"));
+ map.put(getSchemeCalendar(year, month, 13, "记").toString(),
+ getSchemeCalendar(year, month, 13, "记"));
+ map.put(getSchemeCalendar(year, month, 14, "记").toString(),
+ getSchemeCalendar(year, month, 14, "记"));
+ map.put(getSchemeCalendar(year, month, 15, "假").toString(),
+ getSchemeCalendar(year, month, 15, "假"));
+ map.put(getSchemeCalendar(year, month, 18, "记").toString(),
+ getSchemeCalendar(year, month, 18, "记"));
+ map.put(getSchemeCalendar(year, month, 25, "假").toString(),
+ getSchemeCalendar(year, month, 25, "假"));
+ map.put(getSchemeCalendar(year, month, 27, "多").toString(),
+ getSchemeCalendar(year, month, 27, "多"));
+ //此方法在巨大的数据量上不影响遍历性能,推荐使用
+ mCalendarView.setSchemeDate(map);
+
+
+ mRecyclerView = findViewById(R.id.recyclerView);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mRecyclerView.addItemDecoration(new GroupItemDecoration());
+ mRecyclerView.setAdapter(new ArticleAdapter(this));
+ mRecyclerView.notifyDataSetChanged();
+ }
+
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.ll_flyme:
+ SolarActivity.show(this);
+ break;
+ case R.id.ll_simple:
+ SimpleActivity.show(this);
+ break;
+ case R.id.ll_colorful:
+ ColorfulActivity.show(this);
+ break;
+ case R.id.ll_index:
+ IndexActivity.show(this);
+ break;
+ }
+ }
+
+ private Calendar getSchemeCalendar(int year, int month, int day, String text) {
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setSchemeColor(Color.WHITE);
+ calendar.setScheme(text);
+ calendar.addScheme(0xFFa8b015, "rightTop");
+ calendar.addScheme(0xFF423cb0, "leftTop");
+ calendar.addScheme(0xFF643c8c, "bottom");
+
+ return calendar;
+ }
+
+ @Override
+ public void onCalendarOutOfRange(Calendar calendar) {
+
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onCalendarSelect(Calendar calendar, boolean isClick) {
+ mTextLunar.setVisibility(View.VISIBLE);
+ mTextYear.setVisibility(View.VISIBLE);
+ mTextMonthDay.setText(calendar.getMonth() + "月" + calendar.getDay() + "日");
+ mTextYear.setText(String.valueOf(calendar.getYear()));
+ mTextLunar.setText(calendar.getLunar());
+ mYear = calendar.getYear();
+ }
+
+ @Override
+ public void onYearChange(int year) {
+ mTextMonthDay.setText(String.valueOf(year));
+ }
+
+
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/solay/SolarMonthView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/solay/SolarMonthView.java
new file mode 100644
index 00000000..de428094
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/solay/SolarMonthView.java
@@ -0,0 +1,114 @@
+package com.haibin.calendarviewproject.solay;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.MonthView;
+
+import java.util.List;
+
+/**
+ * 精致星系月视图,利用三角函数计算坐标
+ * solar MonthView ,using trigonometric function
+ * Created by huanghaibin on 2018/2/8.
+ */
+
+public class SolarMonthView extends MonthView {
+
+
+ private Paint mPointPaint = new Paint();
+
+ private int mRadius;
+ private int mPointRadius;
+
+ public SolarMonthView(Context context) {
+ super(context);
+
+
+ mPointPaint.setAntiAlias(true);
+ mPointPaint.setStyle(Paint.Style.FILL);
+ mSchemePaint.setStyle(Paint.Style.STROKE);
+ mSchemePaint.setStrokeWidth(dipToPx(context, 1.2f));
+ mSchemePaint.setColor(0xFFFFFFFF);
+ mPointRadius = dipToPx(context, 3.6f);
+ mPointPaint.setColor(Color.RED);
+
+ }
+
+ @Override
+ protected void onPreviewHook() {
+ mRadius = Math.min(mItemWidth, mItemHeight) / 5 * 2;
+ }
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ return false;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+ int cx = x + mItemWidth / 2;
+ int cy = y + mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSchemePaint);
+
+ List schemes = calendar.getSchemes();
+
+ mPointPaint.setColor(schemes.get(0).getShcemeColor());//You can also use three fixed Paint 你也可以使用三个Paint对象
+ int rightTopX = (int) (cx + mRadius * Math.cos(-10 * Math.PI / 180));
+ int rightTopY = (int) (cy + mRadius * Math.sin(-10 * Math.PI / 180));
+ canvas.drawCircle(rightTopX, rightTopY, mPointRadius, mPointPaint);
+
+ mPointPaint.setColor(schemes.get(1).getShcemeColor());
+ int leftTopX = (int) (cx + mRadius * Math.cos(-140 * Math.PI / 180));
+ int leftTopY = (int) (cy + mRadius * Math.sin(-140 * Math.PI / 180));
+ canvas.drawCircle(leftTopX, leftTopY, mPointRadius, mPointPaint);
+
+ mPointPaint.setColor(schemes.get(2).getShcemeColor());
+ int bottomX = (int) (cx + mRadius * Math.cos(100 * Math.PI / 180));
+ int bottomY = (int) (cy + mRadius * Math.sin(100 * Math.PI / 180));
+ canvas.drawCircle(bottomX, bottomY, mPointRadius, mPointPaint);
+
+ }
+
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine + y;
+ int cx = x + mItemWidth / 2;
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ mSelectTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ }
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/solay/SolarWeekBar.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/solay/SolarWeekBar.java
new file mode 100644
index 00000000..9254170c
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/solay/SolarWeekBar.java
@@ -0,0 +1,53 @@
+package com.haibin.calendarviewproject.solay;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.widget.TextView;
+
+import com.haibin.calendarview.WeekBar;
+import com.haibin.calendarviewproject.R;
+
+/**
+ * 自定义英文栏
+ * Created by huanghaibin on 2017/11/30.
+ */
+
+public class SolarWeekBar extends WeekBar {
+
+ public SolarWeekBar(Context context) {
+ super(context);
+ LayoutInflater.from(context).inflate(R.layout.solar_week_bar, this, true);
+ setBackgroundColor(context.getResources().getColor(R.color.solar_background));
+ }
+
+ /**
+ * 当周起始发生变化,使用自定义布局需要重写这个方法,避免出问题
+ *
+ * @param weekStart 周起始
+ */
+ @Override
+ protected void onWeekStartChange(int weekStart) {
+ for (int i = 0; i < getChildCount(); i++) {
+ ((TextView) getChildAt(i)).setText(getWeekString(i, weekStart));
+ }
+ }
+
+ /**
+ * 或者周文本,这个方法仅供父类使用
+ *
+ * @param index index
+ * @param weekStart weekStart
+ * @return 或者周文本
+ */
+ private String getWeekString(int index, int weekStart) {
+ String[] weeks = getContext().getResources().getStringArray(R.array.english_week_string_array);
+
+ if (weekStart == 1) {
+ return weeks[index];
+ }
+ if (weekStart == 2) {
+ return weeks[index == 6 ? 0 : index + 1];
+ }
+ return weeks[index == 0 ? 6 : index - 1];
+ }
+}
diff --git a/CalendarView/app/src/main/java/com/haibin/calendarviewproject/solay/SolarWeekView.java b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/solay/SolarWeekView.java
new file mode 100644
index 00000000..aacad281
--- /dev/null
+++ b/CalendarView/app/src/main/java/com/haibin/calendarviewproject/solay/SolarWeekView.java
@@ -0,0 +1,111 @@
+package com.haibin.calendarviewproject.solay;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+
+import com.haibin.calendarview.Calendar;
+import com.haibin.calendarview.WeekView;
+
+import java.util.List;
+
+/**
+ * 精致周视图,using trigonometric function
+ * Created by huanghaibin on 2018/2/8.
+ */
+
+public class SolarWeekView extends WeekView {
+
+
+ private Paint mPointPaint = new Paint();
+
+ private int mRadius;
+ private int mPointRadius;
+
+ public SolarWeekView(Context context) {
+ super(context);
+
+ mPointPaint.setAntiAlias(true);
+ mPointPaint.setStyle(Paint.Style.FILL);
+ mSchemePaint.setStyle(Paint.Style.STROKE);
+ mSchemePaint.setStrokeWidth(dipToPx(context, 1.2f));
+ mSchemePaint.setColor(0xFFFFFFFF);
+ mPointRadius = dipToPx(context, 3.6f);
+ mPointPaint.setColor(Color.RED);
+
+ }
+
+ @Override
+ protected void onPreviewHook() {
+ mRadius = Math.min(mItemWidth, mItemHeight) / 5 * 2;
+ }
+
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
+ return false;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x) {
+ int cx = x + mItemWidth / 2;
+ int cy = mItemHeight / 2;
+ canvas.drawCircle(cx, cy, mRadius, mSchemePaint);
+
+ List schemes = calendar.getSchemes();
+
+ mPointPaint.setColor(schemes.get(0).getShcemeColor());//You can also use three fixed Paint 你也可以使用三个Paint对象
+ int rightTopX = (int) (cx + mRadius * Math.cos(-10 * Math.PI / 180));
+ int rightTopY = (int) (cy + mRadius * Math.sin(-10 * Math.PI / 180));
+ canvas.drawCircle(rightTopX, rightTopY, mPointRadius, mPointPaint);
+
+ mPointPaint.setColor(schemes.get(1).getShcemeColor());
+ int leftTopX = (int) (cx + mRadius * Math.cos(-140 * Math.PI / 180));
+ int leftTopY = (int) (cy + mRadius * Math.sin(-140 * Math.PI / 180));
+ canvas.drawCircle(leftTopX, leftTopY, mPointRadius, mPointPaint);
+
+ mPointPaint.setColor(schemes.get(2).getShcemeColor());
+ int bottomX = (int) (cx + mRadius * Math.cos(100 * Math.PI / 180));
+ int bottomY = (int) (cy + mRadius * Math.sin(100 * Math.PI / 180));
+ canvas.drawCircle(bottomX, bottomY, mPointRadius, mPointPaint);
+ }
+
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine;
+ int cx = x + mItemWidth / 2;
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ mSelectTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mSchemeTextPaint);
+
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mCurMonthTextPaint);
+ }
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ private static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/app/src/main/res/color/week_bar_color.xml b/CalendarView/app/src/main/res/color/week_bar_color.xml
new file mode 100644
index 00000000..a8109669
--- /dev/null
+++ b/CalendarView/app/src/main/res/color/week_bar_color.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/app/src/main/res/drawable-v21/bg_material.xml b/CalendarView/app/src/main/res/drawable-v21/bg_material.xml
new file mode 100644
index 00000000..f324ec87
--- /dev/null
+++ b/CalendarView/app/src/main/res/drawable-v21/bg_material.xml
@@ -0,0 +1,9 @@
+
+
+ -
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/drawable-v21/bg_ripple.xml b/CalendarView/app/src/main/res/drawable-v21/bg_ripple.xml
new file mode 100644
index 00000000..4dd3b129
--- /dev/null
+++ b/CalendarView/app/src/main/res/drawable-v21/bg_ripple.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/CalendarView/app/src/main/res/drawable/bg_material.xml b/CalendarView/app/src/main/res/drawable/bg_material.xml
new file mode 100644
index 00000000..20fd151a
--- /dev/null
+++ b/CalendarView/app/src/main/res/drawable/bg_material.xml
@@ -0,0 +1,13 @@
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/drawable/bg_ripple.xml b/CalendarView/app/src/main/res/drawable/bg_ripple.xml
new file mode 100644
index 00000000..20fd151a
--- /dev/null
+++ b/CalendarView/app/src/main/res/drawable/bg_ripple.xml
@@ -0,0 +1,13 @@
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/layout/activity_calendar.xml b/CalendarView/app/src/main/res/layout/activity_calendar.xml
new file mode 100644
index 00000000..3d1cb910
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_calendar.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/app/src/main/res/layout/activity_colorful.xml b/CalendarView/app/src/main/res/layout/activity_colorful.xml
new file mode 100644
index 00000000..30e22924
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_colorful.xml
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/layout/activity_custom.xml b/CalendarView/app/src/main/res/layout/activity_custom.xml
new file mode 100644
index 00000000..3dafc25a
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_custom.xml
@@ -0,0 +1,153 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/layout/activity_full.xml b/CalendarView/app/src/main/res/layout/activity_full.xml
new file mode 100644
index 00000000..1a478843
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_full.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/app/src/main/res/layout/activity_index.xml b/CalendarView/app/src/main/res/layout/activity_index.xml
new file mode 100644
index 00000000..b9e5d8d6
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_index.xml
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/layout/activity_main.xml b/CalendarView/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..6c083d20
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,761 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/layout/activity_meizu.xml b/CalendarView/app/src/main/res/layout/activity_meizu.xml
new file mode 100644
index 00000000..b779d9d1
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_meizu.xml
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/layout/activity_mix.xml b/CalendarView/app/src/main/res/layout/activity_mix.xml
new file mode 100644
index 00000000..2bcb6236
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_mix.xml
@@ -0,0 +1,153 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/layout/activity_multi.xml b/CalendarView/app/src/main/res/layout/activity_multi.xml
new file mode 100644
index 00000000..d87e38e2
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_multi.xml
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/layout/activity_progress.xml b/CalendarView/app/src/main/res/layout/activity_progress.xml
new file mode 100644
index 00000000..27f83273
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_progress.xml
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/layout/activity_range.xml b/CalendarView/app/src/main/res/layout/activity_range.xml
new file mode 100644
index 00000000..0d91e19e
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_range.xml
@@ -0,0 +1,253 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/layout/activity_simple.xml b/CalendarView/app/src/main/res/layout/activity_simple.xml
new file mode 100644
index 00000000..969a1348
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_simple.xml
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/layout/activity_single.xml b/CalendarView/app/src/main/res/layout/activity_single.xml
new file mode 100644
index 00000000..0145065d
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_single.xml
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/layout/activity_solay.xml b/CalendarView/app/src/main/res/layout/activity_solay.xml
new file mode 100644
index 00000000..b1eee4a0
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_solay.xml
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/layout/activity_test.xml b/CalendarView/app/src/main/res/layout/activity_test.xml
new file mode 100644
index 00000000..a5b9c51d
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_test.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/app/src/main/res/layout/activity_view_pager.xml b/CalendarView/app/src/main/res/layout/activity_view_pager.xml
new file mode 100644
index 00000000..35b1a808
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/activity_view_pager.xml
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CalendarView/app/src/main/res/layout/custom_week_bar.xml b/CalendarView/app/src/main/res/layout/custom_week_bar.xml
new file mode 100644
index 00000000..cfaa3418
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/custom_week_bar.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/app/src/main/res/layout/english_week_bar.xml b/CalendarView/app/src/main/res/layout/english_week_bar.xml
new file mode 100644
index 00000000..a6a5c4ab
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/english_week_bar.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/app/src/main/res/layout/fragment_pager.xml b/CalendarView/app/src/main/res/layout/fragment_pager.xml
new file mode 100644
index 00000000..5262bba0
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/fragment_pager.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/app/src/main/res/layout/item_list_article.xml b/CalendarView/app/src/main/res/layout/item_list_article.xml
new file mode 100644
index 00000000..abc1e206
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/item_list_article.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/app/src/main/res/layout/item_list_article_group.xml b/CalendarView/app/src/main/res/layout/item_list_article_group.xml
new file mode 100644
index 00000000..c29272a6
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/item_list_article_group.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/app/src/main/res/layout/item_list_string.xml b/CalendarView/app/src/main/res/layout/item_list_string.xml
new file mode 100644
index 00000000..cf7ec6a9
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/item_list_string.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/app/src/main/res/layout/solar_week_bar.xml b/CalendarView/app/src/main/res/layout/solar_week_bar.xml
new file mode 100644
index 00000000..08464217
--- /dev/null
+++ b/CalendarView/app/src/main/res/layout/solar_week_bar.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/app/src/main/res/mipmap-hdpi/ic_launcher.png b/CalendarView/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..ed1e866f
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/CalendarView/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000..9a078e3e
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-mdpi/ic_launcher.png b/CalendarView/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..ed1e866f
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/CalendarView/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 00000000..efc028a6
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/CalendarView/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..ed1e866f
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/CalendarView/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..3af2608a
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/CalendarView/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..ed1e866f
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/CalendarView/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..9bec2e62
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_calendar.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_calendar.png
new file mode 100644
index 00000000..48e7ea1f
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_calendar.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_clear.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_clear.png
new file mode 100644
index 00000000..e8018eeb
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_clear.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_colorful_logo.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_colorful_logo.png
new file mode 100644
index 00000000..d3145154
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_colorful_logo.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_custom.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_custom.png
new file mode 100644
index 00000000..3aeaff14
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_custom.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_flyme_logo.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_flyme_logo.png
new file mode 100644
index 00000000..607ddb93
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_flyme_logo.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_full.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_full.png
new file mode 100644
index 00000000..d1429ca5
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_full.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_func.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_func.png
new file mode 100644
index 00000000..be9e1a21
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_func.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_increase.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_increase.png
new file mode 100644
index 00000000..b32033c2
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_increase.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_index_logo.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_index_logo.png
new file mode 100644
index 00000000..fb59323c
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_index_logo.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..ed1e866f
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..34947cd6
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_more.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_more.png
new file mode 100644
index 00000000..86b138eb
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_more.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_multi.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_multi.png
new file mode 100644
index 00000000..1bfb6d4b
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_multi.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_progress.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_progress.png
new file mode 100644
index 00000000..7ce931d0
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_progress.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_range.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_range.png
new file mode 100644
index 00000000..f018e34b
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_range.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_reduce.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_reduce.png
new file mode 100644
index 00000000..134c36b8
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_reduce.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_simple_logo.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_simple_logo.png
new file mode 100644
index 00000000..64b2efb1
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_simple_logo.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_single.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_single.png
new file mode 100644
index 00000000..bf21dc61
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_single.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_solar_system.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_solar_system.png
new file mode 100644
index 00000000..cf612c2a
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_solar_system.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_sun.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_sun.png
new file mode 100644
index 00000000..efb1e1a1
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_sun.png differ
diff --git a/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_tab_logo.png b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_tab_logo.png
new file mode 100644
index 00000000..75c819a0
Binary files /dev/null and b/CalendarView/app/src/main/res/mipmap-xxxhdpi/ic_tab_logo.png differ
diff --git a/CalendarView/app/src/main/res/values-zh/strings.xml b/CalendarView/app/src/main/res/values-zh/strings.xml
new file mode 100644
index 00000000..613922a2
--- /dev/null
+++ b/CalendarView/app/src/main/res/values-zh/strings.xml
@@ -0,0 +1,88 @@
+
+ CalendarViewProject
+
+
+ - 日
+ - 一
+ - 二
+ - 三
+ - 四
+ - 五
+ - 六
+
+
+
+ - 周日
+ - 周一
+ - 周二
+ - 周三
+ - 周四
+ - 周五
+ - 周六
+
+
+ 动态更新
+
+ 功能支持
+
+
+ - 周日为周起始
+ - 周一为周起始
+ - 周六为周起始
+ - 更换选择模式:单选/默认
+ - 热插拔更换周月视图
+ - 月视图模式:全部显示
+ - 月视图模式:仅当前月份
+ - 月视图模式:自动填充
+
+
+
+ - 展开日历布局
+ - 收缩日历布局
+ - 上一页
+ - 下一页
+ - 返回今天
+ - 限制日期范围
+ - 显示日期范围
+
+
+
+ Flyme手机日历
+ 国内手机日历风格,自动选择第一天
+
+ 精美定制中国风
+ 周末变色、圆点事件+文本标记
+
+ Mix错位风格
+ Mix错位风格
+
+ 全屏风格
+ 如果你需要的话,分分钟的事
+
+ 范围选择
+ 如果你需要范围选择的日历
+
+ 多选风格
+ 如果你需要多选风格的日历
+
+ 多彩风格
+ 取决于你怎么绘制
+
+ ViewPager风格
+ 如果内容是ViewPager的话
+
+ 单选风格
+ 单选风格是比较受欢迎
+
+ 进度条风格
+ 如果你正在开发todo APP的话
+
+ 下标订阅风格
+ 只要你想画,就能画
+
+ 星系风格
+ 很简单就能定制
+
+ 简单风格
+ 这是最简单的日历
+
diff --git a/CalendarView/app/src/main/res/values/attrs.xml b/CalendarView/app/src/main/res/values/attrs.xml
new file mode 100644
index 00000000..79e21016
--- /dev/null
+++ b/CalendarView/app/src/main/res/values/attrs.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/app/src/main/res/values/colors.xml b/CalendarView/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..f3dc5285
--- /dev/null
+++ b/CalendarView/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #ffffff
+ #ffffff
+ #ffffff
+
+ #e7e7e7
+ #f2f2f2
+ #009988
+
diff --git a/CalendarView/app/src/main/res/values/strings.xml b/CalendarView/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..1ea819fa
--- /dev/null
+++ b/CalendarView/app/src/main/res/values/strings.xml
@@ -0,0 +1,88 @@
+
+ CalendarViewProject
+
+
+ - Sun
+ - Mon
+ - Tue
+ - Wed
+ - Thu
+ - Fri
+ - Sat
+
+
+
+ - 周日
+ - 周一
+ - 周二
+ - 周三
+ - 周四
+ - 周五
+ - 周六
+
+
+ Dynamic Update
+
+ Function
+
+
+ - Change week start with sun
+ - Change week start with mon
+ - Change week start with sat
+ - Change select mode:single/default
+ - Change month and week view with CustomView
+ - MonthViewShowMode:all
+ - MonthViewShowMode:only_current
+ - MonthViewShowMode:fix
+
+
+
+ - Expand CalendarLayout
+ - Shrink CalendarLayout
+ - scrollToPre
+ - scrollToNext
+ - scrollToCurrent
+ - Limit calendar range
+ - Show range
+
+
+
+ Flyme
+ Meizu mobile phone calendar
+
+ Beautiful Chinese style
+ Beautiful Chinese style custom
+
+ Beautiful Mix style
+ Beautiful Mix style
+
+ Full screen style
+ If you need a full screen calendar
+
+ Range selection
+ Range selection style if you need
+
+ Multiple selection
+ Multiple selection style if you need
+
+ Colorful style
+ Everything what you need
+
+ ViewPager style
+ ViewPager content
+
+ Radio style
+ Radio style may be very popular
+
+ Progress bar style
+ If you are developing a todo application
+
+ Subscript style
+ Everything depends on how you draw
+
+ Galaxy style
+ So easy to customize
+
+ Simple style
+ Simple calendar
+
diff --git a/CalendarView/app/src/main/res/values/styles.xml b/CalendarView/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..55a4ab03
--- /dev/null
+++ b/CalendarView/app/src/main/res/values/styles.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/CalendarView/app/src/test/java/com/haibin/calendarviewproject/ExampleUnitTest.java b/CalendarView/app/src/test/java/com/haibin/calendarviewproject/ExampleUnitTest.java
new file mode 100644
index 00000000..b50d588c
--- /dev/null
+++ b/CalendarView/app/src/test/java/com/haibin/calendarviewproject/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.haibin.calendarviewproject;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/CalendarView/build.gradle b/CalendarView/build.gradle
new file mode 100644
index 00000000..a5ebab6b
--- /dev/null
+++ b/CalendarView/build.gradle
@@ -0,0 +1,25 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ google()
+ }
+ dependencies {
+ //noinspection GradleDependency
+ classpath 'com.android.tools.build:gradle:3.4.2'
+ classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7'
+ classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ google()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/CalendarView/calendarview/.gitignore b/CalendarView/calendarview/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/CalendarView/calendarview/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/CalendarView/calendarview/build.gradle b/CalendarView/calendarview/build.gradle
new file mode 100644
index 00000000..76cb79a6
--- /dev/null
+++ b/CalendarView/calendarview/build.gradle
@@ -0,0 +1,46 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 28
+
+ defaultConfig {
+ minSdkVersion 14
+ //noinspection OldTargetApi
+ targetSdkVersion 28
+ versionCode 371
+ versionName "3.7.1"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ android {
+ lintOptions {
+ abortOnError false
+ }
+ }
+ task intoJar(type: Copy) {
+ delete 'build/libs/CalendarView.jar'
+ from('build/intermediates/bundles/release/')
+ into('build/libs/')
+ include('classes.jar')
+ rename ('classes.jar', 'CalendarView.jar')
+ }
+ intoJar.dependsOn(build)
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'androidx.recyclerview:recyclerview:1.1.0'
+ testImplementation 'junit:junit:4.12'
+}
+apply from: '../script/gradle-jcenter-push.gradle'
\ No newline at end of file
diff --git a/CalendarView/calendarview/proguard-rules.pro b/CalendarView/calendarview/proguard-rules.pro
new file mode 100644
index 00000000..f3e3ccae
--- /dev/null
+++ b/CalendarView/calendarview/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in D:\android\sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/CalendarUtilTest.java b/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/CalendarUtilTest.java
new file mode 100644
index 00000000..73f38edd
--- /dev/null
+++ b/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/CalendarUtilTest.java
@@ -0,0 +1,286 @@
+package com.haibin.calendarview;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * 新版月视图算法测试
+ * Created by huanghaibin on 2018/2/8.
+ */
+@SuppressWarnings("all")
+public class CalendarUtilTest {
+ @Test
+ public void getMonthViewHeight() throws Exception {
+ //周一起始
+ assertEquals(5, CalendarUtil.getMonthViewHeight(2018, 4, 1, 1));
+ assertEquals(5, CalendarUtil.getMonthViewHeight(2018, 5, 1, 1));
+ assertEquals(5, CalendarUtil.getMonthViewHeight(2018, 6, 1, 1));
+ assertEquals(6, CalendarUtil.getMonthViewHeight(2018, 9, 1, 1));
+
+ //周一起始
+ assertEquals(6, CalendarUtil.getMonthViewHeight(2018, 4, 1, 2));
+ assertEquals(5, CalendarUtil.getMonthViewHeight(2018, 5, 1, 2));
+ assertEquals(5, CalendarUtil.getMonthViewHeight(2018, 6, 1, 2));
+ assertEquals(6, CalendarUtil.getMonthViewHeight(2018, 7, 1, 2));
+ }
+
+ /**
+ * 根据星期数和最小日期推算出该星期的第一天
+ */
+ @Test
+ public void getFirstCalendarStartWithMinCalendar() throws Exception {
+ Calendar calendar = new Calendar();
+ calendar.setYear(2018);
+ calendar.setMonth(8);
+ calendar.setDay(13);
+
+ Calendar firstCalendar = CalendarUtil.getFirstCalendarStartWithMinCalendar(2018, 8, 13,
+ 1,
+ 2);
+ assertEquals(calendar, firstCalendar);
+
+ calendar = new Calendar();
+ calendar.setYear(2018);
+ calendar.setMonth(8);
+ calendar.setDay(20);
+
+ firstCalendar = CalendarUtil.getFirstCalendarStartWithMinCalendar(2018, 8, 13,
+ 2,
+ 2);
+ assertEquals(calendar, firstCalendar);
+
+ calendar = new Calendar();
+ calendar.setYear(2018);
+ calendar.setMonth(8);
+ calendar.setDay(12);
+
+ firstCalendar = CalendarUtil.getFirstCalendarStartWithMinCalendar(2018, 8, 13,
+ 1,
+ 1);
+ assertEquals(calendar, firstCalendar);
+ }
+
+ /**
+ * 获取两个日期之间一共有多少周,
+ * 注意周起始周一、周日、周六
+ *
+ * @return 周数用于WeekViewPager itemCount
+ */
+ @Test
+ public void getWeekCountBetweenBothCalendar() throws Exception {
+ int count = CalendarUtil.getWeekCountBetweenBothCalendar(2018, 8, 5,
+ 2018, 8, 12, 1);
+ assertEquals(2, count);
+
+ count = CalendarUtil.getWeekCountBetweenBothCalendar(2018, 8, 1,
+ 2018, 8, 12, 2);
+
+ assertEquals(2, count);
+
+ count = CalendarUtil.getWeekCountBetweenBothCalendar(2018, 7, 29,
+ 2018, 8, 12, 2);
+
+ assertEquals(3, count);
+
+ count = CalendarUtil.getWeekCountBetweenBothCalendar(2017, 12, 1,
+ 2017, 12, 3, 2);
+
+ assertEquals(1, count);
+
+ count = CalendarUtil.getWeekCountBetweenBothCalendar(2018, 8, 13,
+ 2018, 12, 12, 2);
+
+ assertEquals(18, count);
+ }
+
+ /**
+ * 是否在日期范围內
+ */
+ @Test
+ public void isCalendarInRange() throws Exception {
+ Calendar calendar = new Calendar();
+ calendar.setYear(2017);
+ calendar.setMonth(12);
+ calendar.setDay(30);
+
+ boolean isInRange = CalendarUtil.isCalendarInRange(calendar,
+ 2017, 3, 12,
+ 2018, 12, 31);
+
+ assertEquals(true, isInRange);
+
+ calendar = new Calendar();
+ calendar.setYear(2017);
+ calendar.setMonth(3);
+ calendar.setDay(11);
+
+ isInRange = CalendarUtil.isCalendarInRange(calendar,
+ 2017, 3, 12,
+ 2018, 12, 31);
+
+ assertEquals(false, isInRange);
+
+ calendar = new Calendar();
+ calendar.setYear(2018);
+ calendar.setMonth(12);
+ calendar.setDay(31);
+
+ isInRange = CalendarUtil.isCalendarInRange(calendar,
+ 2017, 3, 12,
+ 2018, 12, 31);
+
+ assertEquals(true, isInRange);
+
+ calendar = new Calendar();
+ calendar.setYear(2018);
+ calendar.setMonth(12);
+ calendar.setDay(31);
+
+ isInRange = CalendarUtil.isCalendarInRange(calendar,
+ 2019, 1, 31,
+ 2020, 11, 30);
+
+ assertEquals(false, isInRange);
+
+ }
+
+ /**
+ * 根据日期获取距离最小年份是第几周
+ */
+ @Test
+ public void getWeekFromCalendarStartWithMinCalendar() throws Exception {
+ Calendar calendar = new Calendar();
+ calendar.setYear(2018);
+ calendar.setMonth(8);
+ calendar.setDay(1);
+
+ int week = CalendarUtil.getWeekFromCalendarStartWithMinCalendar(calendar,
+ 2018, 8, 1, 1);
+
+ assertEquals(1, week);
+
+ calendar = new Calendar();
+ calendar.setYear(2018);
+ calendar.setMonth(9);
+ calendar.setDay(1);
+
+ week = CalendarUtil.getWeekFromCalendarStartWithMinCalendar(calendar,
+ 2018, 8, 13, 2);
+
+ assertEquals(3, week);
+
+ }
+
+ @Test
+ public void getWeekCountDiff() throws Exception {
+ int diff = CalendarUtil.getWeekViewEndDiff(2020,4,2,1);
+ assertEquals(2,diff);
+ }
+
+
+ @Test
+ public void getWeekViewStartDiff() throws Exception {
+
+ }
+
+
+ @Test
+ public void getWeekViewEndDiff() throws Exception {
+
+ }
+
+ @Test
+ public void getWeekFromDayInMonth() throws Exception {
+
+ Calendar calendar = new Calendar();
+ calendar.setYear(2018);
+ calendar.setMonth(4);
+ calendar.setDay(24);
+
+ CalendarUtil.initCalendarForMonthView(2018, 4, calendar, 1);
+
+ }
+
+ @Test
+ public void getMonthEndDiff() throws Exception {
+ assertEquals(5, CalendarUtil.getMonthEndDiff(2018, 4, 1));
+ assertEquals(2, CalendarUtil.getMonthEndDiff(2018, 5, 1));
+ assertEquals(0, CalendarUtil.getMonthEndDiff(2018, 6, 1));
+ assertEquals(4, CalendarUtil.getMonthEndDiff(2018, 7, 1));
+
+ assertEquals(4, CalendarUtil.getMonthEndDiff(2018, 4, 7));
+ assertEquals(1, CalendarUtil.getMonthEndDiff(2018, 5, 7));
+ assertEquals(6, CalendarUtil.getMonthEndDiff(2018, 6, 7));
+ assertEquals(3, CalendarUtil.getMonthEndDiff(2018, 7, 7));
+
+ assertEquals(6, CalendarUtil.getMonthEndDiff(2018, 4, 2));
+ assertEquals(3, CalendarUtil.getMonthEndDiff(2018, 5, 2));
+ assertEquals(1, CalendarUtil.getMonthEndDiff(2018, 6, 2));
+ assertEquals(5, CalendarUtil.getMonthEndDiff(2018, 7, 2));
+ }
+
+ @Test
+ public void getMonthViewStartDiff() throws Exception {
+
+ Calendar calendar = new Calendar();
+ calendar.setYear(2018);
+ calendar.setMonth(4);
+ calendar.setDay(1);
+
+ assertEquals(1, CalendarUtil.getMonthViewStartDiff(calendar, 7));
+
+ assertEquals(0, CalendarUtil.getMonthViewStartDiff(calendar, 1));
+
+ assertEquals(6, CalendarUtil.getMonthViewStartDiff(calendar, 2));
+
+ calendar.setMonth(5);
+
+
+ assertEquals(3, CalendarUtil.getMonthViewStartDiff(calendar, 7));
+
+ assertEquals(2, CalendarUtil.getMonthViewStartDiff(calendar, 1));
+
+ assertEquals(1, CalendarUtil.getMonthViewStartDiff(calendar, 2));
+ }
+
+ /**
+ * 根据日期获取两个年份中第几周,用来设置 WeekView currentItem
+ *
+ * @throws Exception Exception
+ */
+ @Test
+ public void differ() throws Exception {
+ Calendar calendar1 = new Calendar();
+ calendar1.setYear(2018);
+ calendar1.setMonth(4);
+ calendar1.setDay(1);
+
+ Calendar calendar2 = new Calendar();
+ calendar2.setYear(2018);
+ calendar2.setMonth(4);
+ calendar2.setDay(3);
+
+ assertEquals(-2,CalendarUtil.differ(calendar1,calendar2));
+
+ calendar1.setYear(2018);
+ calendar1.setMonth(9);
+ calendar1.setDay(30);
+
+ calendar2.setYear(2018);
+ calendar2.setMonth(9);
+ calendar2.setDay(1);
+
+ assertEquals(29,CalendarUtil.differ(calendar1,calendar2));
+
+ calendar1.setYear(2018);
+ calendar1.setMonth(9);
+ calendar1.setDay(12);
+
+ calendar2.setYear(2018);
+ calendar2.setMonth(9);
+ calendar2.setDay(5);
+
+ assertEquals(7,CalendarUtil.differ(calendar1,calendar2));
+ }
+}
\ No newline at end of file
diff --git a/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/ExampleInstrumentedTest.java b/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/ExampleInstrumentedTest.java
new file mode 100644
index 00000000..3dcf8cbe
--- /dev/null
+++ b/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.haibin.calendarview;
+
+import android.content.Context;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.haibin.calendarview.test", appContext.getPackageName());
+ }
+}
diff --git a/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/LunarCalendarTest.java b/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/LunarCalendarTest.java
new file mode 100644
index 00000000..ef3cc8ee
--- /dev/null
+++ b/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/LunarCalendarTest.java
@@ -0,0 +1,59 @@
+package com.haibin.calendarview;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * 测试
+ * Created by haibin on 2017/4/10.
+ */
+public class LunarCalendarTest {
+ @Test
+ public void getLunarText() throws Exception {
+ Calendar calendar = new Calendar();
+ calendar.setYear(2017);
+ calendar.setMonth(6);
+ calendar.setDay(24);
+
+ Calendar calendar1 = new Calendar();
+ calendar1.setYear(2017);
+ calendar1.setMonth(6);
+ calendar1.setDay(25);
+ assertEquals("六月",LunarCalendar.getLunarText(calendar));
+ assertEquals("六月",LunarCalendar.getLunarText(calendar1));
+ }
+
+ /**
+ * 测试当年的清明节偏移日期为0、1、2, 4或5号6号
+ *
+ * @throws Exception Exception
+ */
+ @Test
+ public void getTermsOffset() throws Exception {
+
+ }
+
+
+ /**
+ * 测试每个月农历有多少天,也可适用于除夕算法
+ *
+ * @throws Exception Exception
+ */
+ @Test
+ public void daysInLunarMonth() throws Exception {
+ int d = LunarCalendar.daysInLunarMonth(2019,12);
+ assertEquals(30,d);
+ }
+
+ /**
+ * 测试每年冬至是具体几日
+ *
+ * @throws Exception 异常
+ */
+ @Test
+ public void getWinterSolstice() throws Exception {
+ assertEquals(22, 22);
+ }
+
+}
\ No newline at end of file
diff --git a/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/LunarUtilTest.java b/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/LunarUtilTest.java
new file mode 100644
index 00000000..00c1be90
--- /dev/null
+++ b/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/LunarUtilTest.java
@@ -0,0 +1,18 @@
+package com.haibin.calendarview;
+
+import org.junit.Test;
+
+/**
+ * 农历测试
+ * Created by huanghaibin on 2017/12/12.
+ */
+public class LunarUtilTest {
+ @Test
+ public void solarToLunar() throws Exception {
+ LunarUtil.solarToLunar(2017,6,24);
+ LunarUtil.solarToLunar(2017,6,25);
+ LunarUtil.solarToLunar(2017,7,23);
+ LunarUtil.solarToLunar(2017,7,24);
+ }
+
+}
\ No newline at end of file
diff --git a/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/SolarTermUtilTest.java b/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/SolarTermUtilTest.java
new file mode 100644
index 00000000..03a73160
--- /dev/null
+++ b/CalendarView/calendarview/src/androidTest/java/com/haibin/calendarview/SolarTermUtilTest.java
@@ -0,0 +1,17 @@
+package com.haibin.calendarview;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * 24节气单元测试
+ * Created by huanghaibin on 2018/2/9.
+ */
+public class SolarTermUtilTest {
+ @Test
+ public void getSolarTerms() throws Exception {
+ SolarTermUtil.getSolarTerms(2017);
+ }
+
+}
\ No newline at end of file
diff --git a/CalendarView/calendarview/src/main/AndroidManifest.xml b/CalendarView/calendarview/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..195dfdfd
--- /dev/null
+++ b/CalendarView/calendarview/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/BaseMonthView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/BaseMonthView.java
new file mode 100644
index 00000000..711d2f57
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/BaseMonthView.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+
+/**
+ * 月视图基础控件,可自由继承实现
+ * 可通过此扩展各种视图如:MonthView、RangeMonthView、MultiMonthView
+ */
+public abstract class BaseMonthView extends BaseView {
+
+ MonthViewPager mMonthViewPager;
+
+ /**
+ * 当前日历卡年份
+ */
+ protected int mYear;
+
+ /**
+ * 当前日历卡月份
+ */
+ protected int mMonth;
+
+
+ /**
+ * 日历的行数
+ */
+ protected int mLineCount;
+
+ /**
+ * 日历高度
+ */
+ protected int mHeight;
+
+
+ /**
+ * 下个月偏移的数量
+ */
+ protected int mNextDiff;
+
+
+ public BaseMonthView(Context context) {
+ super(context);
+ }
+
+ /**
+ * 初始化日期
+ *
+ * @param year year
+ * @param month month
+ */
+ final void initMonthWithDate(int year, int month) {
+ mYear = year;
+ mMonth = month;
+ initCalendar();
+ mHeight = CalendarUtil.getMonthViewHeight(year, month, mItemHeight, mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+
+ }
+
+ /**
+ * 初始化日历
+ */
+ @SuppressLint("WrongConstant")
+ private void initCalendar() {
+
+ mNextDiff = CalendarUtil.getMonthEndDiff(mYear, mMonth, mDelegate.getWeekStart());
+ int preDiff = CalendarUtil.getMonthViewStartDiff(mYear, mMonth, mDelegate.getWeekStart());
+ int monthDayCount = CalendarUtil.getMonthDaysCount(mYear, mMonth);
+
+ mItems = CalendarUtil.initCalendarForMonthView(mYear, mMonth, mDelegate.getCurrentDay(), mDelegate.getWeekStart());
+
+ if (mItems.contains(mDelegate.getCurrentDay())) {
+ mCurrentItem = mItems.indexOf(mDelegate.getCurrentDay());
+ } else {
+ mCurrentItem = mItems.indexOf(mDelegate.mSelectedCalendar);
+ }
+
+ if (mCurrentItem > 0 &&
+ mDelegate.mCalendarInterceptListener != null &&
+ mDelegate.mCalendarInterceptListener.onCalendarIntercept(mDelegate.mSelectedCalendar)) {
+ mCurrentItem = -1;
+ }
+
+ if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ALL_MONTH) {
+ mLineCount = 6;
+ } else {
+ mLineCount = (preDiff + monthDayCount + mNextDiff) / 7;
+ }
+ addSchemesFromMap();
+ invalidate();
+ }
+
+ /**
+ * 获取点击选中的日期
+ *
+ * @return return
+ */
+ protected Calendar getIndex() {
+ if (mItemWidth == 0 || mItemHeight == 0) {
+ return null;
+ }
+ if (mX <= mDelegate.getCalendarPaddingLeft() || mX >= getWidth() - mDelegate.getCalendarPaddingRight()) {
+ onClickCalendarPadding();
+ return null;
+ }
+ int indexX = (int) (mX - mDelegate.getCalendarPaddingLeft()) / mItemWidth;
+ if (indexX >= 7) {
+ indexX = 6;
+ }
+ int indexY = (int) mY / mItemHeight;
+ int position = indexY * 7 + indexX;// 选择项
+ if (position >= 0 && position < mItems.size()) {
+ return mItems.get(position);
+ }
+ return null;
+ }
+
+ private void onClickCalendarPadding() {
+ if (mDelegate.mClickCalendarPaddingListener == null) {
+ return;
+ }
+ Calendar calendar = null;
+ int indexX = (int) (mX - mDelegate.getCalendarPaddingLeft()) / mItemWidth;
+ if (indexX >= 7) {
+ indexX = 6;
+ }
+ int indexY = (int) mY / mItemHeight;
+ int position = indexY * 7 + indexX;// 选择项
+ if (position >= 0 && position < mItems.size()) {
+ calendar = mItems.get(position);
+ }
+ if (calendar == null) {
+ return;
+ }
+ mDelegate.mClickCalendarPaddingListener.onClickCalendarPadding(mX, mY, true, calendar,
+ getClickCalendarPaddingObject(mX, mY, calendar));
+ }
+
+ /**
+ * 获取点击事件处的对象
+ *
+ * @param x x
+ * @param y y
+ * @param adjacentCalendar adjacent calendar
+ * @return obj can as null
+ */
+ @SuppressWarnings("unused")
+ protected Object getClickCalendarPaddingObject(float x, float y, Calendar adjacentCalendar) {
+ return null;
+ }
+
+ /**
+ * 记录已经选择的日期
+ *
+ * @param calendar calendar
+ */
+ final void setSelectedCalendar(Calendar calendar) {
+ mCurrentItem = mItems.indexOf(calendar);
+ }
+
+
+ /**
+ * 更新显示模式
+ */
+ final void updateShowMode() {
+ mLineCount = CalendarUtil.getMonthViewLineCount(mYear, mMonth,
+ mDelegate.getWeekStart(), mDelegate.getMonthViewShowMode());
+ mHeight = CalendarUtil.getMonthViewHeight(mYear, mMonth, mItemHeight, mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ invalidate();
+ }
+
+ /**
+ * 更新周起始
+ */
+ final void updateWeekStart() {
+ initCalendar();
+ mHeight = CalendarUtil.getMonthViewHeight(mYear, mMonth, mItemHeight, mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ }
+
+ @Override
+ void updateItemHeight() {
+ super.updateItemHeight();
+ mHeight = CalendarUtil.getMonthViewHeight(mYear, mMonth, mItemHeight, mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ }
+
+
+ @Override
+ void updateCurrentDate() {
+ if (mItems == null)
+ return;
+ if (mItems.contains(mDelegate.getCurrentDay())) {
+ for (Calendar a : mItems) {//添加操作
+ a.setCurrentDay(false);
+ }
+ int index = mItems.indexOf(mDelegate.getCurrentDay());
+ mItems.get(index).setCurrentDay(true);
+ }
+ invalidate();
+ }
+
+
+ /**
+ * 获取选中的下标
+ *
+ * @param calendar calendar
+ * @return 获取选中的下标
+ */
+ protected final int getSelectedIndex(Calendar calendar) {
+ return mItems.indexOf(calendar);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mLineCount != 0) {
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY);
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ /**
+ * 开始绘制前的钩子,这里做一些初始化的操作,每次绘制只调用一次,性能高效
+ * 没有需要可忽略不实现
+ * 例如:
+ * 1、需要绘制圆形标记事件背景,可以在这里计算半径
+ * 2、绘制矩形选中效果,也可以在这里计算矩形宽和高
+ */
+ protected void onPreviewHook() {
+ // TODO: 2017/11/16
+ }
+
+
+ /**
+ * 循环绘制开始的回调,不需要可忽略
+ * 绘制每个日历项的循环,用来计算baseLine、圆心坐标等都可以在这里实现
+ *
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ */
+ protected void onLoopStart(int x, int y) {
+ // TODO: 2017/11/16
+ }
+
+ @Override
+ protected void onDestroy() {
+
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/BaseRecyclerAdapter.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/BaseRecyclerAdapter.java
new file mode 100644
index 00000000..eb3902b4
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/BaseRecyclerAdapter.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 基本的适配器
+ */
+abstract class BaseRecyclerAdapter extends RecyclerView.Adapter {
+
+ @SuppressWarnings("all")
+ LayoutInflater mInflater;
+ private List mItems;
+ private OnItemClickListener onItemClickListener;
+ private OnClickListener onClickListener;
+ Context mContext;
+
+ BaseRecyclerAdapter(Context context) {
+ mContext = context;
+ this.mItems = new ArrayList<>();
+ mInflater = LayoutInflater.from(context);
+ onClickListener = new OnClickListener() {
+ @Override
+ public void onClick(int position, long itemId) {
+ if (onItemClickListener != null)
+ onItemClickListener.onItemClick(position, itemId);
+ }
+ };
+
+ }
+
+ @SuppressWarnings("ConstantConditions")
+ @NonNull
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ final RecyclerView.ViewHolder holder = onCreateDefaultViewHolder(parent, viewType);
+ if (holder != null) {
+ holder.itemView.setTag(holder);
+ holder.itemView.setOnClickListener(onClickListener);
+ }
+ return holder;
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
+ onBindViewHolder(holder, mItems.get(position), position);
+ }
+
+ abstract RecyclerView.ViewHolder onCreateDefaultViewHolder(ViewGroup parent, int type);
+
+ abstract void onBindViewHolder(RecyclerView.ViewHolder holder, T item, int position);
+
+ @Override
+ public int getItemCount() {
+ return mItems.size();
+ }
+
+ void setOnItemClickListener(OnItemClickListener onItemClickListener) {
+ this.onItemClickListener = onItemClickListener;
+ }
+
+ @SuppressWarnings("unused")
+ void addAll(List items) {
+ if (items != null && items.size() > 0) {
+ mItems.addAll(items);
+ notifyItemRangeInserted(mItems.size(), items.size());
+ }
+ }
+
+ final void addItem(T item) {
+ if (item != null) {
+ this.mItems.add(item);
+ notifyItemChanged(mItems.size());
+ }
+ }
+
+ @SuppressWarnings("unused")
+ final List getItems() {
+ return mItems;
+ }
+
+
+ final T getItem(int position) {
+ if (position < 0 || position >= mItems.size())
+ return null;
+ return mItems.get(position);
+ }
+
+ static abstract class OnClickListener implements View.OnClickListener {
+ @Override
+ public void onClick(View v) {
+ RecyclerView.ViewHolder holder = (RecyclerView.ViewHolder) v.getTag();
+ onClick(holder.getAdapterPosition(), holder.getItemId());
+ }
+
+ public abstract void onClick(int position, long itemId);
+ }
+
+
+ interface OnItemClickListener {
+ void onItemClick(int position, long itemId);
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/BaseView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/BaseView.java
new file mode 100644
index 00000000..1b9270a3
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/BaseView.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * 基本的日历View,派生出MonthView 和 WeekView
+ * Created by huanghaibin on 2018/1/23.
+ */
+
+public abstract class BaseView extends View implements View.OnClickListener, View.OnLongClickListener {
+
+ CalendarViewDelegate mDelegate;
+
+ /**
+ * 当前月份日期的笔
+ */
+ protected Paint mCurMonthTextPaint = new Paint();
+
+ /**
+ * 其它月份日期颜色
+ */
+ protected Paint mOtherMonthTextPaint = new Paint();
+
+ /**
+ * 当前月份农历文本颜色
+ */
+ protected Paint mCurMonthLunarTextPaint = new Paint();
+
+ /**
+ * 当前月份农历文本颜色
+ */
+ protected Paint mSelectedLunarTextPaint = new Paint();
+
+ /**
+ * 其它月份农历文本颜色
+ */
+ protected Paint mOtherMonthLunarTextPaint = new Paint();
+
+ /**
+ * 其它月份农历文本颜色
+ */
+ protected Paint mSchemeLunarTextPaint = new Paint();
+
+ /**
+ * 标记的日期背景颜色画笔
+ */
+ protected Paint mSchemePaint = new Paint();
+
+ /**
+ * 被选择的日期背景色
+ */
+ protected Paint mSelectedPaint = new Paint();
+
+ /**
+ * 标记的文本画笔
+ */
+ protected Paint mSchemeTextPaint = new Paint();
+
+ /**
+ * 选中的文本画笔
+ */
+ protected Paint mSelectTextPaint = new Paint();
+
+ /**
+ * 当前日期文本颜色画笔
+ */
+ protected Paint mCurDayTextPaint = new Paint();
+
+ /**
+ * 当前日期文本颜色画笔
+ */
+ protected Paint mCurDayLunarTextPaint = new Paint();
+
+ /**
+ * 日历布局,需要在日历下方放自己的布局
+ */
+ CalendarLayout mParentLayout;
+
+ /**
+ * 日历项
+ */
+ protected List mItems;
+
+ /**
+ * 每一项的高度
+ */
+ protected int mItemHeight;
+
+ /**
+ * 每一项的宽度
+ */
+ protected int mItemWidth;
+
+ /**
+ * Text的基线
+ */
+ protected float mTextBaseLine;
+
+ /**
+ * 点击的x、y坐标
+ */
+ protected float mX, mY;
+
+ /**
+ * 是否点击
+ */
+ boolean isClick = true;
+
+ /**
+ * 字体大小
+ */
+ static final int TEXT_SIZE = 14;
+
+ /**
+ * 当前点击项
+ */
+ int mCurrentItem = -1;
+
+ /**
+ * 周起始
+ */
+ int mWeekStartWidth;
+
+ public BaseView(Context context) {
+ this(context, null);
+ }
+
+ public BaseView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ initPaint(context);
+ }
+
+ /**
+ * 初始化配置
+ *
+ * @param context context
+ */
+ private void initPaint(Context context) {
+ mCurMonthTextPaint.setAntiAlias(true);
+ mCurMonthTextPaint.setTextAlign(Paint.Align.CENTER);
+ mCurMonthTextPaint.setColor(0xFF111111);
+ mCurMonthTextPaint.setFakeBoldText(true);
+ mCurMonthTextPaint.setTextSize(CalendarUtil.dipToPx(context, TEXT_SIZE));
+
+ mOtherMonthTextPaint.setAntiAlias(true);
+ mOtherMonthTextPaint.setTextAlign(Paint.Align.CENTER);
+ mOtherMonthTextPaint.setColor(0xFFe1e1e1);
+ mOtherMonthTextPaint.setFakeBoldText(true);
+ mOtherMonthTextPaint.setTextSize(CalendarUtil.dipToPx(context, TEXT_SIZE));
+
+ mCurMonthLunarTextPaint.setAntiAlias(true);
+ mCurMonthLunarTextPaint.setTextAlign(Paint.Align.CENTER);
+
+ mSelectedLunarTextPaint.setAntiAlias(true);
+ mSelectedLunarTextPaint.setTextAlign(Paint.Align.CENTER);
+
+ mOtherMonthLunarTextPaint.setAntiAlias(true);
+ mOtherMonthLunarTextPaint.setTextAlign(Paint.Align.CENTER);
+
+
+ mSchemeLunarTextPaint.setAntiAlias(true);
+ mSchemeLunarTextPaint.setTextAlign(Paint.Align.CENTER);
+
+ mSchemeTextPaint.setAntiAlias(true);
+ mSchemeTextPaint.setStyle(Paint.Style.FILL);
+ mSchemeTextPaint.setTextAlign(Paint.Align.CENTER);
+ mSchemeTextPaint.setColor(0xffed5353);
+ mSchemeTextPaint.setFakeBoldText(true);
+ mSchemeTextPaint.setTextSize(CalendarUtil.dipToPx(context, TEXT_SIZE));
+
+ mSelectTextPaint.setAntiAlias(true);
+ mSelectTextPaint.setStyle(Paint.Style.FILL);
+ mSelectTextPaint.setTextAlign(Paint.Align.CENTER);
+ mSelectTextPaint.setColor(0xffed5353);
+ mSelectTextPaint.setFakeBoldText(true);
+ mSelectTextPaint.setTextSize(CalendarUtil.dipToPx(context, TEXT_SIZE));
+
+ mSchemePaint.setAntiAlias(true);
+ mSchemePaint.setStyle(Paint.Style.FILL);
+ mSchemePaint.setStrokeWidth(2);
+ mSchemePaint.setColor(0xffefefef);
+
+ mCurDayTextPaint.setAntiAlias(true);
+ mCurDayTextPaint.setTextAlign(Paint.Align.CENTER);
+ mCurDayTextPaint.setColor(Color.RED);
+ mCurDayTextPaint.setFakeBoldText(true);
+ mCurDayTextPaint.setTextSize(CalendarUtil.dipToPx(context, TEXT_SIZE));
+
+ mCurDayLunarTextPaint.setAntiAlias(true);
+ mCurDayLunarTextPaint.setTextAlign(Paint.Align.CENTER);
+ mCurDayLunarTextPaint.setColor(Color.RED);
+ mCurDayLunarTextPaint.setFakeBoldText(true);
+ mCurDayLunarTextPaint.setTextSize(CalendarUtil.dipToPx(context, TEXT_SIZE));
+
+ mSelectedPaint.setAntiAlias(true);
+ mSelectedPaint.setStyle(Paint.Style.FILL);
+ mSelectedPaint.setStrokeWidth(2);
+
+ setOnClickListener(this);
+ setOnLongClickListener(this);
+ }
+
+ /**
+ * 初始化所有UI配置
+ *
+ * @param delegate delegate
+ */
+ final void setup(CalendarViewDelegate delegate) {
+ this.mDelegate = delegate;
+ mWeekStartWidth = mDelegate.getWeekStart();
+ updateStyle();
+ updateItemHeight();
+
+ initPaint();
+ }
+
+
+ final void updateStyle() {
+ if (mDelegate == null) {
+ return;
+ }
+ this.mCurDayTextPaint.setColor(mDelegate.getCurDayTextColor());
+ this.mCurDayLunarTextPaint.setColor(mDelegate.getCurDayLunarTextColor());
+ this.mCurMonthTextPaint.setColor(mDelegate.getCurrentMonthTextColor());
+ this.mOtherMonthTextPaint.setColor(mDelegate.getOtherMonthTextColor());
+ this.mCurMonthLunarTextPaint.setColor(mDelegate.getCurrentMonthLunarTextColor());
+ this.mSelectedLunarTextPaint.setColor(mDelegate.getSelectedLunarTextColor());
+ this.mSelectTextPaint.setColor(mDelegate.getSelectedTextColor());
+ this.mOtherMonthLunarTextPaint.setColor(mDelegate.getOtherMonthLunarTextColor());
+ this.mSchemeLunarTextPaint.setColor(mDelegate.getSchemeLunarTextColor());
+ this.mSchemePaint.setColor(mDelegate.getSchemeThemeColor());
+ this.mSchemeTextPaint.setColor(mDelegate.getSchemeTextColor());
+ this.mCurMonthTextPaint.setTextSize(mDelegate.getDayTextSize());
+ this.mOtherMonthTextPaint.setTextSize(mDelegate.getDayTextSize());
+ this.mCurDayTextPaint.setTextSize(mDelegate.getDayTextSize());
+ this.mSchemeTextPaint.setTextSize(mDelegate.getDayTextSize());
+ this.mSelectTextPaint.setTextSize(mDelegate.getDayTextSize());
+
+ this.mCurMonthLunarTextPaint.setTextSize(mDelegate.getLunarTextSize());
+ this.mSelectedLunarTextPaint.setTextSize(mDelegate.getLunarTextSize());
+ this.mCurDayLunarTextPaint.setTextSize(mDelegate.getLunarTextSize());
+ this.mOtherMonthLunarTextPaint.setTextSize(mDelegate.getLunarTextSize());
+ this.mSchemeLunarTextPaint.setTextSize(mDelegate.getLunarTextSize());
+
+ this.mSelectedPaint.setStyle(Paint.Style.FILL);
+ this.mSelectedPaint.setColor(mDelegate.getSelectedThemeColor());
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ void updateItemHeight() {
+ this.mItemHeight = mDelegate.getCalendarItemHeight();
+ Paint.FontMetrics metrics = mCurMonthTextPaint.getFontMetrics();
+ mTextBaseLine = mItemHeight / 2 - metrics.descent + (metrics.bottom - metrics.top) / 2;
+ }
+
+
+ /**
+ * 移除事件
+ */
+ final void removeSchemes() {
+ for (Calendar a : mItems) {
+ a.setScheme("");
+ a.setSchemeColor(0);
+ a.setSchemes(null);
+ }
+ }
+
+ /**
+ * 添加事件标记,来自Map
+ */
+ final void addSchemesFromMap() {
+ if (mDelegate.mSchemeDatesMap == null || mDelegate.mSchemeDatesMap.size() == 0) {
+ return;
+ }
+ for (Calendar a : mItems) {
+ if (mDelegate.mSchemeDatesMap.containsKey(a.toString())) {
+ Calendar d = mDelegate.mSchemeDatesMap.get(a.toString());
+ if (d == null) {
+ continue;
+ }
+ a.setScheme(TextUtils.isEmpty(d.getScheme()) ? mDelegate.getSchemeText() : d.getScheme());
+ a.setSchemeColor(d.getSchemeColor());
+ a.setSchemes(d.getSchemes());
+ } else {
+ a.setScheme("");
+ a.setSchemeColor(0);
+ a.setSchemes(null);
+ }
+ }
+ }
+
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getPointerCount() > 1)
+ return false;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mX = event.getX();
+ mY = event.getY();
+ isClick = true;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ float mDY;
+ if (isClick) {
+ mDY = event.getY() - mY;
+ isClick = Math.abs(mDY) <= 50;
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ mX = event.getX();
+ mY = event.getY();
+ break;
+ }
+ return super.onTouchEvent(event);
+ }
+
+
+ /**
+ * 开始绘制前的钩子,这里做一些初始化的操作,每次绘制只调用一次,性能高效
+ * 没有需要可忽略不实现
+ * 例如:
+ * 1、需要绘制圆形标记事件背景,可以在这里计算半径
+ * 2、绘制矩形选中效果,也可以在这里计算矩形宽和高
+ */
+ protected void onPreviewHook() {
+ // TODO: 2017/11/16
+ }
+
+ /**
+ * 是否是选中的
+ *
+ * @param calendar calendar
+ * @return true or false
+ */
+ protected boolean isSelected(Calendar calendar) {
+ return mItems != null && mItems.indexOf(calendar) == mCurrentItem;
+ }
+
+ /**
+ * 更新事件
+ */
+ final void update() {
+ if (mDelegate.mSchemeDatesMap == null || mDelegate.mSchemeDatesMap.size() == 0) {//清空操作
+ removeSchemes();
+ invalidate();
+ return;
+ }
+ addSchemesFromMap();
+ invalidate();
+ }
+
+
+ /**
+ * 是否拦截日期,此设置续设置mCalendarInterceptListener
+ *
+ * @param calendar calendar
+ * @return 是否拦截日期
+ */
+ protected final boolean onCalendarIntercept(Calendar calendar) {
+ return mDelegate.mCalendarInterceptListener != null &&
+ mDelegate.mCalendarInterceptListener.onCalendarIntercept(calendar);
+ }
+
+ /**
+ * 是否在日期范围内
+ *
+ * @param calendar calendar
+ * @return 是否在日期范围内
+ */
+ protected final boolean isInRange(Calendar calendar) {
+ return mDelegate != null && CalendarUtil.isCalendarInRange(calendar, mDelegate);
+ }
+
+ /**
+ * 跟新当前日期
+ */
+ abstract void updateCurrentDate();
+
+ /**
+ * 销毁
+ */
+ protected abstract void onDestroy();
+
+ protected int getWeekStartWith() {
+ return mDelegate != null ? mDelegate.getWeekStart() : CalendarViewDelegate.WEEK_START_WITH_SUN;
+ }
+
+
+ protected int getCalendarPaddingLeft() {
+ return mDelegate != null ? mDelegate.getCalendarPaddingLeft() : 0;
+ }
+
+
+ protected int getCalendarPaddingRight() {
+ return mDelegate != null ? mDelegate.getCalendarPaddingRight() : 0;
+ }
+
+
+ /**
+ * 初始化画笔相关
+ */
+ protected void initPaint() {
+
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/BaseWeekView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/BaseWeekView.java
new file mode 100644
index 00000000..1db6622a
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/BaseWeekView.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+
+/**
+ * 最基础周视图,因为日历UI采用热插拔实现,所以这里必须继承实现,达到UI一致即可
+ * 可通过此扩展各种视图如:WeekView、RangeWeekView
+ */
+
+public abstract class BaseWeekView extends BaseView {
+
+ public BaseWeekView(Context context) {
+ super(context);
+ }
+
+ /**
+ * 初始化周视图控件
+ *
+ * @param calendar calendar
+ */
+ final void setup(Calendar calendar) {
+ mItems = CalendarUtil.initCalendarForWeekView(calendar, mDelegate, mDelegate.getWeekStart());
+ addSchemesFromMap();
+ invalidate();
+ }
+
+
+ /**
+ * 记录已经选择的日期
+ *
+ * @param calendar calendar
+ */
+ final void setSelectedCalendar(Calendar calendar) {
+ if (mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_SINGLE &&
+ !calendar.equals(mDelegate.mSelectedCalendar)) {
+ return;
+ }
+ mCurrentItem = mItems.indexOf(calendar);
+ }
+
+
+ /**
+ * 周视图切换点击默认位置
+ *
+ * @param calendar calendar
+ * @param isNotice isNotice
+ */
+ final void performClickCalendar(Calendar calendar, boolean isNotice) {
+
+ if (mParentLayout == null ||
+ mDelegate.mInnerListener == null ||
+ mItems == null || mItems.size() == 0) {
+ return;
+ }
+
+ int week = CalendarUtil.getWeekViewIndexFromCalendar(calendar, mDelegate.getWeekStart());
+ if (mItems.contains(mDelegate.getCurrentDay())) {
+ week = CalendarUtil.getWeekViewIndexFromCalendar(mDelegate.getCurrentDay(), mDelegate.getWeekStart());
+ }
+
+ int curIndex = week;
+
+ Calendar currentCalendar = mItems.get(week);
+ if (mDelegate.getSelectMode() != CalendarViewDelegate.SELECT_MODE_DEFAULT) {
+ if (mItems.contains(mDelegate.mSelectedCalendar)) {
+ currentCalendar = mDelegate.mSelectedCalendar;
+ } else {
+ mCurrentItem = -1;
+ }
+ }
+
+ if (!isInRange(currentCalendar)) {
+ curIndex = getEdgeIndex(isMinRangeEdge(currentCalendar));
+ currentCalendar = mItems.get(curIndex);
+ }
+
+
+ currentCalendar.setCurrentDay(currentCalendar.equals(mDelegate.getCurrentDay()));
+ mDelegate.mInnerListener.onWeekDateSelected(currentCalendar, false);
+ int i = CalendarUtil.getWeekFromDayInMonth(currentCalendar, mDelegate.getWeekStart());
+ mParentLayout.updateSelectWeek(i);
+
+
+ if (mDelegate.mCalendarSelectListener != null
+ && isNotice
+ && mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_DEFAULT) {
+ mDelegate.mCalendarSelectListener.onCalendarSelect(currentCalendar, false);
+ }
+
+ mParentLayout.updateContentViewTranslateY();
+ if (mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_DEFAULT) {
+ mCurrentItem = curIndex;
+ }
+
+ if (!mDelegate.isShowYearSelectedLayout &&
+ mDelegate.mIndexCalendar != null &&
+ calendar.getYear() != mDelegate.mIndexCalendar.getYear() &&
+ mDelegate.mYearChangeListener != null) {
+ mDelegate.mYearChangeListener.onYearChange(mDelegate.mIndexCalendar.getYear());
+ }
+
+ mDelegate.mIndexCalendar = currentCalendar;
+ invalidate();
+ }
+
+ /**
+ * 是否是最小访问边界了
+ *
+ * @param calendar calendar
+ * @return 是否是最小访问边界了
+ */
+ final boolean isMinRangeEdge(Calendar calendar) {
+ java.util.Calendar c = java.util.Calendar.getInstance();
+ c.set(mDelegate.getMinYear(), mDelegate.getMinYearMonth() - 1, mDelegate.getMinYearDay());
+ long minTime = c.getTimeInMillis();
+ c.set(calendar.getYear(), calendar.getMonth() - 1, calendar.getDay());
+ long curTime = c.getTimeInMillis();
+ return curTime < minTime;
+ }
+
+ /**
+ * 获得边界范围内下标
+ *
+ * @param isMinEdge isMinEdge
+ * @return 获得边界范围内下标
+ */
+ final int getEdgeIndex(boolean isMinEdge) {
+ for (int i = 0; i < mItems.size(); i++) {
+ Calendar item = mItems.get(i);
+ boolean isInRange = isInRange(item);
+ if (isMinEdge && isInRange) {
+ return i;
+ } else if (!isMinEdge && !isInRange) {
+ return i - 1;
+ }
+ }
+ return isMinEdge ? 6 : 0;
+ }
+
+
+ /**
+ * 获取点击的日历
+ *
+ * @return 获取点击的日历
+ */
+ protected Calendar getIndex() {
+ if (mX <= mDelegate.getCalendarPaddingLeft() || mX >= getWidth() - mDelegate.getCalendarPaddingRight()) {
+ onClickCalendarPadding();
+ return null;
+ }
+
+ int indexX = (int) (mX - mDelegate.getCalendarPaddingLeft()) / mItemWidth;
+ if (indexX >= 7) {
+ indexX = 6;
+ }
+ int indexY = (int) mY / mItemHeight;
+ int position = indexY * 7 + indexX;// 选择项
+ if (position >= 0 && position < mItems.size())
+ return mItems.get(position);
+ return null;
+ }
+
+
+ private void onClickCalendarPadding() {
+ if (mDelegate.mClickCalendarPaddingListener == null) {
+ return;
+ }
+ Calendar calendar = null;
+ int indexX = (int) (mX - mDelegate.getCalendarPaddingLeft()) / mItemWidth;
+ if (indexX >= 7) {
+ indexX = 6;
+ }
+ int indexY = (int) mY / mItemHeight;
+ int position = indexY * 7 + indexX;// 选择项
+ if (position >= 0 && position < mItems.size()) {
+ calendar = mItems.get(position);
+ }
+ if (calendar == null) {
+ return;
+ }
+ mDelegate.mClickCalendarPaddingListener.onClickCalendarPadding(mX, mY, false, calendar,
+ getClickCalendarPaddingObject(mX, mY, calendar));
+ }
+
+ /**
+ * /**
+ * 获取点击事件处的对象
+ *
+ * @param x x
+ * @param y y
+ * @param adjacentCalendar adjacent calendar
+ * @return obj can as null
+ */
+ @SuppressWarnings("unused")
+ protected Object getClickCalendarPaddingObject(float x, float y, Calendar adjacentCalendar) {
+ return null;
+ }
+
+
+ /**
+ * 更新显示模式
+ */
+ final void updateShowMode() {
+ invalidate();
+ }
+
+ /**
+ * 更新周起始
+ */
+ final void updateWeekStart() {
+
+ int position = (int) getTag();
+ Calendar calendar = CalendarUtil.getFirstCalendarStartWithMinCalendar(mDelegate.getMinYear(),
+ mDelegate.getMinYearMonth(),
+ mDelegate.getMinYearDay(),
+ position + 1,
+ mDelegate.getWeekStart());
+ setSelectedCalendar(mDelegate.mSelectedCalendar);
+ setup(calendar);
+ }
+
+ /**
+ * 更新当选模式
+ */
+ final void updateSingleSelect() {
+ if (!mItems.contains(mDelegate.mSelectedCalendar)) {
+ mCurrentItem = -1;
+ invalidate();
+ }
+ }
+
+ @Override
+ void updateCurrentDate() {
+ if (mItems == null)
+ return;
+ if (mItems.contains(mDelegate.getCurrentDay())) {
+ for (Calendar a : mItems) {//添加操作
+ a.setCurrentDay(false);
+ }
+ int index = mItems.indexOf(mDelegate.getCurrentDay());
+ mItems.get(index).setCurrentDay(true);
+ }
+ invalidate();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(mItemHeight, MeasureSpec.EXACTLY);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ /**
+ * 开始绘制前的钩子,这里做一些初始化的操作,每次绘制只调用一次,性能高效
+ * 没有需要可忽略不实现
+ * 例如:
+ * 1、需要绘制圆形标记事件背景,可以在这里计算半径
+ * 2、绘制矩形选中效果,也可以在这里计算矩形宽和高
+ */
+ protected void onPreviewHook() {
+ // TODO: 2017/11/16
+ }
+
+
+ /**
+ * 循环绘制开始的回调,不需要可忽略
+ * 绘制每个日历项的循环,用来计算baseLine、圆心坐标等都可以在这里实现
+ *
+ * @param x 日历Card x起点坐标
+ */
+ @SuppressWarnings("unused")
+ protected void onLoopStart(int x) {
+ // TODO: 2017/11/16
+ }
+
+ @Override
+ protected void onDestroy() {
+
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/Calendar.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/Calendar.java
new file mode 100644
index 00000000..a71f03c3
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/Calendar.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.text.TextUtils;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 日历对象、
+ */
+@SuppressWarnings("all")
+public final class Calendar implements Serializable, Comparable {
+ private static final long serialVersionUID = 141315161718191143L;
+
+
+ /**
+ * 年
+ */
+ private int year;
+
+ /**
+ * 月1-12
+ */
+ private int month;
+
+ /**
+ * 如果是闰月,则返回闰月
+ */
+ private int leapMonth;
+
+ /**
+ * 日1-31
+ */
+ private int day;
+
+ /**
+ * 是否是闰年
+ */
+ private boolean isLeapYear;
+
+ /**
+ * 是否是本月,这里对应的是月视图的本月,而非当前月份,请注意
+ */
+ private boolean isCurrentMonth;
+
+ /**
+ * 是否是今天
+ */
+ private boolean isCurrentDay;
+
+ /**
+ * 农历字符串,没有特别大的意义,用来做简单的农历或者节日标记
+ * 建议通过lunarCakendar获取完整的农历日期
+ */
+ private String lunar;
+
+
+ /**
+ * 24节气
+ */
+ private String solarTerm;
+
+
+ /**
+ * 公历节日
+ */
+ private String gregorianFestival;
+
+ /**
+ * 传统农历节日
+ */
+ private String traditionFestival;
+
+ /**
+ * 计划,可以用来标记当天是否有任务,这里是默认的,如果使用多标记,请使用下面API
+ * using addScheme(int schemeColor,String scheme); multi scheme
+ */
+ private String scheme;
+
+ /**
+ * 各种自定义标记颜色、没有则选择默认颜色,如果使用多标记,请使用下面API
+ * using addScheme(int schemeColor,String scheme); multi scheme
+ */
+ private int schemeColor;
+
+
+ /**
+ * 多标记
+ * multi scheme,using addScheme();
+ */
+ private List schemes;
+
+ /**
+ * 是否是周末
+ */
+ private boolean isWeekend;
+
+ /**
+ * 星期,0-6 对应周日到周一
+ */
+ private int week;
+
+ /**
+ * 获取完整的农历日期
+ */
+ private Calendar lunarCalendar;
+
+
+ public int getYear() {
+ return year;
+ }
+
+ public void setYear(int year) {
+ this.year = year;
+ }
+
+ public int getMonth() {
+ return month;
+ }
+
+ public void setMonth(int month) {
+ this.month = month;
+ }
+
+ public int getDay() {
+ return day;
+ }
+
+ public void setDay(int day) {
+ this.day = day;
+ }
+
+ public boolean isCurrentMonth() {
+ return isCurrentMonth;
+ }
+
+
+ public void setCurrentMonth(boolean currentMonth) {
+ this.isCurrentMonth = currentMonth;
+ }
+
+ public boolean isCurrentDay() {
+ return isCurrentDay;
+ }
+
+ public void setCurrentDay(boolean currentDay) {
+ isCurrentDay = currentDay;
+ }
+
+
+ public String getLunar() {
+ return lunar;
+ }
+
+ public void setLunar(String lunar) {
+ this.lunar = lunar;
+ }
+
+
+ public String getScheme() {
+ return scheme;
+ }
+
+
+ public void setScheme(String scheme) {
+ this.scheme = scheme;
+ }
+
+
+ public int getSchemeColor() {
+ return schemeColor;
+ }
+
+ public void setSchemeColor(int schemeColor) {
+ this.schemeColor = schemeColor;
+ }
+
+
+ public List getSchemes() {
+ return schemes;
+ }
+
+ public void setSchemes(List schemes) {
+ this.schemes = schemes;
+ }
+
+
+ public void addScheme(Scheme scheme) {
+ if (schemes == null) {
+ schemes = new ArrayList<>();
+ }
+ schemes.add(scheme);
+ }
+
+ public void addScheme(int schemeColor, String scheme) {
+ if (schemes == null) {
+ schemes = new ArrayList<>();
+ }
+ schemes.add(new Scheme(schemeColor, scheme));
+ }
+
+ public void addScheme(int type, int schemeColor, String scheme) {
+ if (schemes == null) {
+ schemes = new ArrayList<>();
+ }
+ schemes.add(new Scheme(type, schemeColor, scheme));
+ }
+
+ public void addScheme(int type, int schemeColor, String scheme, String other) {
+ if (schemes == null) {
+ schemes = new ArrayList<>();
+ }
+ schemes.add(new Scheme(type, schemeColor, scheme, other));
+ }
+
+ public void addScheme(int schemeColor, String scheme, String other) {
+ if (schemes == null) {
+ schemes = new ArrayList<>();
+ }
+ schemes.add(new Scheme(schemeColor, scheme, other));
+ }
+
+ public boolean isWeekend() {
+ return isWeekend;
+ }
+
+ public void setWeekend(boolean weekend) {
+ isWeekend = weekend;
+ }
+
+ public int getWeek() {
+ return week;
+ }
+
+ public void setWeek(int week) {
+ this.week = week;
+ }
+
+ public Calendar getLunarCalendar() {
+ return lunarCalendar;
+ }
+
+ public void setLunarCalendar(Calendar lunarCakendar) {
+ this.lunarCalendar = lunarCakendar;
+ }
+
+ public String getSolarTerm() {
+ return solarTerm;
+ }
+
+ public void setSolarTerm(String solarTerm) {
+ this.solarTerm = solarTerm;
+ }
+
+ public String getGregorianFestival() {
+ return gregorianFestival;
+ }
+
+ public void setGregorianFestival(String gregorianFestival) {
+ this.gregorianFestival = gregorianFestival;
+ }
+
+
+ public int getLeapMonth() {
+ return leapMonth;
+ }
+
+ public void setLeapMonth(int leapMonth) {
+ this.leapMonth = leapMonth;
+ }
+
+ public boolean isLeapYear() {
+ return isLeapYear;
+ }
+
+ public void setLeapYear(boolean leapYear) {
+ isLeapYear = leapYear;
+ }
+
+ public String getTraditionFestival() {
+ return traditionFestival;
+ }
+
+ public void setTraditionFestival(String traditionFestival) {
+ this.traditionFestival = traditionFestival;
+ }
+
+ public boolean hasScheme() {
+ if (schemes != null && schemes.size() != 0) {
+ return true;
+ }
+ if (!TextUtils.isEmpty(scheme)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 是否是相同月份
+ *
+ * @param calendar 日期
+ * @return 是否是相同月份
+ */
+ public boolean isSameMonth(Calendar calendar) {
+ return year == calendar.getYear() && month == calendar.getMonth();
+ }
+
+ /**
+ * 比较日期
+ *
+ * @param calendar 日期
+ * @return <0 0 >0
+ */
+ public int compareTo(Calendar calendar) {
+ if (calendar == null) {
+ return 1;
+ }
+ return toString().compareTo(calendar.toString());
+ }
+
+ /**
+ * 运算差距多少天
+ *
+ * @param calendar calendar
+ * @return 运算差距多少天
+ */
+ public final int differ(Calendar calendar) {
+ return CalendarUtil.differ(this, calendar);
+ }
+
+ /**
+ * 日期是否可用
+ *
+ * @return 日期是否可用
+ */
+ public boolean isAvailable() {
+ return year > 0 & month > 0 & day > 0 & day <=31 & month <= 12 & year >= 1900 & year <= 2099;
+ }
+
+ /**
+ * 获取当前日历对应时间戳
+ *
+ * @return getTimeInMillis
+ */
+ public long getTimeInMillis() {
+ java.util.Calendar calendar = java.util.Calendar.getInstance();
+ calendar.set(java.util.Calendar.YEAR, year);
+ calendar.set(java.util.Calendar.MONTH, month - 1);
+ calendar.set(java.util.Calendar.DAY_OF_MONTH, day);
+ return calendar.getTimeInMillis();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o != null && o instanceof Calendar) {
+ if (((Calendar) o).getYear() == year && ((Calendar) o).getMonth() == month && ((Calendar) o).getDay() == day)
+ return true;
+ }
+ return super.equals(o);
+ }
+
+ @Override
+ public String toString() {
+ return year + "" + (month < 10 ? "0" + month : month) + "" + (day < 10 ? "0" + day : day);
+ }
+
+// @Override
+// public int compare(Calendar lhs, Calendar rhs) {
+// if (lhs == null || rhs == null) {
+// return 0;
+// }
+// int result = lhs.compareTo(rhs);
+// return result;
+// }
+
+ final void mergeScheme(Calendar calendar, String defaultScheme) {
+ if (calendar == null)
+ return;
+ setScheme(TextUtils.isEmpty(calendar.getScheme()) ?
+ defaultScheme : calendar.getScheme());
+ setSchemeColor(calendar.getSchemeColor());
+ setSchemes(calendar.getSchemes());
+ }
+
+ final void clearScheme() {
+ setScheme("");
+ setSchemeColor(0);
+ setSchemes(null);
+ }
+
+ /**
+ * 事件标记服务,现在多类型的事务标记建议使用这个
+ */
+ public final static class Scheme implements Serializable {
+ private int type;
+ private int shcemeColor;
+ private String scheme;
+ private String other;
+ private Object obj;
+
+ public Scheme() {
+ }
+
+ public Scheme(int type, int shcemeColor, String scheme, String other) {
+ this.type = type;
+ this.shcemeColor = shcemeColor;
+ this.scheme = scheme;
+ this.other = other;
+ }
+
+ public Scheme(int type, int shcemeColor, String scheme) {
+ this.type = type;
+ this.shcemeColor = shcemeColor;
+ this.scheme = scheme;
+ }
+
+ public Scheme(int shcemeColor, String scheme) {
+ this.shcemeColor = shcemeColor;
+ this.scheme = scheme;
+ }
+
+ public Scheme(int shcemeColor, String scheme, String other) {
+ this.shcemeColor = shcemeColor;
+ this.scheme = scheme;
+ this.other = other;
+ }
+
+ public int getShcemeColor() {
+ return shcemeColor;
+ }
+
+ public void setShcemeColor(int shcemeColor) {
+ this.shcemeColor = shcemeColor;
+ }
+
+ public String getScheme() {
+ return scheme;
+ }
+
+ public void setScheme(String scheme) {
+ this.scheme = scheme;
+ }
+
+ public String getOther() {
+ return other;
+ }
+
+ public void setOther(String other) {
+ this.other = other;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ public Object getObj() {
+ return obj;
+ }
+
+ public void setObj(Object obj) {
+ this.obj = obj;
+ }
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/CalendarLayout.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/CalendarLayout.java
new file mode 100644
index 00000000..8685e851
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/CalendarLayout.java
@@ -0,0 +1,958 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.animation.LinearInterpolator;
+import android.widget.AbsListView;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
+
+/**
+ * 日历布局
+ */
+@SuppressWarnings("unused")
+public class CalendarLayout extends LinearLayout {
+
+ /**
+ * 多点触控支持
+ */
+ private int mActivePointerId;
+
+ private static final int ACTIVE_POINTER = 1;
+
+ private static final int INVALID_POINTER = -1;
+
+ /**
+ * 周月视图
+ */
+ private static final int CALENDAR_SHOW_MODE_BOTH_MONTH_WEEK_VIEW = 0;
+
+
+ /**
+ * 仅周视图
+ */
+ private static final int CALENDAR_SHOW_MODE_ONLY_WEEK_VIEW = 1;
+
+ /**
+ * 仅月视图
+ */
+ private static final int CALENDAR_SHOW_MODE_ONLY_MONTH_VIEW = 2;
+
+ /**
+ * 默认展开
+ */
+ private static final int STATUS_EXPAND = 0;
+
+ /**
+ * 默认收缩
+ */
+ private static final int STATUS_SHRINK = 1;
+
+ /**
+ * 默认状态
+ */
+ private int mDefaultStatus;
+
+ private boolean isWeekView;
+
+ /**
+ * 星期栏
+ */
+ WeekBar mWeekBar;
+
+ /**
+ * 自定义ViewPager,月视图
+ */
+ MonthViewPager mMonthView;
+
+ /**
+ * 日历
+ */
+ CalendarView mCalendarView;
+
+ /**
+ * 自定义的周视图
+ */
+ WeekViewPager mWeekPager;
+
+ /**
+ * 年视图
+ */
+ YearViewPager mYearView;
+
+ /**
+ * ContentView
+ */
+ ViewGroup mContentView;
+
+ /**
+ * 默认手势
+ */
+ private static final int GESTURE_MODE_DEFAULT = 0;
+
+// /**
+ // * 仅日历有效
+ // */
+// private static final int GESTURE_MODE_ONLY_CALENDAR = 1;
+
+ /**
+ * 禁用手势
+ */
+ private static final int GESTURE_MODE_DISABLED = 2;
+
+ /**
+ * 手势模式
+ */
+ private int mGestureMode;
+
+
+ private int mCalendarShowMode;
+
+ private int mContentViewTranslateY; //ContentView 可滑动的最大距离距离 , 固定
+ private int mViewPagerTranslateY = 0;// ViewPager可以平移的距离,不代表mMonthView的平移距离
+
+ private float downY;
+ private float mLastY;
+ private float mLastX;
+ private boolean isAnimating = false;
+
+ /**
+ * 内容布局id
+ */
+ private int mContentViewId;
+
+ /**
+ * 手速判断
+ */
+ private VelocityTracker mVelocityTracker;
+ private int mMaximumVelocity;
+
+ private int mItemHeight;
+
+ private CalendarViewDelegate mDelegate;
+
+ public CalendarLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setOrientation(LinearLayout.VERTICAL);
+ TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CalendarLayout);
+ mContentViewId = array.getResourceId(R.styleable.CalendarLayout_calendar_content_view_id, 0);
+ mDefaultStatus = array.getInt(R.styleable.CalendarLayout_default_status, STATUS_EXPAND);
+ mCalendarShowMode = array.getInt(R.styleable.CalendarLayout_calendar_show_mode, CALENDAR_SHOW_MODE_BOTH_MONTH_WEEK_VIEW);
+ mGestureMode = array.getInt(R.styleable.CalendarLayout_gesture_mode, GESTURE_MODE_DEFAULT);
+ array.recycle();
+ mVelocityTracker = VelocityTracker.obtain();
+ final ViewConfiguration configuration = ViewConfiguration.get(context);
+ int mTouchSlop = configuration.getScaledTouchSlop();
+ mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ }
+
+ /**
+ * 初始化
+ *
+ * @param delegate delegate
+ */
+ final void setup(CalendarViewDelegate delegate) {
+ this.mDelegate = delegate;
+ mItemHeight = mDelegate.getCalendarItemHeight();
+ initCalendarPosition(delegate.mSelectedCalendar.isAvailable() ?
+ delegate.mSelectedCalendar :
+ delegate.createCurrentDate());
+ updateContentViewTranslateY();
+ }
+
+ /**
+ * 初始化当前时间的位置
+ *
+ * @param cur 当前日期时间
+ */
+ private void initCalendarPosition(Calendar cur) {
+ int diff = CalendarUtil.getMonthViewStartDiff(cur, mDelegate.getWeekStart());
+ int size = diff + cur.getDay() - 1;
+ updateSelectPosition(size);
+ }
+
+ /**
+ * 当前第几项被选中,更新平移量
+ *
+ * @param selectPosition 月视图被点击的position
+ */
+ final void updateSelectPosition(int selectPosition) {
+ int line = (selectPosition + 7) / 7;
+ mViewPagerTranslateY = (line - 1) * mItemHeight;
+ }
+
+ /**
+ * 设置选中的周,更新位置
+ *
+ * @param week week
+ */
+ final void updateSelectWeek(int week) {
+ mViewPagerTranslateY = (week - 1) * mItemHeight;
+ }
+
+
+ /**
+ * 更新内容ContentView可平移的最大距离
+ */
+ void updateContentViewTranslateY() {
+ Calendar calendar = mDelegate.mIndexCalendar;
+ if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ALL_MONTH) {
+ mContentViewTranslateY = 5 * mItemHeight;
+ } else {
+ mContentViewTranslateY = CalendarUtil.getMonthViewHeight(calendar.getYear(),
+ calendar.getMonth(), mItemHeight, mDelegate.getWeekStart())
+ - mItemHeight;
+ }
+ //已经显示周视图,则需要动态平移contentView的高度
+ if (mWeekPager.getVisibility() == VISIBLE) {
+ if (mContentView == null)
+ return;
+ mContentView.setTranslationY(-mContentViewTranslateY);
+ }
+ }
+
+ /**
+ * 更新日历项高度
+ */
+ final void updateCalendarItemHeight() {
+ mItemHeight = mDelegate.getCalendarItemHeight();
+ if (mContentView == null)
+ return;
+ Calendar calendar = mDelegate.mIndexCalendar;
+ updateSelectWeek(CalendarUtil.getWeekFromDayInMonth(calendar, mDelegate.getWeekStart()));
+ if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ALL_MONTH) {
+ mContentViewTranslateY = 5 * mItemHeight;
+ } else {
+ mContentViewTranslateY = CalendarUtil.getMonthViewHeight(calendar.getYear(), calendar.getMonth(),
+ mItemHeight, mDelegate.getWeekStart()) - mItemHeight;
+ }
+ translationViewPager();
+ if (mWeekPager.getVisibility() == VISIBLE) {
+ mContentView.setTranslationY(-mContentViewTranslateY);
+ }
+ }
+
+ /**
+ * 隐藏日历
+ */
+ public void hideCalendarView() {
+ if (mCalendarView == null) {
+ return;
+ }
+ mCalendarView.setVisibility(GONE);
+ if (!isExpand()) {
+ expand(0);
+ }
+ requestLayout();
+ }
+
+ /**
+ * 显示日历
+ */
+ public void showCalendarView() {
+
+ mCalendarView.setVisibility(VISIBLE);
+ requestLayout();
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mGestureMode == GESTURE_MODE_DISABLED ||
+ mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_MONTH_VIEW ||
+ mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_WEEK_VIEW) {//禁用手势,或者只显示某种视图
+ return false;
+ }
+ if (mDelegate == null) {
+ return false;
+ }
+ if (mDelegate.isShowYearSelectedLayout) {
+ return false;
+ }
+
+ if (mContentView == null || mCalendarView == null || mCalendarView.getVisibility() == GONE) {
+ return false;
+ }
+
+ int action = event.getAction();
+ float y = event.getY();
+ mVelocityTracker.addMovement(event);
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ int index = event.getActionIndex();
+ mActivePointerId = event.getPointerId(index);
+ mLastY = downY = y;
+ return true;
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ final int indexx = event.getActionIndex();
+ mActivePointerId = event.getPointerId(indexx);
+ if (mActivePointerId == 0) {
+ //核心代码:就是让下面的 dy = y- mLastY == 0,避免抖动
+ mLastY = event.getY(mActivePointerId);
+ }
+ break;
+ }
+ case MotionEvent.ACTION_MOVE:
+
+ getPointerIndex(event, mActivePointerId);
+ if (mActivePointerId == INVALID_POINTER) {
+ //如果切换了手指,那把mLastY换到最新手指的y坐标即可,核心就是让下面的 dy== 0,避免抖动
+ mLastY = y;
+ mActivePointerId = ACTIVE_POINTER;
+ }
+ float dy = y - mLastY;
+
+ //向上滑动,并且contentView平移到最大距离,显示周视图
+ if (dy < 0 && mContentView.getTranslationY() == -mContentViewTranslateY) {
+ mLastY = y;
+ event.setAction(MotionEvent.ACTION_DOWN);
+ dispatchTouchEvent(event);
+ mWeekPager.setVisibility(VISIBLE);
+ mMonthView.setVisibility(INVISIBLE);
+ if (!isWeekView && mDelegate.mViewChangeListener != null) {
+ mDelegate.mViewChangeListener.onViewChange(false);
+ }
+ isWeekView = true;
+ return true;
+ }
+ hideWeek(false);
+
+ //向下滑动,并且contentView已经完全平移到底部
+ if (dy > 0 && mContentView.getTranslationY() + dy >= 0) {
+ mContentView.setTranslationY(0);
+ translationViewPager();
+ mLastY = y;
+ return super.onTouchEvent(event);
+ }
+
+ //向上滑动,并且contentView已经平移到最大距离,则contentView平移到最大的距离
+ if (dy < 0 && mContentView.getTranslationY() + dy <= -mContentViewTranslateY) {
+ mContentView.setTranslationY(-mContentViewTranslateY);
+ translationViewPager();
+ mLastY = y;
+ return super.onTouchEvent(event);
+ }
+ //否则按比例平移
+ mContentView.setTranslationY(mContentView.getTranslationY() + dy);
+ translationViewPager();
+ mLastY = y;
+ break;
+ case MotionEvent.ACTION_CANCEL:
+
+ case MotionEvent.ACTION_POINTER_UP:
+ int pointerIndex = getPointerIndex(event, mActivePointerId);
+ if (mActivePointerId == INVALID_POINTER)
+ break;
+ mLastY = event.getY(pointerIndex);
+ break;
+ case MotionEvent.ACTION_UP:
+
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ float mYVelocity = velocityTracker.getYVelocity();
+ if (mContentView.getTranslationY() == 0
+ || mContentView.getTranslationY() == mContentViewTranslateY) {
+ expand();
+ break;
+ }
+ if (Math.abs(mYVelocity) >= 800) {
+ if (mYVelocity < 0) {
+ shrink();
+ } else {
+ expand();
+ }
+ return super.onTouchEvent(event);
+ }
+ if (event.getY() - downY > 0) {
+ expand();
+ } else {
+ shrink();
+ }
+ break;
+ }
+ return super.onTouchEvent(event);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (isAnimating) {
+ return super.dispatchTouchEvent(ev);
+ }
+ if (mGestureMode == GESTURE_MODE_DISABLED) {
+ return super.dispatchTouchEvent(ev);
+ }
+ if (mYearView == null ||
+ mCalendarView == null || mCalendarView.getVisibility() == GONE ||
+ mContentView == null ||
+ mContentView.getVisibility() != VISIBLE) {
+ return super.dispatchTouchEvent(ev);
+ }
+
+ if (mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_MONTH_VIEW ||
+ mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_WEEK_VIEW) {
+ return super.dispatchTouchEvent(ev);
+ }
+
+ if (mYearView.getVisibility() == VISIBLE || mDelegate.isShowYearSelectedLayout) {
+ return super.dispatchTouchEvent(ev);
+ }
+ final int action = ev.getAction();
+ float y = ev.getY();
+ if (action == MotionEvent.ACTION_MOVE) {
+ float dy = y - mLastY;
+ /*
+ * 如果向下滚动,有 2 种情况处理 且y在ViewPager下方
+ * 1、RecyclerView 或者其它滚动的View,当mContentView滚动到顶部时,拦截事件
+ * 2、非滚动控件,直接拦截事件
+ */
+ if (dy > 0 && mContentView.getTranslationY() == -mContentViewTranslateY) {
+ if (isScrollTop()) {
+ requestDisallowInterceptTouchEvent(false);//父View向子View拦截分发事件
+ return super.dispatchTouchEvent(ev);
+ }
+ }
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (isAnimating) {
+ return true;
+ }
+ if (mGestureMode == GESTURE_MODE_DISABLED) {
+ return false;
+ }
+ if (mYearView == null ||
+ mCalendarView == null || mCalendarView.getVisibility() == GONE ||
+ mContentView == null ||
+ mContentView.getVisibility() != VISIBLE) {
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ if (mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_MONTH_VIEW ||
+ mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_WEEK_VIEW) {
+ return false;
+ }
+
+ if (mYearView.getVisibility() == VISIBLE || mDelegate.isShowYearSelectedLayout) {
+ return super.onInterceptTouchEvent(ev);
+ }
+ final int action = ev.getAction();
+ float y = ev.getY();
+ float x = ev.getX();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ int index = ev.getActionIndex();
+ mActivePointerId = ev.getPointerId(index);
+ mLastY = downY = y;
+ mLastX = x;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ float dy = y - mLastY;
+ float dx = x - mLastX;
+ /*
+ 如果向上滚动,且ViewPager已经收缩,不拦截事件
+ */
+ if (dy < 0 && mContentView.getTranslationY() == -mContentViewTranslateY) {
+ return false;
+ }
+ /*
+ * 如果向下滚动,有 2 种情况处理 且y在ViewPager下方
+ * 1、RecyclerView 或者其它滚动的View,当mContentView滚动到顶部时,拦截事件
+ * 2、非滚动控件,直接拦截事件
+ */
+ if (dy > 0 && mContentView.getTranslationY() == -mContentViewTranslateY
+ && y >= mDelegate.getCalendarItemHeight() + mDelegate.getWeekBarHeight()) {
+ if (!isScrollTop()) {
+ return false;
+ }
+ }
+
+ if (dy > 0 && mContentView.getTranslationY() == 0 && y >= CalendarUtil.dipToPx(getContext(), 98)) {
+ return false;
+ }
+
+ if (Math.abs(dy) > Math.abs(dx) ) { //纵向滑动距离大于横向滑动距离,拦截滑动事件
+ if ((dy > 0 && mContentView.getTranslationY() <= 0)
+ || (dy < 0 && mContentView.getTranslationY() >= -mContentViewTranslateY)) {
+ mLastY = y;
+ return true;
+ }
+ }
+ break;
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
+
+ private int getPointerIndex(MotionEvent ev, int id) {
+ int activePointerIndex = ev.findPointerIndex(id);
+ if (activePointerIndex == -1) {
+ mActivePointerId = INVALID_POINTER;
+ }
+ return activePointerIndex;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ if (mContentView == null || mCalendarView == null) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
+
+ int year = mDelegate.mIndexCalendar.getYear();
+ int month = mDelegate.mIndexCalendar.getMonth();
+ int weekBarHeight = CalendarUtil.dipToPx(getContext(), 1)
+ + mDelegate.getWeekBarHeight();
+
+ int monthHeight = CalendarUtil.getMonthViewHeight(year, month,
+ mDelegate.getCalendarItemHeight(),
+ mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode())
+ + weekBarHeight;
+
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ if (mDelegate.isFullScreenCalendar()) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int heightSpec = MeasureSpec.makeMeasureSpec(height - weekBarHeight - mDelegate.getCalendarItemHeight(),
+ MeasureSpec.EXACTLY);
+ mContentView.measure(widthMeasureSpec, heightSpec);
+ mContentView.layout(mContentView.getLeft(), mContentView.getTop(), mContentView.getRight(), mContentView.getBottom());
+ return;
+ }
+
+ if (monthHeight >= height && mMonthView.getHeight() > 0) {
+ height = monthHeight;
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(monthHeight +
+ weekBarHeight +
+ mDelegate.getWeekBarHeight(), MeasureSpec.EXACTLY);
+ } else if (monthHeight < height && mMonthView.getHeight() > 0) {
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+ }
+
+ int h;
+ if (mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_MONTH_VIEW ||
+ mCalendarView.getVisibility() == GONE) {
+ h = height - (mCalendarView.getVisibility() == GONE ? 0 : mCalendarView.getHeight());
+ } else if (mGestureMode == GESTURE_MODE_DISABLED && !isAnimating) {
+ if (isExpand()) {
+ h = height - monthHeight;
+ } else {
+ h = height - weekBarHeight - mItemHeight;
+ }
+ } else {
+ h = height - weekBarHeight - mItemHeight;
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int heightSpec = MeasureSpec.makeMeasureSpec(h,
+ MeasureSpec.EXACTLY);
+ mContentView.measure(widthMeasureSpec, heightSpec);
+ mContentView.layout(mContentView.getLeft(), mContentView.getTop(), mContentView.getRight(), mContentView.getBottom());
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mMonthView = findViewById(R.id.vp_month);
+ mWeekPager = findViewById(R.id.vp_week);
+ if (getChildCount() > 0) {
+ mCalendarView = (CalendarView) getChildAt(0);
+ }
+ mContentView = findViewById(mContentViewId);
+ mYearView = findViewById(R.id.selectLayout);
+ }
+
+
+ /**
+ * 平移ViewPager月视图
+ */
+ private void translationViewPager() {
+ float percent = mContentView.getTranslationY() * 1.0f / mContentViewTranslateY;
+ mMonthView.setTranslationY(mViewPagerTranslateY * percent);
+ }
+
+
+ public void setModeBothMonthWeekView() {
+ mCalendarShowMode = CALENDAR_SHOW_MODE_BOTH_MONTH_WEEK_VIEW;
+ requestLayout();
+ }
+
+ public void setModeOnlyWeekView() {
+ mCalendarShowMode = CALENDAR_SHOW_MODE_ONLY_WEEK_VIEW;
+ requestLayout();
+ }
+
+ public void setModeOnlyMonthView() {
+ mCalendarShowMode = CALENDAR_SHOW_MODE_ONLY_MONTH_VIEW;
+ requestLayout();
+ }
+
+
+ @Nullable
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Bundle bundle = new Bundle();
+ Parcelable parcelable = super.onSaveInstanceState();
+ bundle.putParcelable("super", parcelable);
+ bundle.putBoolean("isExpand", isExpand());
+ return bundle;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ Bundle bundle = (Bundle) state;
+ Parcelable superData = bundle.getParcelable("super");
+ boolean isExpand = bundle.getBoolean("isExpand");
+ if (isExpand) {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ expand(0);
+ }
+ });
+ } else {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ shrink(0);
+ }
+ });
+
+ }
+ super.onRestoreInstanceState(superData);
+ }
+
+ /**
+ * 是否展开了
+ *
+ * @return isExpand
+ */
+ public final boolean isExpand() {
+ return mMonthView.getVisibility() == VISIBLE;
+ }
+
+
+ public boolean expand() {
+ return expand(240);
+ }
+
+
+ /**
+ * 展开
+ *
+ * @param duration 时长
+ * @return 展开是否成功
+ */
+ public boolean expand(int duration) {
+ if (isAnimating ||
+ mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_WEEK_VIEW ||
+ mContentView == null)
+ return false;
+ if (mMonthView.getVisibility() != VISIBLE) {
+ mWeekPager.setVisibility(GONE);
+ onShowMonthView();
+ isWeekView = false;
+ mMonthView.setVisibility(VISIBLE);
+ }
+ ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mContentView,
+ "translationY", mContentView.getTranslationY(), 0f);
+ objectAnimator.setDuration(duration);
+ objectAnimator.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float currentValue = (Float) animation.getAnimatedValue();
+ float percent = currentValue * 1.0f / mContentViewTranslateY;
+ mMonthView.setTranslationY(mViewPagerTranslateY * percent);
+ isAnimating = true;
+ }
+ });
+ objectAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ isAnimating = false;
+ if (mGestureMode == GESTURE_MODE_DISABLED) {
+ requestLayout();
+ }
+ hideWeek(true);
+ if (mDelegate.mViewChangeListener != null && isWeekView) {
+ mDelegate.mViewChangeListener.onViewChange(true);
+ }
+ isWeekView = false;
+
+ }
+ });
+ objectAnimator.start();
+ return true;
+ }
+
+ public boolean shrink() {
+ return shrink(240);
+ }
+
+ /**
+ * 收缩
+ *
+ * @param duration 时长
+ * @return 成功或者失败
+ */
+ public boolean shrink(int duration) {
+ if (mGestureMode == GESTURE_MODE_DISABLED) {
+ requestLayout();
+ }
+ if (isAnimating || mContentView == null) {
+ return false;
+ }
+ ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mContentView,
+ "translationY", mContentView.getTranslationY(), -mContentViewTranslateY);
+ objectAnimator.setDuration(duration);
+ objectAnimator.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float currentValue = (Float) animation.getAnimatedValue();
+ float percent = currentValue * 1.0f / mContentViewTranslateY;
+ mMonthView.setTranslationY(mViewPagerTranslateY * percent);
+ isAnimating = true;
+ }
+ });
+ objectAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ isAnimating = false;
+ showWeek();
+ isWeekView = true;
+
+ }
+ });
+ objectAnimator.start();
+ return true;
+ }
+
+ /**
+ * 初始化状态
+ */
+ final void initStatus() {
+
+ if ((mDefaultStatus == STATUS_SHRINK ||
+ mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_WEEK_VIEW) &&
+ mCalendarShowMode != CALENDAR_SHOW_MODE_ONLY_MONTH_VIEW) {
+ if (mContentView == null) {
+ mWeekPager.setVisibility(VISIBLE);
+ mMonthView.setVisibility(GONE);
+ return;
+ }
+ post(new Runnable() {
+ @Override
+ public void run() {
+ ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mContentView,
+ "translationY", mContentView.getTranslationY(), -mContentViewTranslateY);
+ objectAnimator.setDuration(0);
+ objectAnimator.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float currentValue = (Float) animation.getAnimatedValue();
+ float percent = currentValue * 1.0f / mContentViewTranslateY;
+ mMonthView.setTranslationY(mViewPagerTranslateY * percent);
+ isAnimating = true;
+ }
+ });
+ objectAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ isAnimating = false;
+ isWeekView = true;
+ showWeek();
+ if (mDelegate == null || mDelegate.mViewChangeListener == null) {
+ return;
+ }
+ mDelegate.mViewChangeListener.onViewChange(false);
+
+ }
+ });
+ objectAnimator.start();
+ }
+ });
+ } else {
+ if (mDelegate.mViewChangeListener == null) {
+ return;
+ }
+ post(new Runnable() {
+ @Override
+ public void run() {
+ mDelegate.mViewChangeListener.onViewChange(true);
+ }
+ });
+ }
+ }
+
+ /**
+ * 隐藏周视图
+ */
+ private void hideWeek(boolean isNotify) {
+ if (isNotify) {
+ onShowMonthView();
+ }
+ mWeekPager.setVisibility(GONE);
+ mMonthView.setVisibility(VISIBLE);
+ }
+
+ /**
+ * 显示周视图
+ */
+ private void showWeek() {
+ onShowWeekView();
+ if (mWeekPager != null && mWeekPager.getAdapter() != null) {
+ mWeekPager.getAdapter().notifyDataSetChanged();
+ mWeekPager.setVisibility(VISIBLE);
+ }
+ mMonthView.setVisibility(INVISIBLE);
+ }
+
+ /**
+ * 周视图显示事件
+ */
+ private void onShowWeekView() {
+ if (mWeekPager.getVisibility() == VISIBLE) {
+ return;
+ }
+ if (mDelegate != null && mDelegate.mViewChangeListener != null && !isWeekView) {
+ mDelegate.mViewChangeListener.onViewChange(false);
+ }
+ }
+
+
+ /**
+ * 周视图显示事件
+ */
+ private void onShowMonthView() {
+ if (mMonthView.getVisibility() == VISIBLE) {
+ return;
+ }
+ if (mDelegate != null && mDelegate.mViewChangeListener != null && isWeekView) {
+ mDelegate.mViewChangeListener.onViewChange(true);
+ }
+ }
+
+ /**
+ * ContentView是否滚动到顶部 如果完全不适合,就复写这个方法
+ *
+ * @return 是否滚动到顶部
+ */
+ protected boolean isScrollTop() {
+ if (mContentView instanceof CalendarScrollView) {
+ return ((CalendarScrollView) mContentView).isScrollToTop();
+ }
+ if (mContentView instanceof RecyclerView)
+ return ((RecyclerView) mContentView).computeVerticalScrollOffset() == 0;
+ if (mContentView instanceof AbsListView) {
+ boolean result = false;
+ AbsListView listView = (AbsListView) mContentView;
+ if (listView.getFirstVisiblePosition() == 0) {
+ final View topChildView = listView.getChildAt(0);
+ result = topChildView.getTop() == 0;
+ }
+ return result;
+ }
+ return mContentView.getScrollY() == 0;
+ }
+
+
+ /**
+ * 隐藏内容布局
+ */
+ @SuppressLint("NewApi")
+ final void hideContentView() {
+ if (mContentView == null)
+ return;
+ mContentView.animate()
+ .translationY(getHeight() - mMonthView.getHeight())
+ .setDuration(220)
+ .setInterpolator(new LinearInterpolator())
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mContentView.setVisibility(INVISIBLE);
+ mContentView.clearAnimation();
+ }
+ });
+ }
+
+ /**
+ * 显示内容布局
+ */
+ @SuppressLint("NewApi")
+ final void showContentView() {
+ if (mContentView == null)
+ return;
+ mContentView.setTranslationY(getHeight() - mMonthView.getHeight());
+ mContentView.setVisibility(VISIBLE);
+ mContentView.animate()
+ .translationY(0)
+ .setDuration(180)
+ .setInterpolator(new LinearInterpolator())
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ }
+ });
+ }
+
+
+ @SuppressWarnings("unused")
+ private int getCalendarViewHeight() {
+ return mMonthView.getVisibility() == VISIBLE ? mDelegate.getWeekBarHeight() + mMonthView.getHeight() :
+ mDelegate.getWeekBarHeight() + mDelegate.getCalendarItemHeight();
+ }
+
+ /**
+ * 如果有十分特别的ContentView,可以自定义实现这个接口
+ */
+ public interface CalendarScrollView {
+ /**
+ * 是否滚动到顶部
+ *
+ * @return 是否滚动到顶部
+ */
+ boolean isScrollToTop();
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/CalendarUtil.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/CalendarUtil.java
new file mode 100644
index 00000000..a77157c0
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/CalendarUtil.java
@@ -0,0 +1,815 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 一些日期辅助计算工具
+ */
+@SuppressWarnings("all")
+public final class CalendarUtil {
+
+ private static final long ONE_DAY = 1000 * 3600 * 24;
+
+ @SuppressLint("SimpleDateFormat")
+ static int getDate(String formatStr, Date date) {
+ SimpleDateFormat format = new SimpleDateFormat(formatStr);
+ return Integer.parseInt(format.format(date));
+ }
+
+ /**
+ * 判断一个日期是否是周末,即周六日
+ *
+ * @param calendar calendar
+ * @return 判断一个日期是否是周末,即周六日
+ */
+ public static boolean isWeekend(Calendar calendar) {
+ int week = getWeekFormCalendar(calendar);
+ return week == 0 || week == 6;
+ }
+
+ /**
+ * 获取某月的天数
+ *
+ * @param year 年
+ * @param month 月
+ * @return 某月的天数
+ */
+ public static int getMonthDaysCount(int year, int month) {
+ int count = 0;
+ //判断大月份
+ if (month == 1 || month == 3 || month == 5 || month == 7
+ || month == 8 || month == 10 || month == 12) {
+ count = 31;
+ }
+
+ //判断小月
+ if (month == 4 || month == 6 || month == 9 || month == 11) {
+ count = 30;
+ }
+
+ //判断平年与闰年
+ if (month == 2) {
+ if (isLeapYear(year)) {
+ count = 29;
+ } else {
+ count = 28;
+ }
+ }
+ return count;
+ }
+
+
+ /**
+ * 是否是闰年
+ *
+ * @param year year
+ * @return 是否是闰年
+ */
+ public static boolean isLeapYear(int year) {
+ return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
+ }
+
+
+ public static int getMonthViewLineCount(int year, int month, int weekStartWith, int mode) {
+ if (mode == CalendarViewDelegate.MODE_ALL_MONTH) {
+ return 6;
+ }
+ int nextDiff = CalendarUtil.getMonthEndDiff(year, month, weekStartWith);
+ int preDiff = CalendarUtil.getMonthViewStartDiff(year, month, weekStartWith);
+ int monthDayCount = CalendarUtil.getMonthDaysCount(year, month);
+ return (preDiff + monthDayCount + nextDiff) / 7;
+ }
+
+ /**
+ * 获取月视图的确切高度
+ * Test pass
+ *
+ * @param year 年
+ * @param month 月
+ * @param itemHeight 每项的高度
+ * @param weekStartWith 周起始
+ * @return 不需要多余行的高度
+ */
+ public static int getMonthViewHeight(int year, int month, int itemHeight, int weekStartWith) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+ date.set(year, month - 1, 1, 12, 0, 0);
+ int preDiff = getMonthViewStartDiff(year, month, weekStartWith);
+ int monthDaysCount = getMonthDaysCount(year, month);
+ int nextDiff = getMonthEndDiff(year, month, monthDaysCount, weekStartWith);
+ return (preDiff + monthDaysCount + nextDiff) / 7 * itemHeight;
+ }
+
+ /**
+ * 获取月视图的确切高度
+ * Test pass
+ *
+ * @param year 年
+ * @param month 月
+ * @param itemHeight 每项的高度
+ * @param weekStartWith weekStartWith
+ * @param mode mode
+ * @return 不需要多余行的高度
+ */
+ public static int getMonthViewHeight(int year, int month, int itemHeight, int weekStartWith, int mode) {
+ if (mode == CalendarViewDelegate.MODE_ALL_MONTH) {
+ return itemHeight * 6;
+ }
+ return getMonthViewHeight(year, month, itemHeight, weekStartWith);
+ }
+
+ /**
+ * 获取某天在该月的第几周,换言之就是获取这一天在该月视图的第几行,第几周,根据周起始动态获取
+ * Test pass,单元测试通过
+ *
+ * @param calendar calendar
+ * @param weekStart 其实星期是哪一天?
+ * @return 获取某天在该月的第几周 the week line in MonthView
+ */
+ public static int getWeekFromDayInMonth(Calendar calendar, int weekStart) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+ date.set(calendar.getYear(), calendar.getMonth() - 1, 1, 12, 0, 0);
+ //该月第一天为星期几,星期天 == 0
+ int diff = getMonthViewStartDiff(calendar, weekStart);
+ return (calendar.getDay() + diff - 1) / 7 + 1;
+ }
+
+ /**
+ * 获取上一个日子
+ *
+ * @param calendar calendar
+ * @return 获取上一个日子
+ */
+ public static Calendar getPreCalendar(Calendar calendar) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+
+ date.set(calendar.getYear(), calendar.getMonth() - 1, calendar.getDay(), 12, 0, 0);//
+
+ long timeMills = date.getTimeInMillis();//获得起始时间戳
+
+ date.setTimeInMillis(timeMills - ONE_DAY);
+
+ Calendar preCalendar = new Calendar();
+ preCalendar.setYear(date.get(java.util.Calendar.YEAR));
+ preCalendar.setMonth(date.get(java.util.Calendar.MONTH) + 1);
+ preCalendar.setDay(date.get(java.util.Calendar.DAY_OF_MONTH));
+
+ return preCalendar;
+ }
+
+ public static Calendar getNextCalendar(Calendar calendar) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+
+ date.set(calendar.getYear(), calendar.getMonth() - 1, calendar.getDay(), 12, 0, 0);//
+
+ long timeMills = date.getTimeInMillis();//获得起始时间戳
+
+ date.setTimeInMillis(timeMills + ONE_DAY);
+
+ Calendar nextCalendar = new Calendar();
+ nextCalendar.setYear(date.get(java.util.Calendar.YEAR));
+ nextCalendar.setMonth(date.get(java.util.Calendar.MONTH) + 1);
+ nextCalendar.setDay(date.get(java.util.Calendar.DAY_OF_MONTH));
+
+ return nextCalendar;
+ }
+
+ /**
+ * DAY_OF_WEEK return 1 2 3 4 5 6 7,偏移了一位
+ * 获取日期所在月视图对应的起始偏移量
+ * Test pass
+ *
+ * @param calendar calendar
+ * @param weekStart weekStart 星期的起始
+ * @return 获取日期所在月视图对应的起始偏移量 the start diff with MonthView
+ */
+ static int getMonthViewStartDiff(Calendar calendar, int weekStart) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+ date.set(calendar.getYear(), calendar.getMonth() - 1, 1, 12, 0, 0);
+ int week = date.get(java.util.Calendar.DAY_OF_WEEK);
+ if (weekStart == CalendarViewDelegate.WEEK_START_WITH_SUN) {
+ return week - 1;
+ }
+ if (weekStart == CalendarViewDelegate.WEEK_START_WITH_MON) {
+ return week == 1 ? 6 : week - weekStart;
+ }
+ return week == CalendarViewDelegate.WEEK_START_WITH_SAT ? 0 : week;
+ }
+
+
+ /**
+ * DAY_OF_WEEK return 1 2 3 4 5 6 7,偏移了一位
+ * 获取日期所在月视图对应的起始偏移量
+ * Test pass
+ *
+ * @param year 年
+ * @param month 月
+ * @param weekStart 周起始
+ * @return 获取日期所在月视图对应的起始偏移量 the start diff with MonthView
+ */
+ static int getMonthViewStartDiff(int year, int month, int weekStart) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+ date.set(year, month - 1, 1, 12, 0, 0);
+ int week = date.get(java.util.Calendar.DAY_OF_WEEK);
+ if (weekStart == CalendarViewDelegate.WEEK_START_WITH_SUN) {
+ return week - 1;
+ }
+ if (weekStart == CalendarViewDelegate.WEEK_START_WITH_MON) {
+ return week == 1 ? 6 : week - weekStart;
+ }
+ return week == CalendarViewDelegate.WEEK_START_WITH_SAT ? 0 : week;
+ }
+
+
+ /**
+ * DAY_OF_WEEK return 1 2 3 4 5 6 7,偏移了一位
+ * 获取日期月份对应的结束偏移量,用于计算两个年份之间总共有多少周,不用于MonthView
+ * Test pass
+ *
+ * @param year 年
+ * @param month 月
+ * @param weekStart 周起始
+ * @return 获取日期月份对应的结束偏移量 the end diff in Month not MonthView
+ */
+ static int getMonthEndDiff(int year, int month, int weekStart) {
+ return getMonthEndDiff(year, month, getMonthDaysCount(year, month), weekStart);
+ }
+
+
+ /**
+ * DAY_OF_WEEK return 1 2 3 4 5 6 7,偏移了一位
+ * 获取日期月份对应的结束偏移量,用于计算两个年份之间总共有多少周,不用于MonthView
+ * Test pass
+ *
+ * @param year 年
+ * @param month 月
+ * @param weekStart 周起始
+ * @return 获取日期月份对应的结束偏移量 the end diff in Month not MonthView
+ */
+ private static int getMonthEndDiff(int year, int month, int day, int weekStart) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+ date.set(year, month - 1, day);
+ int week = date.get(java.util.Calendar.DAY_OF_WEEK);
+ if (weekStart == CalendarViewDelegate.WEEK_START_WITH_SUN) {
+ return 7 - week;
+ }
+ if (weekStart == CalendarViewDelegate.WEEK_START_WITH_MON) {
+ return week == 1 ? 0 : 7 - week + 1;
+ }
+ return week == 7 ? 6 : 7 - week - 1;
+ }
+
+ /**
+ * 获取某个日期是星期几
+ * 测试通过
+ *
+ * @param calendar 某个日期
+ * @return 返回某个日期是星期几
+ */
+ static int getWeekFormCalendar(Calendar calendar) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+ date.set(calendar.getYear(), calendar.getMonth() - 1, calendar.getDay());
+ return date.get(java.util.Calendar.DAY_OF_WEEK) - 1;
+ }
+
+
+ /**
+ * 获取周视图的切换默认选项位置 WeekView index
+ * 测试通过 test pass
+ *
+ * @param calendar calendar
+ * @param weekStart weekStart
+ * @return 获取周视图的切换默认选项位置
+ */
+ static int getWeekViewIndexFromCalendar(Calendar calendar, int weekStart) {
+ return getWeekViewStartDiff(calendar.getYear(), calendar.getMonth(), calendar.getDay(), weekStart);
+ }
+
+ /**
+ * 是否在日期范围內
+ * 测试通过 test pass
+ *
+ * @param calendar calendar
+ * @param minYear minYear
+ * @param minYearDay 最小年份天
+ * @param minYearMonth minYearMonth
+ * @param maxYear maxYear
+ * @param maxYearMonth maxYearMonth
+ * @param maxYearDay 最大年份天
+ * @return 是否在日期范围內
+ */
+ static boolean isCalendarInRange(Calendar calendar,
+ int minYear, int minYearMonth, int minYearDay,
+ int maxYear, int maxYearMonth, int maxYearDay) {
+ java.util.Calendar c = java.util.Calendar.getInstance();
+ c.set(minYear, minYearMonth - 1, minYearDay);
+ long minTime = c.getTimeInMillis();
+ c.set(maxYear, maxYearMonth - 1, maxYearDay);
+ long maxTime = c.getTimeInMillis();
+ c.set(calendar.getYear(), calendar.getMonth() - 1, calendar.getDay());
+ long curTime = c.getTimeInMillis();
+ return curTime >= minTime && curTime <= maxTime;
+ }
+
+ /**
+ * 获取两个日期之间一共有多少周,
+ * 注意周起始周一、周日、周六
+ * 测试通过 test pass
+ *
+ * @param minYear minYear 最小年份
+ * @param minYearMonth maxYear 最小年份月份
+ * @param minYearDay 最小年份天
+ * @param maxYear maxYear 最大年份
+ * @param maxYearMonth maxYear 最大年份月份
+ * @param maxYearDay 最大年份天
+ * @param weekStart 周起始
+ * @return 周数用于WeekViewPager itemCount
+ */
+ public static int getWeekCountBetweenBothCalendar(int minYear, int minYearMonth, int minYearDay,
+ int maxYear, int maxYearMonth, int maxYearDay,
+ int weekStart) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+ date.set(minYear, minYearMonth - 1, minYearDay);
+ long minTimeMills = date.getTimeInMillis();//给定时间戳
+ int preDiff = getWeekViewStartDiff(minYear, minYearMonth, minYearDay, weekStart);
+
+ date.set(maxYear, maxYearMonth - 1, maxYearDay);
+
+ long maxTimeMills = date.getTimeInMillis();//给定时间戳
+
+ int nextDiff = getWeekViewEndDiff(maxYear, maxYearMonth, maxYearDay, weekStart);
+
+ int count = preDiff + nextDiff;
+
+ int c = (int) ((maxTimeMills - minTimeMills) / ONE_DAY) + 1;
+ count += c;
+ return count / 7;
+ }
+
+
+ /**
+ * 根据日期获取距离最小日期在第几周
+ * 用来设置 WeekView currentItem
+ * 测试通过 test pass
+ *
+ * @param calendar calendar
+ * @param minYear minYear 最小年份
+ * @param minYearMonth maxYear 最小年份月份
+ * @param minYearDay 最小年份天
+ * @param weekStart 周起始
+ * @return 返回两个年份中第几周 the WeekView currentItem
+ */
+ public static int getWeekFromCalendarStartWithMinCalendar(Calendar calendar,
+ int minYear, int minYearMonth, int minYearDay,
+ int weekStart) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+ date.set(minYear, minYearMonth - 1, minYearDay);//起始日期
+ long firstTimeMill = date.getTimeInMillis();//获得范围起始时间戳
+
+ int preDiff = getWeekViewStartDiff(minYear, minYearMonth, minYearDay, weekStart);//范围起始的周偏移量
+
+ int weekStartDiff = getWeekViewStartDiff(calendar.getYear(),
+ calendar.getMonth(),
+ calendar.getDay(),
+ weekStart);//获取点击的日子在周视图的起始,为了兼容全球时区,最大日差为一天,如果周起始偏差weekStartDiff=0,则日期加1
+
+ date.set(calendar.getYear(),
+ calendar.getMonth() - 1,
+ weekStartDiff == 0 ? calendar.getDay() + 1 : calendar.getDay());
+
+ long curTimeMills = date.getTimeInMillis();//给定时间戳
+
+ int c = (int) ((curTimeMills - firstTimeMill) / ONE_DAY);
+
+ int count = preDiff + c;
+
+ return count / 7 + 1;
+ }
+
+ /**
+ * 根据星期数和最小日期推算出该星期的第一天,
+ * 为了防止夏令时,导致的时间提前和延后1-2小时,导致日期出现误差1天,因此吧hourOfDay = 12
+ * //测试通过 Test pass
+ *
+ * @param minYear 最小年份如2017
+ * @param minYearMonth maxYear 最小年份月份,like : 2017-07
+ * @param minYearDay 最小年份天
+ * @param week 从最小年份minYear月minYearMonth 日1 开始的第几周 week > 0
+ * @param weekStart 周起始
+ * @return 该星期的第一天日期
+ */
+ public static Calendar getFirstCalendarStartWithMinCalendar(int minYear, int minYearMonth, int minYearDay, int week, int weekStart) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+
+ date.set(minYear, minYearMonth - 1, minYearDay, 12, 0);//
+
+ long firstTimeMills = date.getTimeInMillis();//获得起始时间戳
+
+
+ long weekTimeMills = (week - 1) * 7 * ONE_DAY;
+
+ long timeCountMills = weekTimeMills + firstTimeMills;
+
+ date.setTimeInMillis(timeCountMills);
+
+ int startDiff = getWeekViewStartDiff(date.get(java.util.Calendar.YEAR),
+ date.get(java.util.Calendar.MONTH) + 1,
+ date.get(java.util.Calendar.DAY_OF_MONTH), weekStart);
+
+ timeCountMills -= startDiff * ONE_DAY;
+ date.setTimeInMillis(timeCountMills);
+
+ Calendar calendar = new Calendar();
+ calendar.setYear(date.get(java.util.Calendar.YEAR));
+ calendar.setMonth(date.get(java.util.Calendar.MONTH) + 1);
+ calendar.setDay(date.get(java.util.Calendar.DAY_OF_MONTH));
+
+ return calendar;
+ }
+
+
+ /**
+ * 是否在日期范围内
+ *
+ * @param calendar calendar
+ * @param delegate delegate
+ * @return 是否在日期范围内
+ */
+ static boolean isCalendarInRange(Calendar calendar, CalendarViewDelegate delegate) {
+ return isCalendarInRange(calendar,
+ delegate.getMinYear(), delegate.getMinYearMonth(), delegate.getMinYearDay(),
+ delegate.getMaxYear(), delegate.getMaxYearMonth(), delegate.getMaxYearDay());
+ }
+
+ /**
+ * 是否在日期范围內
+ *
+ * @param year year
+ * @param month month
+ * @param minYear minYear
+ * @param minYearMonth minYearMonth
+ * @param maxYear maxYear
+ * @param maxYearMonth maxYearMonth
+ * @return 是否在日期范围內
+ */
+ static boolean isMonthInRange(int year, int month, int minYear, int minYearMonth, int maxYear, int maxYearMonth) {
+ return !(year < minYear || year > maxYear) &&
+ !(year == minYear && month < minYearMonth) &&
+ !(year == maxYear && month > maxYearMonth);
+ }
+
+ /**
+ * 运算 calendar1 - calendar2
+ * test Pass
+ *
+ * @param calendar1 calendar1
+ * @param calendar2 calendar2
+ * @return calendar1 - calendar2
+ */
+ public static int differ(Calendar calendar1, Calendar calendar2) {
+ if (calendar1 == null) {
+ return Integer.MIN_VALUE;
+ }
+ if (calendar2 == null) {
+ return Integer.MAX_VALUE;
+ }
+ java.util.Calendar date = java.util.Calendar.getInstance();
+
+ date.set(calendar1.getYear(), calendar1.getMonth() - 1, calendar1.getDay(), 12, 0, 0);//
+
+ long startTimeMills = date.getTimeInMillis();//获得起始时间戳
+
+ date.set(calendar2.getYear(), calendar2.getMonth() - 1, calendar2.getDay(), 12, 0, 0);//
+
+ long endTimeMills = date.getTimeInMillis();//获得结束时间戳
+
+ return (int) ((startTimeMills - endTimeMills) / ONE_DAY);
+ }
+
+ /**
+ * 比较日期大小
+ *
+ * @param minYear minYear
+ * @param minYearMonth minYearMonth
+ * @param minYearDay minYearDay
+ * @param maxYear maxYear
+ * @param maxYearMonth maxYearMonth
+ * @param maxYearDay maxYearDay
+ * @return <0 0 >0
+ */
+ public static int compareTo(int minYear, int minYearMonth, int minYearDay,
+ int maxYear, int maxYearMonth, int maxYearDay) {
+ Calendar first = new Calendar();
+ first.setYear(minYear);
+ first.setMonth(minYearMonth);
+ first.setDay(minYearDay);
+
+ Calendar second = new Calendar();
+ second.setYear(maxYear);
+ second.setMonth(maxYearMonth);
+ second.setDay(maxYearDay);
+ return first.compareTo(second);
+ }
+
+ /**
+ * 为月视图初始化日历
+ *
+ * @param year year
+ * @param month month
+ * @param currentDate currentDate
+ * @param weekStar weekStar
+ * @return 为月视图初始化日历项
+ */
+ static List initCalendarForMonthView(int year, int month, Calendar currentDate, int weekStar) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+
+ date.set(year, month - 1, 1);
+
+ int mPreDiff = getMonthViewStartDiff(year, month, weekStar);//获取月视图其实偏移量
+
+ int monthDayCount = getMonthDaysCount(year, month);//获取月份真实天数
+
+ int preYear, preMonth;
+ int nextYear, nextMonth;
+
+ int size = 42;
+
+ List mItems = new ArrayList<>();
+
+ int preMonthDaysCount;
+ if (month == 1) {//如果是1月
+ preYear = year - 1;
+ preMonth = 12;
+ nextYear = year;
+ nextMonth = month + 1;
+ preMonthDaysCount = mPreDiff == 0 ? 0 : CalendarUtil.getMonthDaysCount(preYear, preMonth);
+ } else if (month == 12) {//如果是12月
+ preYear = year;
+ preMonth = month - 1;
+ nextYear = year + 1;
+ nextMonth = 1;
+ preMonthDaysCount = mPreDiff == 0 ? 0 : CalendarUtil.getMonthDaysCount(preYear, preMonth);
+ } else {//平常
+ preYear = year;
+ preMonth = month - 1;
+ nextYear = year;
+ nextMonth = month + 1;
+ preMonthDaysCount = mPreDiff == 0 ? 0 : CalendarUtil.getMonthDaysCount(preYear, preMonth);
+ }
+ int nextDay = 1;
+ for (int i = 0; i < size; i++) {
+ Calendar calendarDate = new Calendar();
+ if (i < mPreDiff) {
+ calendarDate.setYear(preYear);
+ calendarDate.setMonth(preMonth);
+ calendarDate.setDay(preMonthDaysCount - mPreDiff + i + 1);
+ } else if (i >= monthDayCount + mPreDiff) {
+ calendarDate.setYear(nextYear);
+ calendarDate.setMonth(nextMonth);
+ calendarDate.setDay(nextDay);
+ ++nextDay;
+ } else {
+ calendarDate.setYear(year);
+ calendarDate.setMonth(month);
+ calendarDate.setCurrentMonth(true);
+ calendarDate.setDay(i - mPreDiff + 1);
+ }
+ if (calendarDate.equals(currentDate)) {
+ calendarDate.setCurrentDay(true);
+ }
+ LunarCalendar.setupLunarCalendar(calendarDate);
+ mItems.add(calendarDate);
+ }
+ return mItems;
+ }
+
+ static List getWeekCalendars(Calendar calendar, CalendarViewDelegate mDelegate) {
+ long curTime = calendar.getTimeInMillis();
+
+ java.util.Calendar date = java.util.Calendar.getInstance();
+ date.set(calendar.getYear(),
+ calendar.getMonth() - 1,
+ calendar.getDay(), 12, 0);//
+ int week = date.get(java.util.Calendar.DAY_OF_WEEK);
+ int startDiff;
+ if (mDelegate.getWeekStart() == 1) {
+ startDiff = week - 1;
+ } else if (mDelegate.getWeekStart() == 2) {
+ startDiff = week == 1 ? 6 : week - mDelegate.getWeekStart();
+ } else {
+ startDiff = week == 7 ? 0 : week;
+ }
+
+ curTime -= startDiff * ONE_DAY;
+ java.util.Calendar minCalendar = java.util.Calendar.getInstance();
+ minCalendar.setTimeInMillis(curTime);
+ Calendar startCalendar = new Calendar();
+ startCalendar.setYear(minCalendar.get(java.util.Calendar.YEAR));
+ startCalendar.setMonth(minCalendar.get(java.util.Calendar.MONTH) + 1);
+ startCalendar.setDay(minCalendar.get(java.util.Calendar.DAY_OF_MONTH));
+ return initCalendarForWeekView(startCalendar, mDelegate, mDelegate.getWeekStart());
+ }
+
+ /**
+ * 生成周视图的7个item
+ *
+ * @param calendar 周视图的第一个日子calendar,所以往后推迟6天,生成周视图
+ * @param mDelegate mDelegate
+ * @param weekStart weekStart
+ * @return 生成周视图的7个item
+ */
+ @SuppressWarnings("unused")
+ static List initCalendarForWeekView(Calendar calendar, CalendarViewDelegate mDelegate, int weekStart) {
+
+ java.util.Calendar date = java.util.Calendar.getInstance();//当天时间
+ date.set(calendar.getYear(), calendar.getMonth() - 1, calendar.getDay(), 12, 0);
+ long curDateMills = date.getTimeInMillis();//生成选择的日期时间戳
+
+ //int weekEndDiff = getWeekViewEndDiff(calendar.getYear(), calendar.getMonth(), calendar.getDay(), weekStart);
+ //weekEndDiff 例如周起始为周日1,当前为2020-04-01,周三,则weekEndDiff为本周结束相差今天三天,weekEndDiff=3
+ int weekEndDiff = 6;
+ List mItems = new ArrayList<>();
+
+ date.setTimeInMillis(curDateMills);
+ Calendar selectCalendar = new Calendar();
+ selectCalendar.setYear(calendar.getYear());
+ selectCalendar.setMonth(calendar.getMonth());
+ selectCalendar.setDay(calendar.getDay());
+ if (selectCalendar.equals(mDelegate.getCurrentDay())) {
+ selectCalendar.setCurrentDay(true);
+ }
+ LunarCalendar.setupLunarCalendar(selectCalendar);
+ selectCalendar.setCurrentMonth(true);
+ mItems.add(selectCalendar);
+
+
+ for (int i = 1; i <= weekEndDiff; i++) {
+ date.setTimeInMillis(curDateMills + i * ONE_DAY);
+ Calendar calendarDate = new Calendar();
+ calendarDate.setYear(date.get(java.util.Calendar.YEAR));
+ calendarDate.setMonth(date.get(java.util.Calendar.MONTH) + 1);
+ calendarDate.setDay(date.get(java.util.Calendar.DAY_OF_MONTH));
+ if (calendarDate.equals(mDelegate.getCurrentDay())) {
+ calendarDate.setCurrentDay(true);
+ }
+ LunarCalendar.setupLunarCalendar(calendarDate);
+ calendarDate.setCurrentMonth(true);
+ mItems.add(calendarDate);
+ }
+ return mItems;
+ }
+
+ /**
+ * 单元测试通过
+ * 从选定的日期,获取周视图起始偏移量,用来生成周视图布局
+ *
+ * @param year year
+ * @param month month
+ * @param day day
+ * @param weekStart 周起始,1,2,7 日 一 六
+ * @return 获取周视图起始偏移量,用来生成周视图布局
+ */
+ private static int getWeekViewStartDiff(int year, int month, int day, int weekStart) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+ date.set(year, month - 1, day, 12, 0);//
+ int week = date.get(java.util.Calendar.DAY_OF_WEEK);
+ if (weekStart == 1) {
+ return week - 1;
+ }
+ if (weekStart == 2) {
+ return week == 1 ? 6 : week - weekStart;
+ }
+ return week == 7 ? 0 : week;
+ }
+
+
+ /**
+ * 单元测试通过
+ * 从选定的日期,获取周视图结束偏移量,用来生成周视图布局
+ * 为了兼容DST,DST时区可能出现时间偏移1-2小时,从而导致凌晨时候实际获得的日期往前或者往后推移了一天,
+ * 日历没有时和分的概念,因此把日期的时间强制在12:00,可以避免DST兼容问题
+ *
+ * @param year year
+ * @param month month
+ * @param day day
+ * @param weekStart 周起始,1,2,7 日 一 六
+ * @return 获取周视图结束偏移量,用来生成周视图布局
+ */
+ public static int getWeekViewEndDiff(int year, int month, int day, int weekStart) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+ date.set(year, month - 1, day, 12, 0);
+ int week = date.get(java.util.Calendar.DAY_OF_WEEK);
+ if (weekStart == 1) {
+ return 7 - week;
+ }
+ if (weekStart == 2) {
+ return week == 1 ? 0 : 7 - week + 1;
+ }
+ return week == 7 ? 6 : 7 - week - 1;
+ }
+
+
+ /**
+ * 从月视图切换获得第一天的日期
+ * Test Pass 它是100%正确的
+ *
+ * @param position position
+ * @param delegate position
+ * @return 从月视图切换获得第一天的日期
+ */
+ static Calendar getFirstCalendarFromMonthViewPager(int position, CalendarViewDelegate delegate) {
+ Calendar calendar = new Calendar();
+ calendar.setYear((position + delegate.getMinYearMonth() - 1) / 12 + delegate.getMinYear());
+ calendar.setMonth((position + delegate.getMinYearMonth() - 1) % 12 + 1);
+ if (delegate.getDefaultCalendarSelectDay() != CalendarViewDelegate.FIRST_DAY_OF_MONTH) {
+ int monthDays = getMonthDaysCount(calendar.getYear(), calendar.getMonth());
+ Calendar indexCalendar = delegate.mIndexCalendar;
+ calendar.setDay(indexCalendar == null || indexCalendar.getDay() == 0 ? 1 :
+ monthDays < indexCalendar.getDay() ? monthDays : indexCalendar.getDay());
+ } else {
+ calendar.setDay(1);
+ }
+ if (!isCalendarInRange(calendar, delegate)) {
+ if (isMinRangeEdge(calendar, delegate)) {
+ calendar = delegate.getMinRangeCalendar();
+ } else {
+ calendar = delegate.getMaxRangeCalendar();
+ }
+ }
+ calendar.setCurrentMonth(calendar.getYear() == delegate.getCurrentDay().getYear() &&
+ calendar.getMonth() == delegate.getCurrentDay().getMonth());
+ calendar.setCurrentDay(calendar.equals(delegate.getCurrentDay()));
+ LunarCalendar.setupLunarCalendar(calendar);
+ return calendar;
+ }
+
+
+ /**
+ * 根据传入的日期获取边界访问日期,要么最大,要么最小
+ *
+ * @param calendar calendar
+ * @param delegate delegate
+ * @return 获取边界访问日期
+ */
+ static Calendar getRangeEdgeCalendar(Calendar calendar, CalendarViewDelegate delegate) {
+ if (CalendarUtil.isCalendarInRange(delegate.getCurrentDay(), delegate)
+ && delegate.getDefaultCalendarSelectDay() != CalendarViewDelegate.LAST_MONTH_VIEW_SELECT_DAY_IGNORE_CURRENT) {
+ return delegate.createCurrentDate();
+ }
+ if (isCalendarInRange(calendar, delegate)) {
+ return calendar;
+ }
+ Calendar minRangeCalendar = delegate.getMinRangeCalendar();
+ if (minRangeCalendar.isSameMonth(calendar)) {
+ return delegate.getMinRangeCalendar();
+ }
+ return delegate.getMaxRangeCalendar();
+ }
+
+ /**
+ * 是否是最小访问边界了
+ *
+ * @param calendar calendar
+ * @return 是否是最小访问边界了
+ */
+ private static boolean isMinRangeEdge(Calendar calendar, CalendarViewDelegate delegate) {
+ java.util.Calendar c = java.util.Calendar.getInstance();
+ c.set(delegate.getMinYear(), delegate.getMinYearMonth() - 1, delegate.getMinYearDay(), 12, 0);
+ long minTime = c.getTimeInMillis();
+ c.set(calendar.getYear(), calendar.getMonth() - 1, calendar.getDay(), 12, 0);
+ long curTime = c.getTimeInMillis();
+ return curTime < minTime;
+ }
+
+ /**
+ * dp转px
+ *
+ * @param context context
+ * @param dpValue dp
+ * @return px
+ */
+ static int dipToPx(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/CalendarView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/CalendarView.java
new file mode 100644
index 00000000..aa7df945
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/CalendarView.java
@@ -0,0 +1,1916 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.animation.LinearInterpolator;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.viewpager.widget.ViewPager;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 日历布局
+ * 各个类使用包权限,避免不必要的public
+ */
+@SuppressWarnings({"unused"})
+public class CalendarView extends FrameLayout {
+
+ /**
+ * 抽取自定义属性
+ */
+ private final CalendarViewDelegate mDelegate;
+
+ /**
+ * 自定义自适应高度的ViewPager
+ */
+ private MonthViewPager mMonthPager;
+
+ /**
+ * 日历周视图
+ */
+ private WeekViewPager mWeekPager;
+
+ /**
+ * 星期栏的线
+ */
+ private View mWeekLine;
+
+ /**
+ * 月份快速选取
+ */
+ private YearViewPager mYearViewPager;
+
+ /**
+ * 星期栏
+ */
+ private WeekBar mWeekBar;
+
+ /**
+ * 日历外部收缩布局
+ */
+ CalendarLayout mParentLayout;
+
+
+ public CalendarView(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mDelegate = new CalendarViewDelegate(context, attrs);
+ init(context);
+ }
+
+ /**
+ * 初始化
+ *
+ * @param context context
+ */
+ private void init(Context context) {
+ LayoutInflater.from(context).inflate(R.layout.cv_layout_calendar_view, this, true);
+ FrameLayout frameContent = findViewById(R.id.frameContent);
+ this.mWeekPager = findViewById(R.id.vp_week);
+ this.mWeekPager.setup(mDelegate);
+
+ try {
+ Constructor constructor = mDelegate.getWeekBarClass().getConstructor(Context.class);
+ mWeekBar = (WeekBar) constructor.newInstance(getContext());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ frameContent.addView(mWeekBar, 2);
+ mWeekBar.setup(mDelegate);
+ mWeekBar.onWeekStartChange(mDelegate.getWeekStart());
+
+ this.mWeekLine = findViewById(R.id.line);
+ this.mWeekLine.setBackgroundColor(mDelegate.getWeekLineBackground());
+ LayoutParams lineParams = (LayoutParams) this.mWeekLine.getLayoutParams();
+ lineParams.setMargins(mDelegate.getWeekLineMargin(),
+ mDelegate.getWeekBarHeight(),
+ mDelegate.getWeekLineMargin(),
+ 0);
+ this.mWeekLine.setLayoutParams(lineParams);
+
+ this.mMonthPager = findViewById(R.id.vp_month);
+ this.mMonthPager.mWeekPager = mWeekPager;
+ this.mMonthPager.mWeekBar = mWeekBar;
+ LayoutParams params = (LayoutParams) this.mMonthPager.getLayoutParams();
+ params.setMargins(0, mDelegate.getWeekBarHeight() + CalendarUtil.dipToPx(context, 1), 0, 0);
+ mWeekPager.setLayoutParams(params);
+
+
+ mYearViewPager = findViewById(R.id.selectLayout);
+ mYearViewPager.setPadding(mDelegate.getYearViewPaddingLeft(), 0, mDelegate.getYearViewPaddingRight(), 0);
+ mYearViewPager.setBackgroundColor(mDelegate.getYearViewBackground());
+ mYearViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ if (mWeekPager.getVisibility() == VISIBLE) {
+ return;
+ }
+ if (mDelegate.mYearChangeListener != null) {
+ mDelegate.mYearChangeListener.onYearChange(position + mDelegate.getMinYear());
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ });
+
+ mDelegate.mInnerListener = new OnInnerDateSelectedListener() {
+ /**
+ * 月视图选择事件
+ * @param calendar calendar
+ * @param isClick 是否是点击
+ */
+ @Override
+ public void onMonthDateSelected(Calendar calendar, boolean isClick) {
+
+ if (calendar.getYear() == mDelegate.getCurrentDay().getYear() &&
+ calendar.getMonth() == mDelegate.getCurrentDay().getMonth()
+ && mMonthPager.getCurrentItem() != mDelegate.mCurrentMonthViewItem) {
+ return;
+ }
+ mDelegate.mIndexCalendar = calendar;
+ if (mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_DEFAULT || isClick) {
+ mDelegate.mSelectedCalendar = calendar;
+ }
+ mWeekPager.updateSelected(mDelegate.mIndexCalendar, false);
+ mMonthPager.updateSelected();
+ if (mWeekBar != null &&
+ (mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_DEFAULT || isClick)) {
+ mWeekBar.onDateSelected(calendar, mDelegate.getWeekStart(), isClick);
+ }
+ }
+
+ /**
+ * 周视图选择事件
+ * @param calendar calendar
+ * @param isClick 是否是点击
+ */
+ @Override
+ public void onWeekDateSelected(Calendar calendar, boolean isClick) {
+ mDelegate.mIndexCalendar = calendar;
+ if (mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_DEFAULT || isClick
+ || mDelegate.mIndexCalendar.equals(mDelegate.mSelectedCalendar)) {
+ mDelegate.mSelectedCalendar = calendar;
+ }
+ int y = calendar.getYear() - mDelegate.getMinYear();
+ int position = 12 * y + mDelegate.mIndexCalendar.getMonth() - mDelegate.getMinYearMonth();
+ mWeekPager.updateSingleSelect();
+ mMonthPager.setCurrentItem(position, false);
+ mMonthPager.updateSelected();
+ if (mWeekBar != null &&
+ (mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_DEFAULT
+ || isClick
+ || mDelegate.mIndexCalendar.equals(mDelegate.mSelectedCalendar))) {
+ mWeekBar.onDateSelected(calendar, mDelegate.getWeekStart(), isClick);
+ }
+ }
+ };
+
+
+ if (mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_DEFAULT) {
+ if (isInRange(mDelegate.getCurrentDay())) {
+ mDelegate.mSelectedCalendar = mDelegate.createCurrentDate();
+ } else {
+ mDelegate.mSelectedCalendar = mDelegate.getMinRangeCalendar();
+ }
+ } else {
+ mDelegate.mSelectedCalendar = new Calendar();
+ }
+
+ mDelegate.mIndexCalendar = mDelegate.mSelectedCalendar;
+
+ mWeekBar.onDateSelected(mDelegate.mSelectedCalendar, mDelegate.getWeekStart(), false);
+
+ mMonthPager.setup(mDelegate);
+ mMonthPager.setCurrentItem(mDelegate.mCurrentMonthViewItem);
+ mYearViewPager.setOnMonthSelectedListener(new YearRecyclerView.OnMonthSelectedListener() {
+ @Override
+ public void onMonthSelected(int year, int month) {
+ int position = 12 * (year - mDelegate.getMinYear()) + month - mDelegate.getMinYearMonth();
+ closeSelectLayout(position);
+ mDelegate.isShowYearSelectedLayout = false;
+ }
+ });
+ mYearViewPager.setup(mDelegate);
+ mWeekPager.updateSelected(mDelegate.createCurrentDate(), false);
+ }
+
+ /**
+ * 设置日期范围
+ *
+ * @param minYear 最小年份
+ * @param minYearMonth 最小年份对应月份
+ * @param minYearDay 最小年份对应天
+ * @param maxYear 最大月份
+ * @param maxYearMonth 最大月份对应月份
+ * @param maxYearDay 最大月份对应天
+ */
+ public void setRange(int minYear, int minYearMonth, int minYearDay,
+ int maxYear, int maxYearMonth, int maxYearDay) {
+ if (CalendarUtil.compareTo(minYear, minYearMonth, minYearDay,
+ maxYear, maxYearMonth, maxYearDay) > 0) {
+ return;
+ }
+ mDelegate.setRange(minYear, minYearMonth, minYearDay,
+ maxYear, maxYearMonth, maxYearDay);
+ mWeekPager.notifyDataSetChanged();
+ mYearViewPager.notifyDataSetChanged();
+ mMonthPager.notifyDataSetChanged();
+ if (!isInRange(mDelegate.mSelectedCalendar)) {
+ mDelegate.mSelectedCalendar = mDelegate.getMinRangeCalendar();
+ mDelegate.updateSelectCalendarScheme();
+ mDelegate.mIndexCalendar = mDelegate.mSelectedCalendar;
+ }
+ mWeekPager.updateRange();
+ mMonthPager.updateRange();
+ mYearViewPager.updateRange();
+ }
+
+ /**
+ * 获取当天
+ *
+ * @return 返回今天
+ */
+ public int getCurDay() {
+ return mDelegate.getCurrentDay().getDay();
+ }
+
+ /**
+ * 获取本月
+ *
+ * @return 返回本月
+ */
+ public int getCurMonth() {
+ return mDelegate.getCurrentDay().getMonth();
+ }
+
+ /**
+ * 获取本年
+ *
+ * @return 返回本年
+ */
+ public int getCurYear() {
+ return mDelegate.getCurrentDay().getYear();
+ }
+
+
+ /**
+ * 打开日历年月份快速选择
+ *
+ * @param year 年
+ */
+ public void showYearSelectLayout(final int year) {
+ showSelectLayout(year);
+ }
+
+ /**
+ * 打开日历年月份快速选择
+ * 请使用 showYearSelectLayout(final int year) 代替,这个没什么,越来越规范
+ *
+ * @param year 年
+ */
+ private void showSelectLayout(final int year) {
+ if (mParentLayout != null && mParentLayout.mContentView != null) {
+ if (!mParentLayout.isExpand()) {
+ mParentLayout.expand();
+ //return;
+ }
+ }
+ mWeekPager.setVisibility(GONE);
+ mDelegate.isShowYearSelectedLayout = true;
+ if (mParentLayout != null) {
+ mParentLayout.hideContentView();
+ }
+ mWeekBar.animate()
+ .translationY(-mWeekBar.getHeight())
+ .setInterpolator(new LinearInterpolator())
+ .setDuration(260)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mWeekBar.setVisibility(GONE);
+ mYearViewPager.setVisibility(VISIBLE);
+ mYearViewPager.scrollToYear(year, false);
+ if (mParentLayout != null && mParentLayout.mContentView != null) {
+ mParentLayout.expand();
+ }
+ }
+ });
+
+ mMonthPager.animate()
+ .scaleX(0)
+ .scaleY(0)
+ .setDuration(260)
+ .setInterpolator(new LinearInterpolator())
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (mDelegate.mYearViewChangeListener != null) {
+ mDelegate.mYearViewChangeListener.onYearViewChange(false);
+ }
+ }
+ });
+ }
+
+
+ /**
+ * 年月份选择视图是否打开
+ *
+ * @return true or false
+ */
+ public boolean isYearSelectLayoutVisible() {
+ return mYearViewPager.getVisibility() == VISIBLE;
+ }
+
+ /**
+ * 关闭年月视图选择布局
+ */
+ public void closeYearSelectLayout() {
+ if (mYearViewPager.getVisibility() == GONE) {
+ return;
+ }
+ int position = 12 * (mDelegate.mSelectedCalendar.getYear() - mDelegate.getMinYear()) +
+ mDelegate.mSelectedCalendar.getMonth() - mDelegate.getMinYearMonth();
+ closeSelectLayout(position);
+ mDelegate.isShowYearSelectedLayout = false;
+ }
+
+ /**
+ * 关闭日历布局,同时会滚动到指定的位置
+ *
+ * @param position 某一年
+ */
+ private void closeSelectLayout(final int position) {
+ mYearViewPager.setVisibility(GONE);
+ mWeekBar.setVisibility(VISIBLE);
+ if (position == mMonthPager.getCurrentItem()) {
+ if (mDelegate.mCalendarSelectListener != null &&
+ mDelegate.getSelectMode() != CalendarViewDelegate.SELECT_MODE_SINGLE) {
+ mDelegate.mCalendarSelectListener.onCalendarSelect(mDelegate.mSelectedCalendar, false);
+ }
+ } else {
+ mMonthPager.setCurrentItem(position, false);
+ }
+ mWeekBar.animate()
+ .translationY(0)
+ .setInterpolator(new LinearInterpolator())
+ .setDuration(280)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mWeekBar.setVisibility(VISIBLE);
+ }
+ });
+ mMonthPager.animate()
+ .scaleX(1)
+ .scaleY(1)
+ .setDuration(180)
+ .setInterpolator(new LinearInterpolator())
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (mDelegate.mYearViewChangeListener != null) {
+ mDelegate.mYearViewChangeListener.onYearViewChange(true);
+ }
+ if (mParentLayout != null) {
+ mParentLayout.showContentView();
+ if (mParentLayout.isExpand()) {
+ mMonthPager.setVisibility(VISIBLE);
+ } else {
+ mWeekPager.setVisibility(VISIBLE);
+ mParentLayout.shrink();
+ }
+ } else {
+ mMonthPager.setVisibility(VISIBLE);
+ }
+ mMonthPager.clearAnimation();
+ }
+ });
+ }
+
+ /**
+ * 滚动到当前
+ */
+ public void scrollToCurrent() {
+ scrollToCurrent(false);
+ }
+
+ /**
+ * 滚动到当前
+ *
+ * @param smoothScroll smoothScroll
+ */
+ public void scrollToCurrent(boolean smoothScroll) {
+ if (!isInRange(mDelegate.getCurrentDay())) {
+ return;
+ }
+ Calendar calendar = mDelegate.createCurrentDate();
+ if (mDelegate.mCalendarInterceptListener != null &&
+ mDelegate.mCalendarInterceptListener.onCalendarIntercept(calendar)) {
+ mDelegate.mCalendarInterceptListener.onCalendarInterceptClick(calendar, false);
+ return;
+ }
+ mDelegate.mSelectedCalendar = mDelegate.createCurrentDate();
+ mDelegate.mIndexCalendar = mDelegate.mSelectedCalendar;
+ mDelegate.updateSelectCalendarScheme();
+ mWeekBar.onDateSelected(mDelegate.mSelectedCalendar, mDelegate.getWeekStart(), false);
+ if (mMonthPager.getVisibility() == VISIBLE) {
+ mMonthPager.scrollToCurrent(smoothScroll);
+ mWeekPager.updateSelected(mDelegate.mIndexCalendar, false);
+ } else {
+ mWeekPager.scrollToCurrent(smoothScroll);
+ }
+ mYearViewPager.scrollToYear(mDelegate.getCurrentDay().getYear(), smoothScroll);
+ }
+
+
+ /**
+ * 滚动到下一个月
+ */
+ public void scrollToNext() {
+ scrollToNext(false);
+ }
+
+ /**
+ * 滚动到下一个月
+ *
+ * @param smoothScroll smoothScroll
+ */
+ public void scrollToNext(boolean smoothScroll) {
+ if (isYearSelectLayoutVisible()) {
+ mYearViewPager.setCurrentItem(mYearViewPager.getCurrentItem() + 1, smoothScroll);
+ } else if (mWeekPager.getVisibility() == VISIBLE) {
+ mWeekPager.setCurrentItem(mWeekPager.getCurrentItem() + 1, smoothScroll);
+ } else {
+ mMonthPager.setCurrentItem(mMonthPager.getCurrentItem() + 1, smoothScroll);
+ }
+
+ }
+
+ /**
+ * 滚动到上一个月
+ */
+ public void scrollToPre() {
+ scrollToPre(false);
+ }
+
+ /**
+ * 滚动到上一个月
+ *
+ * @param smoothScroll smoothScroll
+ */
+ public void scrollToPre(boolean smoothScroll) {
+ if (isYearSelectLayoutVisible()) {
+ mYearViewPager.setCurrentItem(mYearViewPager.getCurrentItem() - 1, smoothScroll);
+ } else if (mWeekPager.getVisibility() == VISIBLE) {
+ mWeekPager.setCurrentItem(mWeekPager.getCurrentItem() - 1, smoothScroll);
+ } else {
+ mMonthPager.setCurrentItem(mMonthPager.getCurrentItem() - 1, smoothScroll);
+ }
+ }
+
+ /**
+ * 滚动到选择的日历
+ */
+ public void scrollToSelectCalendar() {
+ if (!mDelegate.mSelectedCalendar.isAvailable()) {
+ return;
+ }
+ scrollToCalendar(mDelegate.mSelectedCalendar.getYear(),
+ mDelegate.mSelectedCalendar.getMonth(),
+ mDelegate.mSelectedCalendar.getDay(),
+ false,
+ true);
+ }
+
+ /**
+ * 滚动到指定日期
+ *
+ * @param year year
+ * @param month month
+ * @param day day
+ */
+ public void scrollToCalendar(int year, int month, int day) {
+ scrollToCalendar(year, month, day, false, true);
+ }
+
+ /**
+ * 滚动到指定日期
+ *
+ * @param year year
+ * @param month month
+ * @param day day
+ * @param smoothScroll smoothScroll
+ */
+ public void scrollToCalendar(int year, int month, int day, boolean smoothScroll) {
+ scrollToCalendar(year, month, day, smoothScroll, true);
+ }
+
+ /**
+ * 滚动到指定日期
+ *
+ * @param year year
+ * @param month month
+ * @param day day
+ * @param smoothScroll smoothScroll
+ * @param invokeListener 调用日期事件
+ */
+ public void scrollToCalendar(int year, int month, int day, boolean smoothScroll, boolean invokeListener) {
+
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ if (!calendar.isAvailable()) {
+ return;
+ }
+ if (!isInRange(calendar)) {
+ return;
+ }
+ if (mDelegate.mCalendarInterceptListener != null &&
+ mDelegate.mCalendarInterceptListener.onCalendarIntercept(calendar)) {
+ mDelegate.mCalendarInterceptListener.onCalendarInterceptClick(calendar, false);
+ return;
+ }
+
+ if (mWeekPager.getVisibility() == VISIBLE) {
+ mWeekPager.scrollToCalendar(year, month, day, smoothScroll, invokeListener);
+ } else {
+ mMonthPager.scrollToCalendar(year, month, day, smoothScroll, invokeListener);
+ }
+ }
+
+ /**
+ * 滚动到某一年
+ *
+ * @param year 快速滚动的年份
+ */
+ public void scrollToYear(int year) {
+ scrollToYear(year, false);
+ }
+
+ /**
+ * 滚动到某一年
+ *
+ * @param year 快速滚动的年份
+ * @param smoothScroll smoothScroll
+ */
+ public void scrollToYear(int year, boolean smoothScroll) {
+ if (mYearViewPager.getVisibility() != VISIBLE) {
+ return;
+ }
+ mYearViewPager.scrollToYear(year, smoothScroll);
+ }
+
+ /**
+ * 设置月视图是否可滚动
+ *
+ * @param monthViewScrollable 设置月视图是否可滚动
+ */
+ public final void setMonthViewScrollable(boolean monthViewScrollable) {
+ mDelegate.setMonthViewScrollable(monthViewScrollable);
+ }
+
+
+ /**
+ * 设置周视图是否可滚动
+ *
+ * @param weekViewScrollable 设置周视图是否可滚动
+ */
+ public final void setWeekViewScrollable(boolean weekViewScrollable) {
+ mDelegate.setWeekViewScrollable(weekViewScrollable);
+ }
+
+ /**
+ * 设置年视图是否可滚动
+ *
+ * @param yearViewScrollable 设置年视图是否可滚动
+ */
+ public final void setYearViewScrollable(boolean yearViewScrollable) {
+ mDelegate.setYearViewScrollable(yearViewScrollable);
+ }
+
+
+ public final void setDefaultMonthViewSelectDay() {
+ mDelegate.setDefaultCalendarSelectDay(CalendarViewDelegate.FIRST_DAY_OF_MONTH);
+ }
+
+ public final void setLastMonthViewSelectDay() {
+ mDelegate.setDefaultCalendarSelectDay(CalendarViewDelegate.LAST_MONTH_VIEW_SELECT_DAY);
+ }
+
+ public final void setLastMonthViewSelectDayIgnoreCurrent() {
+ mDelegate.setDefaultCalendarSelectDay(CalendarViewDelegate.LAST_MONTH_VIEW_SELECT_DAY_IGNORE_CURRENT);
+ }
+
+ /**
+ * 清除选择范围
+ */
+ public final void clearSelectRange() {
+ mDelegate.clearSelectRange();
+ mMonthPager.clearSelectRange();
+ mWeekPager.clearSelectRange();
+ }
+
+ /**
+ * 清除单选
+ */
+ public final void clearSingleSelect() {
+ mDelegate.mSelectedCalendar = new Calendar();
+ mMonthPager.clearSingleSelect();
+ mWeekPager.clearSingleSelect();
+ }
+
+ /**
+ * 清除多选
+ */
+ public final void clearMultiSelect() {
+ mDelegate.mSelectedCalendars.clear();
+ mMonthPager.clearMultiSelect();
+ mWeekPager.clearMultiSelect();
+ }
+
+ /**
+ * 添加选择
+ *
+ * @param calendars calendars
+ */
+ public final void putMultiSelect(Calendar... calendars) {
+ if (calendars == null || calendars.length == 0) {
+ return;
+ }
+ for (Calendar calendar : calendars) {
+ if (calendar == null || mDelegate.mSelectedCalendars.containsKey(calendar.toString())) {
+ continue;
+ }
+ mDelegate.mSelectedCalendars.put(calendar.toString(), calendar);
+ }
+ update();
+ }
+
+ /**
+ * 清楚一些多选日期
+ *
+ * @param calendars calendars
+ */
+ @SuppressWarnings("RedundantCollectionOperation")
+ public final void removeMultiSelect(Calendar... calendars) {
+ if (calendars == null || calendars.length == 0) {
+ return;
+ }
+ for (Calendar calendar : calendars) {
+ if (calendar == null) {
+ continue;
+ }
+ if (mDelegate.mSelectedCalendars.containsKey(calendar.toString())) {
+ mDelegate.mSelectedCalendars.remove(calendar.toString());
+ }
+ }
+ update();
+ }
+
+
+ public final List getMultiSelectCalendars() {
+ List calendars = new ArrayList<>();
+ if (mDelegate.mSelectedCalendars.size() == 0) {
+ return calendars;
+ }
+ calendars.addAll(mDelegate.mSelectedCalendars.values());
+ Collections.sort(calendars);
+ return calendars;
+ }
+
+ /**
+ * 获取选中范围
+ *
+ * @return return
+ */
+ public final List getSelectCalendarRange() {
+ return mDelegate.getSelectCalendarRange();
+ }
+
+ /**
+ * 设置月视图项高度
+ *
+ * @param calendarItemHeight MonthView item height
+ */
+ public final void setCalendarItemHeight(int calendarItemHeight) {
+ if (mDelegate.getCalendarItemHeight() == calendarItemHeight) {
+ return;
+ }
+ mDelegate.setCalendarItemHeight(calendarItemHeight);
+ mMonthPager.updateItemHeight();
+ mWeekPager.updateItemHeight();
+ if (mParentLayout == null) {
+ return;
+ }
+ mParentLayout.updateCalendarItemHeight();
+ }
+
+
+ /**
+ * 设置月视图
+ *
+ * @param cls MonthView.class
+ */
+ public final void setMonthView(Class> cls) {
+ if (cls == null) {
+ return;
+ }
+ if (mDelegate.getMonthViewClass().equals(cls)) {
+ return;
+ }
+ mDelegate.setMonthViewClass(cls);
+ mMonthPager.updateMonthViewClass();
+ }
+
+ /**
+ * 设置周视图
+ *
+ * @param cls WeekView.class
+ */
+ public final void setWeekView(Class> cls) {
+ if (cls == null) {
+ return;
+ }
+ if (mDelegate.getWeekBarClass().equals(cls)) {
+ return;
+ }
+ mDelegate.setWeekViewClass(cls);
+ mWeekPager.updateWeekViewClass();
+ }
+
+ /**
+ * 设置周栏视图
+ *
+ * @param cls WeekBar.class
+ */
+ public final void setWeekBar(Class> cls) {
+ if (cls == null) {
+ return;
+ }
+ if (mDelegate.getWeekBarClass().equals(cls)) {
+ return;
+ }
+ mDelegate.setWeekBarClass(cls);
+ FrameLayout frameContent = findViewById(R.id.frameContent);
+ frameContent.removeView(mWeekBar);
+
+ try {
+ Constructor constructor = cls.getConstructor(Context.class);
+ mWeekBar = (WeekBar) constructor.newInstance(getContext());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ frameContent.addView(mWeekBar, 2);
+ mWeekBar.setup(mDelegate);
+ mWeekBar.onWeekStartChange(mDelegate.getWeekStart());
+ this.mMonthPager.mWeekBar = mWeekBar;
+ mWeekBar.onDateSelected(mDelegate.mSelectedCalendar, mDelegate.getWeekStart(), false);
+ }
+
+
+ /**
+ * 添加日期拦截事件
+ * 使用此方法,只能基于select_mode = single_mode
+ * 否则的话,如果标记全部日期为不可点击,那是没有意义的,
+ * 框架本身也不可能在滑动的过程中全部去判断每个日期的可点击性
+ *
+ * @param listener listener
+ */
+ public final void setOnCalendarInterceptListener(OnCalendarInterceptListener listener) {
+ if (listener == null) {
+ mDelegate.mCalendarInterceptListener = null;
+ }
+ if (listener == null || mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_DEFAULT) {
+ return;
+ }
+ mDelegate.mCalendarInterceptListener = listener;
+ if (!listener.onCalendarIntercept(mDelegate.mSelectedCalendar)) {
+ return;
+ }
+ mDelegate.mSelectedCalendar = new Calendar();
+ }
+
+ /**
+ * 点击视图Padding位置的事件
+ *
+ * @param listener listener
+ */
+ public final void setOnClickCalendarPaddingListener(OnClickCalendarPaddingListener listener) {
+ if (listener == null) {
+ mDelegate.mClickCalendarPaddingListener = null;
+ }
+ if (listener == null) {
+ return;
+ }
+ mDelegate.mClickCalendarPaddingListener = listener;
+ }
+
+ /**
+ * 年份改变事件
+ *
+ * @param listener listener
+ */
+ public void setOnYearChangeListener(OnYearChangeListener listener) {
+ this.mDelegate.mYearChangeListener = listener;
+ }
+
+ /**
+ * 月份改变事件
+ *
+ * @param listener listener
+ */
+ public void setOnMonthChangeListener(OnMonthChangeListener listener) {
+ this.mDelegate.mMonthChangeListener = listener;
+ }
+
+
+ /**
+ * 周视图切换监听
+ *
+ * @param listener listener
+ */
+ public void setOnWeekChangeListener(OnWeekChangeListener listener) {
+ this.mDelegate.mWeekChangeListener = listener;
+ }
+
+ /**
+ * 日期选择事件
+ *
+ * @param listener listener
+ */
+ public void setOnCalendarSelectListener(OnCalendarSelectListener listener) {
+ this.mDelegate.mCalendarSelectListener = listener;
+ if (mDelegate.mCalendarSelectListener == null) {
+ return;
+ }
+ if (mDelegate.getSelectMode() != CalendarViewDelegate.SELECT_MODE_DEFAULT) {
+ return;
+ }
+ if (!isInRange(mDelegate.mSelectedCalendar)) {
+ return;
+ }
+ mDelegate.updateSelectCalendarScheme();
+ }
+
+
+ /**
+ * 日期选择事件
+ *
+ * @param listener listener
+ */
+ public final void setOnCalendarRangeSelectListener(OnCalendarRangeSelectListener listener) {
+ this.mDelegate.mCalendarRangeSelectListener = listener;
+ }
+
+ /**
+ * 日期多选事件
+ *
+ * @param listener listener
+ */
+ public final void setOnCalendarMultiSelectListener(OnCalendarMultiSelectListener listener) {
+ this.mDelegate.mCalendarMultiSelectListener = listener;
+ }
+
+ /**
+ * 设置最小范围和最大访问,default:minRange = -1,maxRange = -1 没有限制
+ *
+ * @param minRange minRange
+ * @param maxRange maxRange
+ */
+ public final void setSelectRange(int minRange, int maxRange) {
+ if (minRange > maxRange) {
+ return;
+ }
+ mDelegate.setSelectRange(minRange, maxRange);
+ }
+
+
+ public final void setSelectStartCalendar(int startYear, int startMonth, int startDay) {
+ if (mDelegate.getSelectMode() != CalendarViewDelegate.SELECT_MODE_RANGE) {
+ return;
+ }
+ Calendar startCalendar = new Calendar();
+ startCalendar.setYear(startYear);
+ startCalendar.setMonth(startMonth);
+ startCalendar.setDay(startDay);
+ setSelectStartCalendar(startCalendar);
+ }
+
+ public final void setSelectStartCalendar(Calendar startCalendar) {
+ if (mDelegate.getSelectMode() != CalendarViewDelegate.SELECT_MODE_RANGE) {
+ return;
+ }
+ if (startCalendar == null) {
+ return;
+ }
+ if (!isInRange(startCalendar)) {
+ if (mDelegate.mCalendarRangeSelectListener != null) {
+ mDelegate.mCalendarRangeSelectListener.onSelectOutOfRange(startCalendar, true);
+ }
+ return;
+ }
+ if (onCalendarIntercept(startCalendar)) {
+ if (mDelegate.mCalendarInterceptListener != null) {
+ mDelegate.mCalendarInterceptListener.onCalendarInterceptClick(startCalendar, false);
+ }
+ return;
+ }
+ mDelegate.mSelectedEndRangeCalendar = null;
+ mDelegate.mSelectedStartRangeCalendar = startCalendar;
+ scrollToCalendar(startCalendar.getYear(), startCalendar.getMonth(), startCalendar.getDay());
+ }
+
+ public final void setSelectEndCalendar(int endYear, int endMonth, int endDay) {
+ if (mDelegate.getSelectMode() != CalendarViewDelegate.SELECT_MODE_RANGE) {
+ return;
+ }
+ if (mDelegate.mSelectedStartRangeCalendar == null) {
+ return;
+ }
+ Calendar endCalendar = new Calendar();
+ endCalendar.setYear(endYear);
+ endCalendar.setMonth(endMonth);
+ endCalendar.setDay(endDay);
+ setSelectEndCalendar(endCalendar);
+ }
+
+ public final void setSelectEndCalendar(Calendar endCalendar) {
+ if (mDelegate.getSelectMode() != CalendarViewDelegate.SELECT_MODE_RANGE) {
+ return;
+ }
+ if (mDelegate.mSelectedStartRangeCalendar == null) {
+ return;
+ }
+ setSelectCalendarRange(mDelegate.mSelectedStartRangeCalendar, endCalendar);
+ }
+
+ /**
+ * 直接指定选择范围,set select calendar range
+ *
+ * @param startYear startYear
+ * @param startMonth startMonth
+ * @param startDay startDay
+ * @param endYear endYear
+ * @param endMonth endMonth
+ * @param endDay endDay
+ */
+ public final void setSelectCalendarRange(int startYear, int startMonth, int startDay,
+ int endYear, int endMonth, int endDay) {
+ if (mDelegate.getSelectMode() != CalendarViewDelegate.SELECT_MODE_RANGE) {
+ return;
+ }
+ Calendar startCalendar = new Calendar();
+ startCalendar.setYear(startYear);
+ startCalendar.setMonth(startMonth);
+ startCalendar.setDay(startDay);
+
+ Calendar endCalendar = new Calendar();
+ endCalendar.setYear(endYear);
+ endCalendar.setMonth(endMonth);
+ endCalendar.setDay(endDay);
+ setSelectCalendarRange(startCalendar, endCalendar);
+ }
+
+ /**
+ * 设置选择日期范围
+ *
+ * @param startCalendar startCalendar
+ * @param endCalendar endCalendar
+ */
+ public final void setSelectCalendarRange(Calendar startCalendar, Calendar endCalendar) {
+ if (mDelegate.getSelectMode() != CalendarViewDelegate.SELECT_MODE_RANGE) {
+ return;
+ }
+ if (startCalendar == null || endCalendar == null) {
+ return;
+ }
+ if (onCalendarIntercept(startCalendar)) {
+ if (mDelegate.mCalendarInterceptListener != null) {
+ mDelegate.mCalendarInterceptListener.onCalendarInterceptClick(startCalendar, false);
+ }
+ return;
+ }
+ if (onCalendarIntercept(endCalendar)) {
+ if (mDelegate.mCalendarInterceptListener != null) {
+ mDelegate.mCalendarInterceptListener.onCalendarInterceptClick(endCalendar, false);
+ }
+ return;
+ }
+ int minDiffer = endCalendar.differ(startCalendar);
+ if (minDiffer < 0) {
+ return;
+ }
+ if (!isInRange(startCalendar) || !isInRange(endCalendar)) {
+ return;
+ }
+
+
+ //优先判断各种直接return的情况,减少代码深度
+ if (mDelegate.getMinSelectRange() != -1 && mDelegate.getMinSelectRange() > minDiffer + 1) {
+ if (mDelegate.mCalendarRangeSelectListener != null) {
+ mDelegate.mCalendarRangeSelectListener.onSelectOutOfRange(endCalendar, true);
+ }
+ return;
+ } else if (mDelegate.getMaxSelectRange() != -1 && mDelegate.getMaxSelectRange() <
+ minDiffer + 1) {
+ if (mDelegate.mCalendarRangeSelectListener != null) {
+ mDelegate.mCalendarRangeSelectListener.onSelectOutOfRange(endCalendar, false);
+ }
+ return;
+ }
+ if (mDelegate.getMinSelectRange() == -1 && minDiffer == 0) {
+ mDelegate.mSelectedStartRangeCalendar = startCalendar;
+ mDelegate.mSelectedEndRangeCalendar = null;
+ if (mDelegate.mCalendarRangeSelectListener != null) {
+ mDelegate.mCalendarRangeSelectListener.onCalendarRangeSelect(startCalendar, false);
+ }
+ scrollToCalendar(startCalendar.getYear(), startCalendar.getMonth(), startCalendar.getDay());
+ return;
+ }
+
+ mDelegate.mSelectedStartRangeCalendar = startCalendar;
+ mDelegate.mSelectedEndRangeCalendar = endCalendar;
+ if (mDelegate.mCalendarRangeSelectListener != null) {
+ mDelegate.mCalendarRangeSelectListener.onCalendarRangeSelect(startCalendar, false);
+ mDelegate.mCalendarRangeSelectListener.onCalendarRangeSelect(endCalendar, true);
+ }
+ scrollToCalendar(startCalendar.getYear(), startCalendar.getMonth(), startCalendar.getDay());
+ }
+
+ /**
+ * 是否拦截日期,此设置续设置mCalendarInterceptListener
+ *
+ * @param calendar calendar
+ * @return 是否拦截日期
+ */
+ protected final boolean onCalendarIntercept(Calendar calendar) {
+ return mDelegate.mCalendarInterceptListener != null &&
+ mDelegate.mCalendarInterceptListener.onCalendarIntercept(calendar);
+ }
+
+
+ /**
+ * 获得最大多选数量
+ *
+ * @return 获得最大多选数量
+ */
+ public final int getMaxMultiSelectSize() {
+ return mDelegate.getMaxMultiSelectSize();
+ }
+
+ /**
+ * 设置最大多选数量
+ *
+ * @param maxMultiSelectSize 最大多选数量
+ */
+ public final void setMaxMultiSelectSize(int maxMultiSelectSize) {
+ mDelegate.setMaxMultiSelectSize(maxMultiSelectSize);
+ }
+
+ /**
+ * 最小选择范围
+ *
+ * @return 最小选择范围
+ */
+ public final int getMinSelectRange() {
+ return mDelegate.getMinSelectRange();
+ }
+
+ /**
+ * 最大选择范围
+ *
+ * @return 最大选择范围
+ */
+ public final int getMaxSelectRange() {
+ return mDelegate.getMaxSelectRange();
+ }
+
+ /**
+ * 日期长按事件
+ *
+ * @param listener listener
+ */
+ public void setOnCalendarLongClickListener(OnCalendarLongClickListener listener) {
+ this.mDelegate.mCalendarLongClickListener = listener;
+ }
+
+ /**
+ * 日期长按事件
+ *
+ * @param preventLongPressedSelect 防止长按选择日期
+ * @param listener listener
+ */
+ public void setOnCalendarLongClickListener(OnCalendarLongClickListener listener, boolean preventLongPressedSelect) {
+ this.mDelegate.mCalendarLongClickListener = listener;
+ this.mDelegate.setPreventLongPressedSelected(preventLongPressedSelect);
+ }
+
+ /**
+ * 视图改变事件
+ *
+ * @param listener listener
+ */
+ public void setOnViewChangeListener(OnViewChangeListener listener) {
+ this.mDelegate.mViewChangeListener = listener;
+ }
+
+
+ public void setOnYearViewChangeListener(OnYearViewChangeListener listener) {
+ this.mDelegate.mYearViewChangeListener = listener;
+ }
+
+ /**
+ * 保持状态
+ *
+ * @return 状态
+ */
+ @Nullable
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ if (mDelegate == null) {
+ return super.onSaveInstanceState();
+ }
+ Bundle bundle = new Bundle();
+ Parcelable parcelable = super.onSaveInstanceState();
+ bundle.putParcelable("super", parcelable);
+ bundle.putSerializable("selected_calendar", mDelegate.mSelectedCalendar);
+ bundle.putSerializable("index_calendar", mDelegate.mIndexCalendar);
+ return bundle;
+ }
+
+ /**
+ * 恢复状态
+ *
+ * @param state 状态
+ */
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ Bundle bundle = (Bundle) state;
+ Parcelable superData = bundle.getParcelable("super");
+ mDelegate.mSelectedCalendar = (Calendar) bundle.getSerializable("selected_calendar");
+ mDelegate.mIndexCalendar = (Calendar) bundle.getSerializable("index_calendar");
+ if (mDelegate.mCalendarSelectListener != null) {
+ mDelegate.mCalendarSelectListener.onCalendarSelect(mDelegate.mSelectedCalendar, false);
+ }
+ if (mDelegate.mIndexCalendar != null) {
+ scrollToCalendar(mDelegate.mIndexCalendar.getYear(),
+ mDelegate.mIndexCalendar.getMonth(),
+ mDelegate.mIndexCalendar.getDay());
+ }
+ update();
+ super.onRestoreInstanceState(superData);
+ }
+
+
+ /**
+ * 初始化时初始化日历卡默认选择位置
+ */
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (getParent() != null && getParent() instanceof CalendarLayout) {
+ mParentLayout = (CalendarLayout) getParent();
+ mMonthPager.mParentLayout = mParentLayout;
+ mWeekPager.mParentLayout = mParentLayout;
+ mParentLayout.mWeekBar = mWeekBar;
+ mParentLayout.setup(mDelegate);
+ mParentLayout.initStatus();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+ if (mDelegate == null ||
+ !mDelegate.isFullScreenCalendar()) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
+ setCalendarItemHeight((height -
+ mDelegate.getWeekBarHeight()) / 6);
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ /**
+ * 标记哪些日期有事件
+ *
+ * @param mSchemeDates mSchemeDatesMap 通过自己的需求转换即可
+ */
+ public final void setSchemeDate(Map mSchemeDates) {
+ this.mDelegate.mSchemeDatesMap = mSchemeDates;
+ this.mDelegate.updateSelectCalendarScheme();
+ this.mYearViewPager.update();
+ this.mMonthPager.updateScheme();
+ this.mWeekPager.updateScheme();
+ }
+
+ /**
+ * 清空日期标记
+ */
+ public final void clearSchemeDate() {
+ this.mDelegate.mSchemeDatesMap = null;
+ this.mDelegate.clearSelectedScheme();
+ mYearViewPager.update();
+ mMonthPager.updateScheme();
+ mWeekPager.updateScheme();
+ }
+
+ /**
+ * 添加事物标记
+ *
+ * @param calendar calendar
+ */
+ public final void addSchemeDate(Calendar calendar) {
+ if (calendar == null || !calendar.isAvailable()) {
+ return;
+ }
+ if (mDelegate.mSchemeDatesMap == null) {
+ mDelegate.mSchemeDatesMap = new HashMap<>();
+ }
+ mDelegate.mSchemeDatesMap.remove(calendar.toString());
+ mDelegate.mSchemeDatesMap.put(calendar.toString(), calendar);
+ this.mDelegate.updateSelectCalendarScheme();
+ this.mYearViewPager.update();
+ this.mMonthPager.updateScheme();
+ this.mWeekPager.updateScheme();
+ }
+
+ /**
+ * 添加事物标记
+ *
+ * @param mSchemeDates mSchemeDates
+ */
+ public final void addSchemeDate(Map mSchemeDates) {
+ if (this.mDelegate == null || mSchemeDates == null || mSchemeDates.size() == 0) {
+ return;
+ }
+ if (this.mDelegate.mSchemeDatesMap == null) {
+ this.mDelegate.mSchemeDatesMap = new HashMap<>();
+ }
+ this.mDelegate.addSchemes(mSchemeDates);
+ this.mDelegate.updateSelectCalendarScheme();
+ this.mYearViewPager.update();
+ this.mMonthPager.updateScheme();
+ this.mWeekPager.updateScheme();
+ }
+
+ /**
+ * 移除某天的标记
+ * 这个API是安全的
+ *
+ * @param calendar calendar
+ */
+ public final void removeSchemeDate(Calendar calendar) {
+ if (calendar == null) {
+ return;
+ }
+ if (mDelegate.mSchemeDatesMap == null || mDelegate.mSchemeDatesMap.size() == 0) {
+ return;
+ }
+ mDelegate.mSchemeDatesMap.remove(calendar.toString());
+ if (mDelegate.mSelectedCalendar.equals(calendar)) {
+ mDelegate.clearSelectedScheme();
+ }
+
+ mYearViewPager.update();
+ mMonthPager.updateScheme();
+ mWeekPager.updateScheme();
+ }
+
+ /**
+ * 设置背景色
+ *
+ * @param yearViewBackground 年份卡片的背景色
+ * @param weekBackground 星期栏背景色
+ * @param lineBg 线的颜色
+ */
+ public void setBackground(int yearViewBackground, int weekBackground, int lineBg) {
+ mWeekBar.setBackgroundColor(weekBackground);
+ mYearViewPager.setBackgroundColor(yearViewBackground);
+ mWeekLine.setBackgroundColor(lineBg);
+ }
+
+
+ /**
+ * 设置文本颜色
+ *
+ * @param currentDayTextColor 今天字体颜色
+ * @param curMonthTextColor 当前月份字体颜色
+ * @param otherMonthColor 其它月份字体颜色
+ * @param curMonthLunarTextColor 当前月份农历字体颜色
+ * @param otherMonthLunarTextColor 其它农历字体颜色
+ */
+ public void setTextColor(
+ int currentDayTextColor,
+ int curMonthTextColor,
+ int otherMonthColor,
+ int curMonthLunarTextColor,
+ int otherMonthLunarTextColor) {
+ if (mDelegate == null || mMonthPager == null || mWeekPager == null) {
+ return;
+ }
+ mDelegate.setTextColor(currentDayTextColor, curMonthTextColor,
+ otherMonthColor, curMonthLunarTextColor, otherMonthLunarTextColor);
+ mMonthPager.updateStyle();
+ mWeekPager.updateStyle();
+ }
+
+ /**
+ * 设置选择的效果
+ *
+ * @param selectedThemeColor 选中的标记颜色
+ * @param selectedTextColor 选中的字体颜色
+ * @param selectedLunarTextColor 选中的农历字体颜色
+ */
+ public void setSelectedColor(int selectedThemeColor, int selectedTextColor, int selectedLunarTextColor) {
+ if (mDelegate == null || mMonthPager == null || mWeekPager == null) {
+ return;
+ }
+ mDelegate.setSelectColor(selectedThemeColor, selectedTextColor, selectedLunarTextColor);
+ mMonthPager.updateStyle();
+ mWeekPager.updateStyle();
+ }
+
+ /**
+ * 定制颜色
+ *
+ * @param selectedThemeColor 选中的标记颜色
+ * @param schemeColor 标记背景色
+ */
+ public void setThemeColor(int selectedThemeColor, int schemeColor) {
+ if (mDelegate == null || mMonthPager == null || mWeekPager == null) {
+ return;
+ }
+ mDelegate.setThemeColor(selectedThemeColor, schemeColor);
+ mMonthPager.updateStyle();
+ mWeekPager.updateStyle();
+ }
+
+ /**
+ * 设置标记的色
+ *
+ * @param schemeLunarTextColor 标记农历颜色
+ * @param schemeColor 标记背景色
+ * @param schemeTextColor 标记字体颜色
+ */
+ public void setSchemeColor(int schemeColor, int schemeTextColor, int schemeLunarTextColor) {
+ if (mDelegate == null || mMonthPager == null || mWeekPager == null) {
+ return;
+ }
+ mDelegate.setSchemeColor(schemeColor, schemeTextColor, schemeLunarTextColor);
+ mMonthPager.updateStyle();
+ mWeekPager.updateStyle();
+ }
+
+ /**
+ * 设置年视图的颜色
+ *
+ * @param yearViewMonthTextColor 年视图月份颜色
+ * @param yearViewDayTextColor 年视图天的颜色
+ * @param yarViewSchemeTextColor 年视图标记颜色
+ */
+ public void setYearViewTextColor(int yearViewMonthTextColor, int yearViewDayTextColor, int yarViewSchemeTextColor) {
+ if (mDelegate == null || mYearViewPager == null) {
+ return;
+ }
+ mDelegate.setYearViewTextColor(yearViewMonthTextColor, yearViewDayTextColor, yarViewSchemeTextColor);
+ mYearViewPager.updateStyle();
+ }
+
+ /**
+ * 设置星期栏的背景和字体颜色
+ *
+ * @param weekBackground 背景色
+ * @param weekTextColor 字体颜色
+ */
+ public void setWeeColor(int weekBackground, int weekTextColor) {
+ if (mWeekBar == null) {
+ return;
+ }
+ mWeekBar.setBackgroundColor(weekBackground);
+ mWeekBar.setTextColor(weekTextColor);
+ }
+
+
+ public void setCalendarPadding(int mCalendarPadding) {
+ if (mDelegate == null) {
+ return;
+ }
+ mDelegate.setCalendarPadding(mCalendarPadding);
+ update();
+ }
+
+
+ public void setCalendarPaddingLeft(int mCalendarPaddingLeft) {
+ if (mDelegate == null) {
+ return;
+ }
+ mDelegate.setCalendarPaddingLeft(mCalendarPaddingLeft);
+ update();
+ }
+
+ public void setCalendarPaddingRight(int mCalendarPaddingRight) {
+ if (mDelegate == null) {
+ return;
+ }
+ mDelegate.setCalendarPaddingRight(mCalendarPaddingRight);
+ update();
+ }
+
+
+ /**
+ * 默认选择模式
+ */
+ public final void setSelectDefaultMode() {
+ if (mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_DEFAULT) {
+ return;
+ }
+ mDelegate.mSelectedCalendar = mDelegate.mIndexCalendar;
+ mDelegate.setSelectMode(CalendarViewDelegate.SELECT_MODE_DEFAULT);
+ mWeekBar.onDateSelected(mDelegate.mSelectedCalendar, mDelegate.getWeekStart(), false);
+ mMonthPager.updateDefaultSelect();
+ mWeekPager.updateDefaultSelect();
+
+ }
+
+ /**
+ * 范围模式
+ */
+ public void setSelectRangeMode() {
+ if (mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_RANGE) {
+ return;
+ }
+ mDelegate.setSelectMode(CalendarViewDelegate.SELECT_MODE_RANGE);
+ clearSelectRange();
+ }
+
+ /**
+ * 多选模式
+ */
+ public void setSelectMultiMode() {
+ if (mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_MULTI) {
+ return;
+ }
+ mDelegate.setSelectMode(CalendarViewDelegate.SELECT_MODE_MULTI);
+ clearMultiSelect();
+ }
+
+ /**
+ * 单选模式
+ */
+ public void setSelectSingleMode() {
+ if (mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_SINGLE) {
+ return;
+ }
+ mDelegate.setSelectMode(CalendarViewDelegate.SELECT_MODE_SINGLE);
+ mWeekPager.updateSelected();
+ mMonthPager.updateSelected();
+ }
+
+ /**
+ * 设置星期日周起始
+ */
+ public void setWeekStarWithSun() {
+ setWeekStart(CalendarViewDelegate.WEEK_START_WITH_SUN);
+ }
+
+ /**
+ * 设置星期一周起始
+ */
+ public void setWeekStarWithMon() {
+ setWeekStart(CalendarViewDelegate.WEEK_START_WITH_MON);
+ }
+
+ /**
+ * 设置星期六周起始
+ */
+ public void setWeekStarWithSat() {
+ setWeekStart(CalendarViewDelegate.WEEK_START_WITH_SAT);
+ }
+
+ /**
+ * 设置周起始
+ * CalendarViewDelegate.WEEK_START_WITH_SUN
+ * CalendarViewDelegate.WEEK_START_WITH_MON
+ * CalendarViewDelegate.WEEK_START_WITH_SAT
+ *
+ * @param weekStart 周起始
+ */
+ private void setWeekStart(int weekStart) {
+ if (weekStart != CalendarViewDelegate.WEEK_START_WITH_SUN &&
+ weekStart != CalendarViewDelegate.WEEK_START_WITH_MON &&
+ weekStart != CalendarViewDelegate.WEEK_START_WITH_SAT)
+ return;
+ if (weekStart == mDelegate.getWeekStart())
+ return;
+ mDelegate.setWeekStart(weekStart);
+ mWeekBar.onWeekStartChange(weekStart);
+ mWeekBar.onDateSelected(mDelegate.mSelectedCalendar, weekStart, false);
+ mWeekPager.updateWeekStart();
+ mMonthPager.updateWeekStart();
+ mYearViewPager.updateWeekStart();
+ }
+
+ /**
+ * 是否是单选模式
+ *
+ * @return isSingleSelectMode
+ */
+ public boolean isSingleSelectMode() {
+ return mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_SINGLE;
+ }
+
+ /**
+ * 设置显示模式为全部
+ */
+ public void setAllMode() {
+ setShowMode(CalendarViewDelegate.MODE_ALL_MONTH);
+ }
+
+ /**
+ * 设置显示模式为仅当前月份
+ */
+ public void setOnlyCurrentMode() {
+ setShowMode(CalendarViewDelegate.MODE_ONLY_CURRENT_MONTH);
+ }
+
+ /**
+ * 设置显示模式为填充
+ */
+ public void setFixMode() {
+ setShowMode(CalendarViewDelegate.MODE_FIT_MONTH);
+ }
+
+ /**
+ * 设置显示模式
+ * CalendarViewDelegate.MODE_ALL_MONTH
+ * CalendarViewDelegate.MODE_ONLY_CURRENT_MONTH
+ * CalendarViewDelegate.MODE_FIT_MONTH
+ *
+ * @param mode 月视图显示模式
+ */
+ private void setShowMode(int mode) {
+ if (mode != CalendarViewDelegate.MODE_ALL_MONTH &&
+ mode != CalendarViewDelegate.MODE_ONLY_CURRENT_MONTH &&
+ mode != CalendarViewDelegate.MODE_FIT_MONTH)
+ return;
+ if (mDelegate.getMonthViewShowMode() == mode)
+ return;
+ mDelegate.setMonthViewShowMode(mode);
+ mWeekPager.updateShowMode();
+ mMonthPager.updateShowMode();
+ mWeekPager.notifyDataSetChanged();
+ }
+
+ /**
+ * 更新界面,
+ * 重新设置颜色等都需要调用该方法
+ */
+ public final void update() {
+ mWeekBar.onWeekStartChange(mDelegate.getWeekStart());
+ mYearViewPager.update();
+ mMonthPager.updateScheme();
+ mWeekPager.updateScheme();
+ }
+
+ /**
+ * 更新周视图
+ */
+ public void updateWeekBar() {
+ mWeekBar.onWeekStartChange(mDelegate.getWeekStart());
+ }
+
+
+ /**
+ * 更新当前日期
+ */
+ public final void updateCurrentDate() {
+ if (mDelegate == null || mMonthPager == null || mWeekPager == null) {
+ return;
+ }
+ java.util.Calendar calendar = java.util.Calendar.getInstance();
+ int day = calendar.get(java.util.Calendar.DAY_OF_MONTH);
+ if (getCurDay() == day) {
+ return;
+ }
+ mDelegate.updateCurrentDay();
+ mMonthPager.updateCurrentDate();
+ mWeekPager.updateCurrentDate();
+ }
+
+ /**
+ * 获取当前周数据
+ *
+ * @return 获取当前周数据
+ */
+ public List getCurrentWeekCalendars() {
+ return mWeekPager.getCurrentWeekCalendars();
+ }
+
+
+ /**
+ * 获取当前月份日期
+ *
+ * @return return
+ */
+ public List getCurrentMonthCalendars() {
+ return mMonthPager.getCurrentMonthCalendars();
+ }
+
+ /**
+ * 获取选择的日期
+ *
+ * @return 获取选择的日期
+ */
+ public Calendar getSelectedCalendar() {
+ return mDelegate.mSelectedCalendar;
+ }
+
+ /**
+ * 获得最小范围日期
+ *
+ * @return 最小范围日期
+ */
+ public Calendar getMinRangeCalendar() {
+ return mDelegate.getMinRangeCalendar();
+ }
+
+
+ /**
+ * 获得最大范围日期
+ *
+ * @return 最大范围日期
+ */
+ public Calendar getMaxRangeCalendar() {
+ return mDelegate.getMaxRangeCalendar();
+ }
+
+ /**
+ * MonthViewPager
+ *
+ * @return 获得月视图
+ */
+ public MonthViewPager getMonthViewPager() {
+ return mMonthPager;
+ }
+
+ /**
+ * 获得周视图
+ *
+ * @return 获得周视图
+ */
+ public WeekViewPager getWeekViewPager() {
+ return mWeekPager;
+ }
+
+ /**
+ * 是否在日期范围内
+ *
+ * @param calendar calendar
+ * @return 是否在日期范围内
+ */
+ protected final boolean isInRange(Calendar calendar) {
+ return mDelegate != null && CalendarUtil.isCalendarInRange(calendar, mDelegate);
+ }
+
+
+ /**
+ * 年份视图切换事件,快速年份切换
+ */
+ public interface OnYearChangeListener {
+ void onYearChange(int year);
+ }
+
+ /**
+ * 月份切换事件
+ */
+ public interface OnMonthChangeListener {
+ void onMonthChange(int year, int month);
+ }
+
+
+ /**
+ * 周视图切换事件
+ */
+ public interface OnWeekChangeListener {
+ void onWeekChange(List weekCalendars);
+ }
+
+ /**
+ * 内部日期选择,不暴露外部使用
+ * 主要是用于更新日历CalendarLayout位置
+ */
+ interface OnInnerDateSelectedListener {
+ /**
+ * 月视图点击
+ *
+ * @param calendar calendar
+ * @param isClick 是否是点击
+ */
+ void onMonthDateSelected(Calendar calendar, boolean isClick);
+
+ /**
+ * 周视图点击
+ *
+ * @param calendar calendar
+ * @param isClick 是否是点击
+ */
+ void onWeekDateSelected(Calendar calendar, boolean isClick);
+ }
+
+
+ /**
+ * 日历范围选择事件
+ */
+ public interface OnCalendarRangeSelectListener {
+
+ /**
+ * 范围选择超出范围越界
+ *
+ * @param calendar calendar
+ */
+ void onCalendarSelectOutOfRange(Calendar calendar);
+
+ /**
+ * 选择范围超出范围
+ *
+ * @param calendar calendar
+ * @param isOutOfMinRange 是否小于最小范围,否则为最大范围
+ */
+ void onSelectOutOfRange(Calendar calendar, boolean isOutOfMinRange);
+
+ /**
+ * 日期选择事件
+ *
+ * @param calendar calendar
+ * @param isEnd 是否结束
+ */
+ void onCalendarRangeSelect(Calendar calendar, boolean isEnd);
+ }
+
+
+ /**
+ * 日历多选事件
+ */
+ public interface OnCalendarMultiSelectListener {
+
+ /**
+ * 多选超出范围越界
+ *
+ * @param calendar calendar
+ */
+ void onCalendarMultiSelectOutOfRange(Calendar calendar);
+
+ /**
+ * 多选超出大小
+ *
+ * @param maxSize 最大大小
+ * @param calendar calendar
+ */
+ void onMultiSelectOutOfSize(Calendar calendar, int maxSize);
+
+ /**
+ * 多选事件
+ *
+ * @param calendar calendar
+ * @param curSize curSize
+ * @param maxSize maxSize
+ */
+ void onCalendarMultiSelect(Calendar calendar, int curSize, int maxSize);
+ }
+
+ /**
+ * 日历选择事件
+ */
+ public interface OnCalendarSelectListener {
+
+ /**
+ * 超出范围越界
+ *
+ * @param calendar calendar
+ */
+ void onCalendarOutOfRange(Calendar calendar);
+
+ /**
+ * 日期选择事件
+ *
+ * @param calendar calendar
+ * @param isClick isClick
+ */
+ void onCalendarSelect(Calendar calendar, boolean isClick);
+ }
+
+ public interface OnCalendarLongClickListener {
+
+ /**
+ * 超出范围越界
+ *
+ * @param calendar calendar
+ */
+ void onCalendarLongClickOutOfRange(Calendar calendar);
+
+ /**
+ * 日期长按事件
+ *
+ * @param calendar calendar
+ */
+ void onCalendarLongClick(Calendar calendar);
+ }
+
+ /**
+ * 视图改变事件
+ */
+ public interface OnViewChangeListener {
+ /**
+ * 视图改变事件
+ *
+ * @param isMonthView isMonthView是否是月视图
+ */
+ void onViewChange(boolean isMonthView);
+ }
+
+ /**
+ * 年视图改变事件
+ */
+ public interface OnYearViewChangeListener {
+ /**
+ * 年视图变化
+ *
+ * @param isClose 是否关闭
+ */
+ void onYearViewChange(boolean isClose);
+ }
+
+ /**
+ * 拦截日期是否可用事件
+ */
+ public interface OnCalendarInterceptListener {
+ boolean onCalendarIntercept(Calendar calendar);
+
+ void onCalendarInterceptClick(Calendar calendar, boolean isClick);
+ }
+
+ /**
+ * 点击Padding位置事件
+ */
+ public interface OnClickCalendarPaddingListener {
+ /**
+ * 点击Padding位置的事件
+ *
+ * @param x x坐标
+ * @param y y坐标
+ * @param isMonthView 是否是月视图,不是则为周视图
+ * @param adjacentCalendar 相邻的日历日期
+ * @param obj 此处的对象,自行设置
+ */
+ void onClickCalendarPadding(float x, float y, boolean isMonthView,
+ Calendar adjacentCalendar, Object obj);
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/CalendarViewDelegate.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/CalendarViewDelegate.java
new file mode 100644
index 00000000..22aeea2e
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/CalendarViewDelegate.java
@@ -0,0 +1,1189 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Google规范化的属性委托,
+ * 代码量多,但是不影响阅读性
+ */
+final class CalendarViewDelegate {
+
+ /**
+ * 周起始:周日
+ */
+ static final int WEEK_START_WITH_SUN = 1;
+
+ /**
+ * 周起始:周一
+ */
+ static final int WEEK_START_WITH_MON = 2;
+
+ /**
+ * 周起始:周六
+ */
+ static final int WEEK_START_WITH_SAT = 7;
+
+ /**
+ * 默认选择日期1号first_day_of_month
+ */
+ static final int FIRST_DAY_OF_MONTH = 0;
+
+ /**
+ * 跟随上个月last_select_day
+ */
+ static final int LAST_MONTH_VIEW_SELECT_DAY = 1;
+
+ /**
+ * 跟随上个月last_select_day_ignore_current忽视今天
+ */
+ static final int LAST_MONTH_VIEW_SELECT_DAY_IGNORE_CURRENT = 2;
+
+ private int mDefaultCalendarSelectDay;
+
+ /**
+ * 周起始
+ */
+ private int mWeekStart;
+
+ /**
+ * 全部显示
+ */
+ static final int MODE_ALL_MONTH = 0;
+ /**
+ * 仅显示当前月份
+ */
+ static final int MODE_ONLY_CURRENT_MONTH = 1;
+
+ /**
+ * 自适应显示,不会多出一行,但是会自动填充
+ */
+ static final int MODE_FIT_MONTH = 2;
+
+ /**
+ * 月份显示模式
+ */
+ private int mMonthViewShowMode;
+
+
+ /**
+ * 默认选择模式
+ */
+ static final int SELECT_MODE_DEFAULT = 0;
+
+ /**
+ * 单选模式
+ */
+ static final int SELECT_MODE_SINGLE = 1;
+
+ /**
+ * 范围选择模式
+ */
+ static final int SELECT_MODE_RANGE = 2;
+
+ /**
+ * 多选模式
+ */
+ static final int SELECT_MODE_MULTI = 3;
+
+ /**
+ * 选择模式
+ */
+ private int mSelectMode;
+
+
+ /**
+ * 支持转换的最小农历年份
+ */
+ static final int MIN_YEAR = 1900;
+ /**
+ * 支持转换的最大农历年份
+ */
+ private static final int MAX_YEAR = 2099;
+
+ /**
+ * 各种字体颜色,看名字知道对应的地方
+ */
+ private int mCurDayTextColor,
+ mCurDayLunarTextColor,
+ mWeekTextColor,
+ mSchemeTextColor,
+ mSchemeLunarTextColor,
+ mOtherMonthTextColor,
+ mCurrentMonthTextColor,
+ mSelectedTextColor,
+ mSelectedLunarTextColor,
+ mCurMonthLunarTextColor,
+ mOtherMonthLunarTextColor;
+
+ private boolean preventLongPressedSelected;
+
+ /**
+ * 年视图一些padding
+ */
+ private int
+ mYearViewPadding,
+ mYearViewPaddingLeft,
+ mYearViewPaddingRight;
+
+ /**
+ * 年视图一些padding
+ */
+ private int
+ mYearViewMonthPaddingLeft,
+ mYearViewMonthPaddingRight,
+ mYearViewMonthPaddingTop,
+ mYearViewMonthPaddingBottom;
+
+ /**
+ * 日历内部左右padding
+ */
+ private int mCalendarPadding;
+
+ /**
+ * 日历内部左padding
+ */
+ private int mCalendarPaddingLeft;
+
+ /**
+ * 日历内部右padding
+ */
+ private int mCalendarPaddingRight;
+
+ /**
+ * 年视图字体大小
+ */
+ private int mYearViewMonthTextSize,
+ mYearViewDayTextSize,
+ mYearViewWeekTextSize;
+
+ /**
+ * 年视图月份高度和周的高度
+ */
+ private int mYearViewMonthHeight,
+ mYearViewWeekHeight;
+
+
+ /**
+ * 年视图字体和标记颜色
+ */
+ private int mYearViewMonthTextColor,
+ mYearViewDayTextColor,
+ mYearViewSchemeTextColor,
+ mYearViewSelectTextColor,
+ mYearViewCurDayTextColor,
+ mYearViewWeekTextColor;
+
+ /**
+ * 星期栏的背景、线的背景、年份背景
+ */
+ private int mWeekLineBackground,
+ mYearViewBackground,
+ mWeekBackground;
+
+ /**
+ * 星期栏Line margin
+ */
+ private int mWeekLineMargin;
+
+ /**
+ * 星期栏字体大小
+ */
+ private int mWeekTextSize;
+
+ /**
+ * 标记的主题色和选中的主题色
+ */
+ private int mSchemeThemeColor, mSelectedThemeColor;
+
+
+ /**
+ * 自定义的日历路径
+ */
+ private String mMonthViewClassPath;
+
+ /**
+ * 月视图类
+ */
+ private Class> mMonthViewClass;
+
+ /**
+ * 自定义周视图路径
+ */
+ private String mWeekViewClassPath;
+
+ /**
+ * 周视图类
+ */
+ private Class> mWeekViewClass;
+
+ /**
+ * 自定义年视图路径
+ */
+ private String mYearViewClassPath;
+
+ /**
+ * 周视图类
+ */
+ private Class> mYearViewClass;
+
+ /**
+ * 自定义周栏路径
+ */
+ private String mWeekBarClassPath;
+
+ /**
+ * 自定义周栏
+ */
+ private Class> mWeekBarClass;
+
+ /**
+ * 年月视图是否打开
+ */
+ boolean isShowYearSelectedLayout;
+
+ /**
+ * 标记文本
+ */
+ private String mSchemeText;
+
+ /**
+ * 最小年份和最大年份
+ */
+ private int mMinYear, mMaxYear;
+
+ /**
+ * 最小年份和最大年份对应最小月份和最大月份
+ * when you want set 2015-07 to 2017-08
+ */
+ private int mMinYearMonth, mMaxYearMonth;
+
+ /**
+ * 最小年份和最大年份对应最小天和最大天数
+ * when you want set like 2015-07-08 to 2017-08-30
+ */
+ private int mMinYearDay, mMaxYearDay;
+
+ /**
+ * 日期和农历文本大小
+ */
+ private int mDayTextSize, mLunarTextSize;
+
+ /**
+ * 日历卡的项高度
+ */
+ private int mCalendarItemHeight;
+
+ /**
+ * 是否是全屏日历
+ */
+ private boolean isFullScreenCalendar;
+
+ /**
+ * 星期栏的高度
+ */
+ private int mWeekBarHeight;
+
+ /**
+ * 今天的日子
+ */
+ private Calendar mCurrentDate;
+
+
+ private boolean mMonthViewScrollable,
+ mWeekViewScrollable,
+ mYearViewScrollable;
+
+ /**
+ * 当前月份和周视图的item位置
+ */
+ int mCurrentMonthViewItem;
+
+ /**
+ * 标记的日期,数量巨大,请使用这个
+ */
+ Map mSchemeDatesMap;
+
+ /**
+ * 点击Padding位置事件
+ */
+ CalendarView.OnClickCalendarPaddingListener mClickCalendarPaddingListener;
+
+ /**
+ * 日期拦截事件
+ */
+ CalendarView.OnCalendarInterceptListener mCalendarInterceptListener;
+
+ /**
+ * 日期选中监听
+ */
+ CalendarView.OnCalendarSelectListener mCalendarSelectListener;
+
+ /**
+ * 范围选择
+ */
+ CalendarView.OnCalendarRangeSelectListener mCalendarRangeSelectListener;
+
+
+ /**
+ * 多选选择事件
+ */
+ CalendarView.OnCalendarMultiSelectListener mCalendarMultiSelectListener;
+
+ /**
+ * 外部日期长按事件
+ */
+ CalendarView.OnCalendarLongClickListener mCalendarLongClickListener;
+
+ /**
+ * 内部日期切换监听,用于内部更新计算
+ */
+ CalendarView.OnInnerDateSelectedListener mInnerListener;
+
+ /**
+ * 快速年份切换
+ */
+ CalendarView.OnYearChangeListener mYearChangeListener;
+
+
+ /**
+ * 月份切换事件
+ */
+ CalendarView.OnMonthChangeListener mMonthChangeListener;
+
+ /**
+ * 周视图改变事件
+ */
+ CalendarView.OnWeekChangeListener mWeekChangeListener;
+
+ /**
+ * 视图改变事件
+ */
+ CalendarView.OnViewChangeListener mViewChangeListener;
+
+
+ /**
+ * 年视图改变事件
+ */
+ CalendarView.OnYearViewChangeListener mYearViewChangeListener;
+
+ /**
+ * 保存选中的日期
+ */
+ Calendar mSelectedCalendar;
+
+ /**
+ * 保存标记位置
+ */
+ Calendar mIndexCalendar;
+
+ /**
+ * 多选日历
+ */
+ Map mSelectedCalendars = new HashMap<>();
+
+ private int mMaxMultiSelectSize;
+
+ /**
+ * 选择范围日历
+ */
+ Calendar mSelectedStartRangeCalendar, mSelectedEndRangeCalendar;
+
+ private int mMinSelectRange, mMaxSelectRange;
+
+ CalendarViewDelegate(Context context, @Nullable AttributeSet attrs) {
+ TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CalendarView);
+
+ LunarCalendar.init(context);
+
+ mCalendarPadding = (int) array.getDimension(R.styleable.CalendarView_calendar_padding, 0);
+ mCalendarPaddingLeft = (int) array.getDimension(R.styleable.CalendarView_calendar_padding_left, 0);
+ mCalendarPaddingRight = (int) array.getDimension(R.styleable.CalendarView_calendar_padding_right, 0);
+
+ if (mCalendarPadding != 0) {
+ mCalendarPaddingLeft = mCalendarPadding;
+ mCalendarPaddingRight = mCalendarPadding;
+ }
+
+ mSchemeTextColor = array.getColor(R.styleable.CalendarView_scheme_text_color, 0xFFFFFFFF);
+ mSchemeLunarTextColor = array.getColor(R.styleable.CalendarView_scheme_lunar_text_color, 0xFFe1e1e1);
+ mSchemeThemeColor = array.getColor(R.styleable.CalendarView_scheme_theme_color, 0x50CFCFCF);
+ mMonthViewClassPath = array.getString(R.styleable.CalendarView_month_view);
+ mYearViewClassPath = array.getString(R.styleable.CalendarView_year_view);
+ mWeekViewClassPath = array.getString(R.styleable.CalendarView_week_view);
+ mWeekBarClassPath = array.getString(R.styleable.CalendarView_week_bar_view);
+ mWeekTextSize = array.getDimensionPixelSize(R.styleable.CalendarView_week_text_size,
+ CalendarUtil.dipToPx(context, 12));
+ mWeekBarHeight = (int) array.getDimension(R.styleable.CalendarView_week_bar_height,
+ CalendarUtil.dipToPx(context, 40));
+ mWeekLineMargin = (int) array.getDimension(R.styleable.CalendarView_week_line_margin,
+ CalendarUtil.dipToPx(context, 0));
+
+ mSchemeText = array.getString(R.styleable.CalendarView_scheme_text);
+ if (TextUtils.isEmpty(mSchemeText)) {
+ mSchemeText = "记";
+ }
+
+ mMonthViewScrollable = array.getBoolean(R.styleable.CalendarView_month_view_scrollable, true);
+ mWeekViewScrollable = array.getBoolean(R.styleable.CalendarView_week_view_scrollable, true);
+ mYearViewScrollable = array.getBoolean(R.styleable.CalendarView_year_view_scrollable, true);
+
+ mDefaultCalendarSelectDay = array.getInt(R.styleable.CalendarView_month_view_auto_select_day,
+ FIRST_DAY_OF_MONTH);
+
+ mMonthViewShowMode = array.getInt(R.styleable.CalendarView_month_view_show_mode, MODE_ALL_MONTH);
+ mWeekStart = array.getInt(R.styleable.CalendarView_week_start_with, WEEK_START_WITH_SUN);
+ mSelectMode = array.getInt(R.styleable.CalendarView_select_mode, SELECT_MODE_DEFAULT);
+ mMaxMultiSelectSize = array.getInt(R.styleable.CalendarView_max_multi_select_size, Integer.MAX_VALUE);
+ mMinSelectRange = array.getInt(R.styleable.CalendarView_min_select_range, -1);
+ mMaxSelectRange = array.getInt(R.styleable.CalendarView_max_select_range, -1);
+ setSelectRange(mMinSelectRange, mMaxSelectRange);
+
+ mWeekBackground = array.getColor(R.styleable.CalendarView_week_background, Color.WHITE);
+ mWeekLineBackground = array.getColor(R.styleable.CalendarView_week_line_background, Color.TRANSPARENT);
+ mYearViewBackground = array.getColor(R.styleable.CalendarView_year_view_background, Color.WHITE);
+ mWeekTextColor = array.getColor(R.styleable.CalendarView_week_text_color, 0xFF333333);
+
+ mCurDayTextColor = array.getColor(R.styleable.CalendarView_current_day_text_color, Color.RED);
+ mCurDayLunarTextColor = array.getColor(R.styleable.CalendarView_current_day_lunar_text_color, Color.RED);
+
+ mSelectedThemeColor = array.getColor(R.styleable.CalendarView_selected_theme_color, 0x50CFCFCF);
+ mSelectedTextColor = array.getColor(R.styleable.CalendarView_selected_text_color, 0xFF111111);
+
+ mSelectedLunarTextColor = array.getColor(R.styleable.CalendarView_selected_lunar_text_color, 0xFF111111);
+ mCurrentMonthTextColor = array.getColor(R.styleable.CalendarView_current_month_text_color, 0xFF111111);
+ mOtherMonthTextColor = array.getColor(R.styleable.CalendarView_other_month_text_color, 0xFFe1e1e1);
+
+ mCurMonthLunarTextColor = array.getColor(R.styleable.CalendarView_current_month_lunar_text_color, 0xffe1e1e1);
+ mOtherMonthLunarTextColor = array.getColor(R.styleable.CalendarView_other_month_lunar_text_color, 0xffe1e1e1);
+ mMinYear = array.getInt(R.styleable.CalendarView_min_year, 1971);
+ mMaxYear = array.getInt(R.styleable.CalendarView_max_year, 2055);
+ mMinYearMonth = array.getInt(R.styleable.CalendarView_min_year_month, 1);
+ mMaxYearMonth = array.getInt(R.styleable.CalendarView_max_year_month, 12);
+ mMinYearDay = array.getInt(R.styleable.CalendarView_min_year_day, 1);
+ mMaxYearDay = array.getInt(R.styleable.CalendarView_max_year_day, -1);
+
+ mDayTextSize = array.getDimensionPixelSize(R.styleable.CalendarView_day_text_size,
+ CalendarUtil.dipToPx(context, 16));
+ mLunarTextSize = array.getDimensionPixelSize(R.styleable.CalendarView_lunar_text_size,
+ CalendarUtil.dipToPx(context, 10));
+ mCalendarItemHeight = (int) array.getDimension(R.styleable.CalendarView_calendar_height,
+ CalendarUtil.dipToPx(context, 56));
+ isFullScreenCalendar = array.getBoolean(R.styleable.CalendarView_calendar_match_parent, false);
+
+ //年视图相关
+ mYearViewMonthTextSize = array.getDimensionPixelSize(R.styleable.CalendarView_year_view_month_text_size,
+ CalendarUtil.dipToPx(context, 18));
+ mYearViewDayTextSize = array.getDimensionPixelSize(R.styleable.CalendarView_year_view_day_text_size,
+ CalendarUtil.dipToPx(context, 7));
+ mYearViewMonthTextColor = array.getColor(R.styleable.CalendarView_year_view_month_text_color, 0xFF111111);
+ mYearViewDayTextColor = array.getColor(R.styleable.CalendarView_year_view_day_text_color, 0xFF111111);
+ mYearViewSchemeTextColor = array.getColor(R.styleable.CalendarView_year_view_scheme_color, mSchemeThemeColor);
+ mYearViewWeekTextColor = array.getColor(R.styleable.CalendarView_year_view_week_text_color, 0xFF333333);
+ mYearViewCurDayTextColor = array.getColor(R.styleable.CalendarView_year_view_current_day_text_color, mCurDayTextColor);
+ mYearViewSelectTextColor = array.getColor(R.styleable.CalendarView_year_view_select_text_color, 0xFF333333);
+ mYearViewWeekTextSize = array.getDimensionPixelSize(R.styleable.CalendarView_year_view_week_text_size,
+ CalendarUtil.dipToPx(context, 8));
+ mYearViewMonthHeight = array.getDimensionPixelSize(R.styleable.CalendarView_year_view_month_height,
+ CalendarUtil.dipToPx(context, 32));
+ mYearViewWeekHeight = array.getDimensionPixelSize(R.styleable.CalendarView_year_view_week_height,
+ CalendarUtil.dipToPx(context, 0));
+
+ mYearViewPadding = (int) array.getDimension(R.styleable.CalendarView_year_view_padding,
+ CalendarUtil.dipToPx(context, 12));
+ mYearViewPaddingLeft = (int) array.getDimension(R.styleable.CalendarView_year_view_padding_left,
+ CalendarUtil.dipToPx(context, 12));
+ mYearViewPaddingRight = (int) array.getDimension(R.styleable.CalendarView_year_view_padding_right,
+ CalendarUtil.dipToPx(context, 12));
+
+ if (mYearViewPadding != 0) {
+ mYearViewPaddingLeft = mYearViewPadding;
+ mYearViewPaddingRight = mYearViewPadding;
+ }
+
+ mYearViewMonthPaddingTop = (int) array.getDimension(R.styleable.CalendarView_year_view_month_padding_top,
+ CalendarUtil.dipToPx(context, 4));
+ mYearViewMonthPaddingBottom = (int) array.getDimension(R.styleable.CalendarView_year_view_month_padding_bottom,
+ CalendarUtil.dipToPx(context, 4));
+
+ mYearViewMonthPaddingLeft = (int) array.getDimension(R.styleable.CalendarView_year_view_month_padding_left,
+ CalendarUtil.dipToPx(context, 4));
+ mYearViewMonthPaddingRight = (int) array.getDimension(R.styleable.CalendarView_year_view_month_padding_right,
+ CalendarUtil.dipToPx(context, 4));
+
+ if (mMinYear <= MIN_YEAR) mMinYear = MIN_YEAR;
+ if (mMaxYear >= MAX_YEAR) mMaxYear = MAX_YEAR;
+ array.recycle();
+ init();
+ }
+
+ private void init() {
+ mCurrentDate = new Calendar();
+ Date d = new Date();
+ mCurrentDate.setYear(CalendarUtil.getDate("yyyy", d));
+ mCurrentDate.setMonth(CalendarUtil.getDate("MM", d));
+ mCurrentDate.setDay(CalendarUtil.getDate("dd", d));
+ mCurrentDate.setCurrentDay(true);
+ LunarCalendar.setupLunarCalendar(mCurrentDate);
+ setRange(mMinYear, mMinYearMonth, mMaxYear, mMaxYearMonth);
+
+ try {
+ mWeekBarClass = TextUtils.isEmpty(mWeekBarClassPath) ?
+ mWeekBarClass = WeekBar.class : Class.forName(mWeekBarClassPath);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ try {
+ mYearViewClass = TextUtils.isEmpty(mYearViewClassPath) ?
+ mYearViewClass = DefaultYearView.class : Class.forName(mYearViewClassPath);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ try {
+ mMonthViewClass = TextUtils.isEmpty(mMonthViewClassPath) ?
+ DefaultMonthView.class : Class.forName(mMonthViewClassPath);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ try {
+ mWeekViewClass = TextUtils.isEmpty(mWeekViewClassPath) ?
+ DefaultWeekView.class : Class.forName(mWeekViewClassPath);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ private void setRange(int minYear, int minYearMonth,
+ int maxYear, int maxYearMonth) {
+ this.mMinYear = minYear;
+ this.mMinYearMonth = minYearMonth;
+ this.mMaxYear = maxYear;
+ this.mMaxYearMonth = maxYearMonth;
+ if (this.mMaxYear < mCurrentDate.getYear()) {
+ this.mMaxYear = mCurrentDate.getYear();
+ }
+ if (this.mMaxYearDay == -1) {
+ this.mMaxYearDay = CalendarUtil.getMonthDaysCount(this.mMaxYear, mMaxYearMonth);
+ }
+ int y = mCurrentDate.getYear() - this.mMinYear;
+ mCurrentMonthViewItem = 12 * y + mCurrentDate.getMonth() - this.mMinYearMonth;
+ }
+
+ void setRange(int minYear, int minYearMonth, int minYearDay,
+ int maxYear, int maxYearMonth, int maxYearDay) {
+ this.mMinYear = minYear;
+ this.mMinYearMonth = minYearMonth;
+ this.mMinYearDay = minYearDay;
+ this.mMaxYear = maxYear;
+ this.mMaxYearMonth = maxYearMonth;
+ this.mMaxYearDay = maxYearDay;
+// if (this.mMaxYear < mCurrentDate.getYear()) {
+// this.mMaxYear = mCurrentDate.getYear();
+// }
+ if (this.mMaxYearDay == -1) {
+ this.mMaxYearDay = CalendarUtil.getMonthDaysCount(this.mMaxYear, mMaxYearMonth);
+ }
+ int y = mCurrentDate.getYear() - this.mMinYear;
+ mCurrentMonthViewItem = 12 * y + mCurrentDate.getMonth() - this.mMinYearMonth;
+ }
+
+ String getSchemeText() {
+ return mSchemeText;
+ }
+
+ int getCurDayTextColor() {
+ return mCurDayTextColor;
+ }
+
+ int getCurDayLunarTextColor() {
+ return mCurDayLunarTextColor;
+ }
+
+ int getWeekTextColor() {
+ return mWeekTextColor;
+ }
+
+ int getSchemeTextColor() {
+ return mSchemeTextColor;
+ }
+
+ int getSchemeLunarTextColor() {
+ return mSchemeLunarTextColor;
+ }
+
+ int getOtherMonthTextColor() {
+ return mOtherMonthTextColor;
+ }
+
+ int getCurrentMonthTextColor() {
+ return mCurrentMonthTextColor;
+ }
+
+ int getSelectedTextColor() {
+ return mSelectedTextColor;
+ }
+
+ int getSelectedLunarTextColor() {
+ return mSelectedLunarTextColor;
+ }
+
+ int getCurrentMonthLunarTextColor() {
+ return mCurMonthLunarTextColor;
+ }
+
+ int getOtherMonthLunarTextColor() {
+ return mOtherMonthLunarTextColor;
+ }
+
+ int getSchemeThemeColor() {
+ return mSchemeThemeColor;
+ }
+
+ int getSelectedThemeColor() {
+ return mSelectedThemeColor;
+ }
+
+ int getWeekBackground() {
+ return mWeekBackground;
+ }
+
+ int getYearViewBackground() {
+ return mYearViewBackground;
+ }
+
+ int getWeekLineBackground() {
+ return mWeekLineBackground;
+ }
+
+ int getWeekLineMargin() {
+ return mWeekLineMargin;
+ }
+
+ Class> getMonthViewClass() {
+ return mMonthViewClass;
+ }
+
+ Class> getWeekViewClass() {
+ return mWeekViewClass;
+ }
+
+ Class> getWeekBarClass() {
+ return mWeekBarClass;
+ }
+
+ Class> getYearViewClass() {
+ return mYearViewClass;
+ }
+
+ String getYearViewClassPath() {
+ return mYearViewClassPath;
+ }
+
+ int getWeekBarHeight() {
+ return mWeekBarHeight;
+ }
+
+ int getMinYear() {
+ return mMinYear;
+ }
+
+ int getMaxYear() {
+ return mMaxYear;
+ }
+
+ int getDayTextSize() {
+ return mDayTextSize;
+ }
+
+ int getLunarTextSize() {
+ return mLunarTextSize;
+ }
+
+ int getCalendarItemHeight() {
+ return mCalendarItemHeight;
+ }
+
+ void setCalendarItemHeight(int height) {
+ mCalendarItemHeight = height;
+ }
+
+ int getMinYearMonth() {
+ return mMinYearMonth;
+ }
+
+ int getMaxYearMonth() {
+ return mMaxYearMonth;
+ }
+
+
+ int getYearViewMonthTextSize() {
+ return mYearViewMonthTextSize;
+ }
+
+ int getYearViewMonthTextColor() {
+ return mYearViewMonthTextColor;
+ }
+
+ int getYearViewWeekTextSize() {
+ return mYearViewWeekTextSize;
+ }
+
+ int getYearViewWeekTextColor() {
+ return mYearViewWeekTextColor;
+ }
+
+ int getYearViewSelectTextColor() {
+ return mYearViewSelectTextColor;
+ }
+
+ int getYearViewCurDayTextColor() {
+ return mYearViewCurDayTextColor;
+ }
+
+ @SuppressWarnings("unused")
+ int getYearViewPadding() {
+ return mYearViewPadding;
+ }
+
+ int getYearViewPaddingLeft() {
+ return mYearViewPaddingLeft;
+ }
+
+ int getYearViewPaddingRight() {
+ return mYearViewPaddingRight;
+ }
+
+
+ int getYearViewMonthPaddingLeft() {
+ return mYearViewMonthPaddingLeft;
+ }
+
+ int getYearViewMonthPaddingRight() {
+ return mYearViewMonthPaddingRight;
+ }
+
+ int getYearViewMonthPaddingTop() {
+ return mYearViewMonthPaddingTop;
+ }
+
+ int getYearViewMonthPaddingBottom() {
+ return mYearViewMonthPaddingBottom;
+ }
+
+ int getYearViewWeekHeight() {
+ return mYearViewWeekHeight;
+ }
+
+ int getYearViewMonthHeight() {
+ return mYearViewMonthHeight;
+ }
+
+ int getYearViewDayTextColor() {
+ return mYearViewDayTextColor;
+ }
+
+ int getYearViewDayTextSize() {
+ return mYearViewDayTextSize;
+ }
+
+ int getYearViewSchemeTextColor() {
+ return mYearViewSchemeTextColor;
+ }
+
+ int getMonthViewShowMode() {
+ return mMonthViewShowMode;
+ }
+
+ void setMonthViewShowMode(int monthViewShowMode) {
+ this.mMonthViewShowMode = monthViewShowMode;
+ }
+
+ void setTextColor(int curDayTextColor, int curMonthTextColor, int otherMonthTextColor, int curMonthLunarTextColor, int otherMonthLunarTextColor) {
+ mCurDayTextColor = curDayTextColor;
+ mOtherMonthTextColor = otherMonthTextColor;
+ mCurrentMonthTextColor = curMonthTextColor;
+ mCurMonthLunarTextColor = curMonthLunarTextColor;
+ mOtherMonthLunarTextColor = otherMonthLunarTextColor;
+ }
+
+ void setSchemeColor(int schemeColor, int schemeTextColor, int schemeLunarTextColor) {
+ this.mSchemeThemeColor = schemeColor;
+ this.mSchemeTextColor = schemeTextColor;
+ this.mSchemeLunarTextColor = schemeLunarTextColor;
+ }
+
+ void setYearViewTextColor(int yearViewMonthTextColor, int yearViewDayTextColor, int yarViewSchemeTextColor) {
+ this.mYearViewMonthTextColor = yearViewMonthTextColor;
+ this.mYearViewDayTextColor = yearViewDayTextColor;
+ this.mYearViewSchemeTextColor = yarViewSchemeTextColor;
+ }
+
+ void setSelectColor(int selectedColor, int selectedTextColor, int selectedLunarTextColor) {
+ this.mSelectedThemeColor = selectedColor;
+ this.mSelectedTextColor = selectedTextColor;
+ this.mSelectedLunarTextColor = selectedLunarTextColor;
+ }
+
+ void setThemeColor(int selectedThemeColor, int schemeColor) {
+ this.mSelectedThemeColor = selectedThemeColor;
+ this.mSchemeThemeColor = schemeColor;
+ }
+
+ boolean isMonthViewScrollable() {
+ return mMonthViewScrollable;
+ }
+
+ boolean isWeekViewScrollable() {
+ return mWeekViewScrollable;
+ }
+
+ boolean isYearViewScrollable() {
+ return mYearViewScrollable;
+ }
+
+ void setMonthViewScrollable(boolean monthViewScrollable) {
+ this.mMonthViewScrollable = monthViewScrollable;
+ }
+
+ void setWeekViewScrollable(boolean weekViewScrollable) {
+ this.mWeekViewScrollable = weekViewScrollable;
+ }
+
+ void setYearViewScrollable(boolean yearViewScrollable) {
+ this.mYearViewScrollable = yearViewScrollable;
+ }
+
+ int getWeekStart() {
+ return mWeekStart;
+ }
+
+ void setWeekStart(int mWeekStart) {
+ this.mWeekStart = mWeekStart;
+ }
+
+ void setDefaultCalendarSelectDay(int defaultCalendarSelect) {
+ this.mDefaultCalendarSelectDay = defaultCalendarSelect;
+ }
+
+ int getDefaultCalendarSelectDay() {
+ return mDefaultCalendarSelectDay;
+ }
+
+ int getWeekTextSize() {
+ return mWeekTextSize;
+ }
+
+ /**
+ * 选择模式
+ *
+ * @return 选择模式
+ */
+ int getSelectMode() {
+ return mSelectMode;
+ }
+
+ /**
+ * 设置选择模式
+ *
+ * @param mSelectMode mSelectMode
+ */
+ void setSelectMode(int mSelectMode) {
+ this.mSelectMode = mSelectMode;
+ }
+
+ int getMinSelectRange() {
+ return mMinSelectRange;
+ }
+
+ int getMaxSelectRange() {
+ return mMaxSelectRange;
+ }
+
+ int getMaxMultiSelectSize() {
+ return mMaxMultiSelectSize;
+ }
+
+ void setMaxMultiSelectSize(int maxMultiSelectSize) {
+ this.mMaxMultiSelectSize = maxMultiSelectSize;
+ }
+
+ final void setSelectRange(int minRange, int maxRange) {
+ if (minRange > maxRange && maxRange > 0) {
+ mMaxSelectRange = minRange;
+ mMinSelectRange = minRange;
+ return;
+ }
+ if (minRange <= 0) {
+ mMinSelectRange = -1;
+ } else {
+ mMinSelectRange = minRange;
+ }
+ if (maxRange <= 0) {
+ mMaxSelectRange = -1;
+ } else {
+ mMaxSelectRange = maxRange;
+ }
+ }
+
+ Calendar getCurrentDay() {
+ return mCurrentDate;
+ }
+
+ void updateCurrentDay() {
+ Date d = new Date();
+ mCurrentDate.setYear(CalendarUtil.getDate("yyyy", d));
+ mCurrentDate.setMonth(CalendarUtil.getDate("MM", d));
+ mCurrentDate.setDay(CalendarUtil.getDate("dd", d));
+ LunarCalendar.setupLunarCalendar(mCurrentDate);
+ }
+
+ @SuppressWarnings("unused")
+ int getCalendarPadding() {
+ return mCalendarPadding;
+ }
+
+ void setCalendarPadding(int mCalendarPadding) {
+ this.mCalendarPadding = mCalendarPadding;
+ mCalendarPaddingLeft = mCalendarPadding;
+ mCalendarPaddingRight = mCalendarPadding;
+ }
+
+ int getCalendarPaddingLeft() {
+ return mCalendarPaddingLeft;
+ }
+
+ void setCalendarPaddingLeft(int mCalendarPaddingLeft) {
+ this.mCalendarPaddingLeft = mCalendarPaddingLeft;
+ }
+
+ int getCalendarPaddingRight() {
+ return mCalendarPaddingRight;
+ }
+
+ void setCalendarPaddingRight(int mCalendarPaddingRight) {
+ this.mCalendarPaddingRight = mCalendarPaddingRight;
+ }
+
+ void setPreventLongPressedSelected(boolean preventLongPressedSelected) {
+ this.preventLongPressedSelected = preventLongPressedSelected;
+ }
+
+ void setMonthViewClass(Class> monthViewClass) {
+ this.mMonthViewClass = monthViewClass;
+ }
+
+ void setWeekBarClass(Class> weekBarClass) {
+ this.mWeekBarClass = weekBarClass;
+ }
+
+ void setWeekViewClass(Class> weekViewClass) {
+ this.mWeekViewClass = weekViewClass;
+ }
+
+ boolean isPreventLongPressedSelected() {
+ return preventLongPressedSelected;
+ }
+
+ void clearSelectedScheme() {
+ mSelectedCalendar.clearScheme();
+ }
+
+ int getMinYearDay() {
+ return mMinYearDay;
+ }
+
+ int getMaxYearDay() {
+ return mMaxYearDay;
+ }
+
+ boolean isFullScreenCalendar() {
+ return isFullScreenCalendar;
+ }
+
+ final void updateSelectCalendarScheme() {
+ if (mSchemeDatesMap != null && mSchemeDatesMap.size() > 0) {
+ String key = mSelectedCalendar.toString();
+ if (mSchemeDatesMap.containsKey(key)) {
+ Calendar d = mSchemeDatesMap.get(key);
+ mSelectedCalendar.mergeScheme(d, getSchemeText());
+ }
+ } else {
+ clearSelectedScheme();
+ }
+ }
+
+ final void updateCalendarScheme(Calendar targetCalendar) {
+ if (targetCalendar == null) {
+ return;
+ }
+ if (mSchemeDatesMap == null || mSchemeDatesMap.size() == 0) {
+ return;
+ }
+ String key = targetCalendar.toString();
+ if (mSchemeDatesMap.containsKey(key)) {
+ Calendar d = mSchemeDatesMap.get(key);
+ targetCalendar.mergeScheme(d, getSchemeText());
+ }
+ }
+
+ Calendar createCurrentDate() {
+ Calendar calendar = new Calendar();
+ calendar.setYear(mCurrentDate.getYear());
+ calendar.setWeek(mCurrentDate.getWeek());
+ calendar.setMonth(mCurrentDate.getMonth());
+ calendar.setDay(mCurrentDate.getDay());
+ calendar.setCurrentDay(true);
+ LunarCalendar.setupLunarCalendar(calendar);
+ return calendar;
+ }
+
+ final Calendar getMinRangeCalendar() {
+ Calendar calendar = new Calendar();
+ calendar.setYear(mMinYear);
+ calendar.setMonth(mMinYearMonth);
+ calendar.setDay(mMinYearDay);
+ calendar.setCurrentDay(calendar.equals(mCurrentDate));
+ LunarCalendar.setupLunarCalendar(calendar);
+ return calendar;
+ }
+
+ @SuppressWarnings("unused")
+ final Calendar getMaxRangeCalendar() {
+ Calendar calendar = new Calendar();
+ calendar.setYear(mMaxYear);
+ calendar.setMonth(mMaxYearMonth);
+ calendar.setDay(mMaxYearDay);
+ calendar.setCurrentDay(calendar.equals(mCurrentDate));
+ LunarCalendar.setupLunarCalendar(calendar);
+ return calendar;
+ }
+
+ /**
+ * 添加事件标记,来自Map
+ */
+ final void addSchemesFromMap(List mItems) {
+ if (mSchemeDatesMap == null || mSchemeDatesMap.size() == 0) {
+ return;
+ }
+ for (Calendar a : mItems) {
+ if (mSchemeDatesMap.containsKey(a.toString())) {
+ Calendar d = mSchemeDatesMap.get(a.toString());
+ if (d == null) {
+ continue;
+ }
+ a.setScheme(TextUtils.isEmpty(d.getScheme()) ? getSchemeText() : d.getScheme());
+ a.setSchemeColor(d.getSchemeColor());
+ a.setSchemes(d.getSchemes());
+ } else {
+ a.setScheme("");
+ a.setSchemeColor(0);
+ a.setSchemes(null);
+ }
+ }
+ }
+
+ /**
+ * 添加数据
+ *
+ * @param mSchemeDates mSchemeDates
+ */
+ final void addSchemes(Map mSchemeDates) {
+ if (mSchemeDates == null || mSchemeDates.size() == 0) {
+ return;
+ }
+ if (this.mSchemeDatesMap == null) {
+ this.mSchemeDatesMap = new HashMap<>();
+ }
+ for (String key : mSchemeDates.keySet()) {
+ this.mSchemeDatesMap.remove(key);
+ Calendar calendar = mSchemeDates.get(key);
+ if (calendar == null) {
+ continue;
+ }
+ this.mSchemeDatesMap.put(key, calendar);
+ }
+ }
+
+ /**
+ * 清楚选择
+ */
+ final void clearSelectRange() {
+ mSelectedStartRangeCalendar = null;
+ mSelectedEndRangeCalendar = null;
+ }
+
+ /**
+ * 获得选中范围
+ *
+ * @return 选中范围
+ */
+ final List getSelectCalendarRange() {
+ if (mSelectMode != SELECT_MODE_RANGE) {
+ return null;
+ }
+ List calendars = new ArrayList<>();
+ if (mSelectedStartRangeCalendar == null ||
+ mSelectedEndRangeCalendar == null) {
+ return calendars;
+ }
+ final long ONE_DAY = 1000 * 3600 * 24;
+ java.util.Calendar date = java.util.Calendar.getInstance();
+
+ date.set(mSelectedStartRangeCalendar.getYear(),
+ mSelectedStartRangeCalendar.getMonth() - 1,
+ mSelectedStartRangeCalendar.getDay());//
+
+ long startTimeMills = date.getTimeInMillis();//获得起始时间戳
+
+
+ date.set(mSelectedEndRangeCalendar.getYear(),
+ mSelectedEndRangeCalendar.getMonth() - 1,
+ mSelectedEndRangeCalendar.getDay());//
+ long endTimeMills = date.getTimeInMillis();
+ for (long start = startTimeMills; start <= endTimeMills; start += ONE_DAY) {
+ date.setTimeInMillis(start);
+ Calendar calendar = new Calendar();
+ calendar.setYear(date.get(java.util.Calendar.YEAR));
+ calendar.setMonth(date.get(java.util.Calendar.MONTH) + 1);
+ calendar.setDay(date.get(java.util.Calendar.DAY_OF_MONTH));
+ LunarCalendar.setupLunarCalendar(calendar);
+ updateCalendarScheme(calendar);
+ if (mCalendarInterceptListener != null &&
+ mCalendarInterceptListener.onCalendarIntercept(calendar)) {
+ continue;
+ }
+
+ calendars.add(calendar);
+ }
+ addSchemesFromMap(calendars);
+ return calendars;
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/DefaultMonthView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/DefaultMonthView.java
new file mode 100644
index 00000000..cb06080c
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/DefaultMonthView.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+/**
+ * 默认高仿魅族日历布局
+ * Created by huanghaibin on 2017/11/15.
+ */
+
+public final class DefaultMonthView extends MonthView {
+
+ private Paint mTextPaint = new Paint();
+ private Paint mSchemeBasicPaint = new Paint();
+ private float mRadio;
+ private int mPadding;
+ private float mSchemeBaseLine;
+
+ public DefaultMonthView(Context context) {
+ super(context);
+
+ mTextPaint.setTextSize(CalendarUtil.dipToPx(context, 8));
+ mTextPaint.setColor(0xffFFFFFF);
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setFakeBoldText(true);
+
+ mSchemeBasicPaint.setAntiAlias(true);
+ mSchemeBasicPaint.setStyle(Paint.Style.FILL);
+ mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);
+ mSchemeBasicPaint.setColor(0xffed5353);
+ mSchemeBasicPaint.setFakeBoldText(true);
+ mRadio = CalendarUtil.dipToPx(getContext(), 7);
+ mPadding = CalendarUtil.dipToPx(getContext(), 4);
+ Paint.FontMetrics metrics = mSchemeBasicPaint.getFontMetrics();
+ mSchemeBaseLine = mRadio - metrics.descent + (metrics.bottom - metrics.top) / 2 + CalendarUtil.dipToPx(getContext(), 1);
+
+ }
+
+ /**
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @return true 则绘制onDrawScheme,因为这里背景色不是是互斥的
+ */
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+ mSelectedPaint.setStyle(Paint.Style.FILL);
+ canvas.drawRect(x + mPadding, y + mPadding, x + mItemWidth - mPadding, y + mItemHeight - mPadding, mSelectedPaint);
+ return true;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+ mSchemeBasicPaint.setColor(calendar.getSchemeColor());
+
+ canvas.drawCircle(x + mItemWidth - mPadding - mRadio / 2, y + mPadding + mRadio, mRadio, mSchemeBasicPaint);
+
+ canvas.drawText(calendar.getScheme(),
+ x + mItemWidth - mPadding - mRadio / 2 - getTextWidth(calendar.getScheme()) / 2,
+ y + mPadding + mSchemeBaseLine, mTextPaint);
+ }
+
+
+ /**
+ * 获取字体的宽
+ * @param text text
+ * @return return
+ */
+ private float getTextWidth(String text) {
+ return mTextPaint.measureText(text);
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int top = y - mItemHeight / 6;
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ mSelectTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mSelectedLunarTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint : mSchemeLunarTextPaint);
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthLunarTextPaint : mOtherMonthLunarTextPaint);
+ }
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/DefaultWeekView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/DefaultWeekView.java
new file mode 100644
index 00000000..41c13dfb
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/DefaultWeekView.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+/**
+ * 默认高仿魅族周视图
+ * Created by huanghaibin on 2017/11/29.
+ */
+
+public final class DefaultWeekView extends WeekView {
+ private Paint mTextPaint = new Paint();
+ private Paint mSchemeBasicPaint = new Paint();
+ private float mRadio;
+ private int mPadding;
+ private float mSchemeBaseLine;
+
+ public DefaultWeekView(Context context) {
+ super(context);
+
+ mTextPaint.setTextSize(CalendarUtil.dipToPx(context, 8));
+ mTextPaint.setColor(0xffffffff);
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setFakeBoldText(true);
+
+ mSchemeBasicPaint.setAntiAlias(true);
+ mSchemeBasicPaint.setStyle(Paint.Style.FILL);
+ mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);
+ mSchemeBasicPaint.setColor(0xffed5353);
+ mSchemeBasicPaint.setFakeBoldText(true);
+ mRadio = CalendarUtil.dipToPx(getContext(), 7);
+ mPadding = CalendarUtil.dipToPx(getContext(), 4);
+ Paint.FontMetrics metrics = mSchemeBasicPaint.getFontMetrics();
+ mSchemeBaseLine = mRadio - metrics.descent + (metrics.bottom - metrics.top) / 2 + CalendarUtil.dipToPx(getContext(), 1);
+
+ }
+
+ /**
+ * 如果需要点击Scheme没有效果,则return true
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @return true 则绘制onDrawScheme,因为这里背景色不是是互斥的
+ */
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme) {
+ mSelectedPaint.setStyle(Paint.Style.FILL);
+ canvas.drawRect(x + mPadding, mPadding, x + mItemWidth - mPadding, mItemHeight - mPadding, mSelectedPaint);
+ return true;
+ }
+
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x) {
+ mSchemeBasicPaint.setColor(calendar.getSchemeColor());
+
+ canvas.drawCircle(x + mItemWidth - mPadding - mRadio / 2, mPadding + mRadio, mRadio, mSchemeBasicPaint);
+
+ canvas.drawText(calendar.getScheme(),
+ x + mItemWidth - mPadding - mRadio / 2 - getTextWidth(calendar.getScheme()) / 2,
+ mPadding + mSchemeBaseLine, mTextPaint);
+ }
+
+ /**
+ * 获取字体的宽
+ * @param text text
+ * @return return
+ */
+ private float getTextWidth(String text) {
+ return mTextPaint.measureText(text);
+ }
+
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected) {
+ int cx = x + mItemWidth / 2;
+ int top = -mItemHeight / 6;
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ mSelectTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10, mSelectedLunarTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint :
+ mSchemeLunarTextPaint);
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10,
+ calendar.isCurrentDay() ? mCurDayLunarTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthLunarTextPaint : mOtherMonthLunarTextPaint);
+ }
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/DefaultYearView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/DefaultYearView.java
new file mode 100644
index 00000000..2396baac
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/DefaultYearView.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.graphics.Canvas;
+
+/**
+ * 默认年视图
+ * Created by huanghaibin on 2018/10/9.
+ */
+
+public class DefaultYearView extends YearView {
+
+ private int mTextPadding;
+
+ public DefaultYearView(Context context) {
+ super(context);
+ mTextPadding = CalendarUtil.dipToPx(context, 3);
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawMonth(Canvas canvas, int year, int month, int x, int y, int width, int height) {
+
+ String text = getContext()
+ .getResources()
+ .getStringArray(R.array.month_string_array)[month - 1];
+
+ canvas.drawText(text,
+ x + mItemWidth / 2 - mTextPadding,
+ y + mMonthTextBaseLine,
+ mMonthTextPaint);
+ }
+
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ @Override
+ protected void onDrawWeek(Canvas canvas, int week, int x, int y, int width, int height) {
+ String text = getContext().getResources().getStringArray(R.array.year_view_week_string_array)[week];
+ canvas.drawText(text,
+ x + width / 2,
+ y + mWeekTextBaseLine,
+ mWeekTextPaint);
+ }
+
+
+ @Override
+ protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
+ return false;
+ }
+
+ @Override
+ protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
+
+ }
+
+ @Override
+ protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
+ float baselineY = mTextBaseLine + y;
+ int cx = x + mItemWidth / 2;
+
+ if (isSelected) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ hasScheme ? mSchemeTextPaint : mSelectTextPaint);
+ } else if (hasScheme) {
+ canvas.drawText(String.valueOf(calendar.getDay()),
+ cx,
+ baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
+
+ } else {
+ canvas.drawText(String.valueOf(calendar.getDay()), cx, baselineY,
+ calendar.isCurrentDay() ? mCurDayTextPaint :
+ calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);
+ }
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/LunarCalendar.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/LunarCalendar.java
new file mode 100644
index 00000000..f4da664d
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/LunarCalendar.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.text.TextUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 农历计算相关
+ */
+@SuppressWarnings("all")
+public final class LunarCalendar {
+
+
+ static void init(Context context) {
+ if (MONTH_STR != null) {
+ return;
+ }
+ TrunkBranchAnnals.init(context);
+ SolarTermUtil.init(context);
+ MONTH_STR = context.getResources().getStringArray(R.array.lunar_first_of_month);
+ TRADITION_FESTIVAL_STR = context.getResources().getStringArray(R.array.tradition_festival);
+ DAY_STR = context.getResources().getStringArray(R.array.lunar_str);
+ SPECIAL_FESTIVAL_STR = context.getResources().getStringArray(R.array.special_festivals);
+ SOLAR_CALENDAR = context.getResources().getStringArray(R.array.solar_festival);
+ }
+
+ /**
+ * 农历月份第一天转写
+ */
+ private static String[] MONTH_STR = null;
+
+ /**
+ * 传统农历节日
+ */
+ private static String[] TRADITION_FESTIVAL_STR = null;
+
+ /**
+ * 农历大写
+ */
+ private static String[] DAY_STR = null;
+
+ /**
+ * 特殊节日的数组
+ */
+ private static String[] SPECIAL_FESTIVAL_STR = null;
+
+ /**
+ * 特殊节日、母亲节和父亲节,感恩节等
+ */
+ @SuppressLint("UseSparseArrays")
+ private static final Map SPECIAL_FESTIVAL = new HashMap<>();
+
+ /**
+ * 公历节日
+ */
+ private static String[] SOLAR_CALENDAR = null;
+
+ /**
+ * 保存每年24节气
+ */
+ @SuppressLint("UseSparseArrays")
+ private static final Map SOLAR_TERMS = new HashMap<>();
+
+ /**
+ * 返回传统农历节日
+ *
+ * @param year 农历年
+ * @param month 农历月
+ * @param day 农历日
+ * @return 返回传统农历节日
+ */
+ private static String getTraditionFestival(int year, int month, int day) {
+ if (month == 12) {
+ int count = daysInLunarMonth(year, month);
+ if (day == count) {
+ return TRADITION_FESTIVAL_STR[0];//除夕
+ }
+ }
+ String text = getString(month, day);
+ String festivalStr = "";
+ for (String festival : TRADITION_FESTIVAL_STR) {
+ if (festival.contains(text)) {
+ festivalStr = festival.replace(text, "");
+ break;
+ }
+ }
+ return festivalStr;
+ }
+
+
+ /**
+ * 数字转换为汉字月份
+ *
+ * @param month 月
+ * @param leap 1==闰月
+ * @return 数字转换为汉字月份
+ */
+ private static String numToChineseMonth(int month, int leap) {
+ if (leap == 1) {
+ return "闰" + MONTH_STR[month - 1];
+ }
+ return MONTH_STR[month - 1];
+ }
+
+ /**
+ * 数字转换为农历节日或者日期
+ *
+ * @param month 月
+ * @param day 日
+ * @param leap 1==闰月
+ * @return 数字转换为汉字日
+ */
+ private static String numToChinese(int month, int day, int leap) {
+ if (day == 1) {
+ return numToChineseMonth(month, leap);
+ }
+ return DAY_STR[day - 1];
+ }
+
+ /**
+ * 用来表示1900年到2099年间农历年份的相关信息,共24位bit的16进制表示,其中:
+ * 1. 前4位表示该年闰哪个月;
+ * 2. 5-17位表示农历年份13个月的大小月分布,0表示小,1表示大;
+ * 3. 最后7位表示农历年首(正月初一)对应的公历日期。
+ *
+ * 以2014年的数据0x955ABF为例说明:
+ * 1001 0101 0101 1010 1011 1111
+ * 闰九月 农历正月初一对应公历1月31号
+ */
+ private static final int[] LUNAR_INFO = {
+ 0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,//1900-1909
+ 0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,//1910-1919
+ 0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970,//1920-1929
+ 0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,//1930-1939
+ 0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,//1940-1949
+ 0x06ca0,0x0b550,0x15355,0x04da0,0x0a5b0,0x14573,0x052b0,0x0a9a8,0x0e950,0x06aa0,//1950-1959
+ 0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,0x05260,0x0f263,0x0d950,0x05b57,0x056a0,//1960-1969
+ 0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,0x0d250,0x0d558,0x0b540,0x0b6a0,0x195a6,//1970-1979
+ 0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,//1980-1989
+ 0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,0x06b58,0x055c0,0x0ab60,0x096d5,0x092e0,//1990-1999
+ 0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,//2000-2009
+ 0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,//2010-2019
+ 0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,//2020-2029
+ 0x05aa0,0x076a3,0x096d0,0x04afb,0x04ad0,0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,//2030-2039
+ 0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0,//2040-2049
+ 0x14b63,0x09370,0x049f8,0x04970,0x064b0,0x168a6,0x0ea50, 0x06b20,0x1a6c4,0x0aae0,//2050-2059
+ 0x0a2e0,0x0d2e3,0x0c960,0x0d557,0x0d4a0,0x0da50,0x05d55,0x056a0,0x0a6d0,0x055d4,//2060-2069
+ 0x052d0,0x0a9b8,0x0a950,0x0b4a0,0x0b6a6,0x0ad50,0x055a0,0x0aba4,0x0a5b0,0x052b0,//2070-2079
+ 0x0b273,0x06930,0x07337,0x06aa0,0x0ad50,0x14b55,0x04b60,0x0a570,0x054e4,0x0d160,//2080-2089
+ 0x0e968,0x0d520,0x0daa0,0x16aa6,0x056d0,0x04ae0,0x0a9d4,0x0a2d0,0x0d150,0x0f252,//2090-2099
+ 0x0d520
+ };
+
+
+ /**
+ * 农历 year年month月的总天数,总共有13个月包括闰月
+ *
+ * @param year 将要计算的年份
+ * @param month 将要计算的月份
+ * @return 传回农历 year年month月的总天数
+ */
+ public static int daysInLunarMonth(int year, int month) {
+ if ((LUNAR_INFO[year - CalendarViewDelegate.MIN_YEAR] & (0x10000 >> month)) == 0)
+ return 29;
+ else
+ return 30;
+ }
+
+ /**
+ * 获取公历节日
+ *
+ * @param month 公历月份
+ * @param day 公历日期
+ * @return 公历节日
+ */
+ private static String gregorianFestival(int month, int day) {
+ String text = getString(month, day);
+ String solar = "";
+ for (String aMSolarCalendar : SOLAR_CALENDAR) {
+ if (aMSolarCalendar.contains(text)) {
+ solar = aMSolarCalendar.replace(text, "");
+ break;
+ }
+ }
+ return solar;
+ }
+
+ private static String getString(int month, int day) {
+ return (month >= 10 ? String.valueOf(month) : "0" + month) + (day >= 10 ? day : "0" + day);
+ }
+
+
+ /**
+ * 返回24节气
+ *
+ * @param year 年
+ * @param month 月
+ * @param day 日
+ * @return 返回24节气
+ */
+ private static String getSolarTerm(int year, int month, int day) {
+ if (!SOLAR_TERMS.containsKey(year)) {
+ SOLAR_TERMS.put(year, SolarTermUtil.getSolarTerms(year));
+ }
+ String[] solarTerm = SOLAR_TERMS.get(year);
+ String text = year + getString(month, day);
+ String solar = "";
+ assert solarTerm != null;
+ for (String solarTermName : solarTerm) {
+ if (solarTermName.contains(text)) {
+ solar = solarTermName.replace(text, "");
+ break;
+ }
+ }
+ return solar;
+ }
+
+
+ /**
+ * 获取农历节日
+ *
+ * @param year 年
+ * @param month 月
+ * @param day 日
+ * @return 农历节日
+ */
+ public static String getLunarText(int year, int month, int day) {
+ String termText = LunarCalendar.getSolarTerm(year, month, day);
+ String solar = LunarCalendar.gregorianFestival(month, day);
+ if (!TextUtils.isEmpty(solar))
+ return solar;
+ if (!TextUtils.isEmpty(termText))
+ return termText;
+ int[] lunar = LunarUtil.solarToLunar(year, month, day);
+ String festival = getTraditionFestival(lunar[0], lunar[1], lunar[2]);
+ if (!TextUtils.isEmpty(festival))
+ return festival;
+ return LunarCalendar.numToChinese(lunar[1], lunar[2], lunar[3]);
+ }
+
+
+ /**
+ * 获取特殊计算方式的节日
+ * 如:每年五月的第二个星期日为母亲节,六月的第三个星期日为父亲节
+ * 每年11月第四个星期四定为"感恩节"
+ *
+ * @param year year
+ * @param month month
+ * @param day day
+ * @return 获取西方节日
+ */
+ private static String getSpecialFestival(int year, int month, int day) {
+ if (!SPECIAL_FESTIVAL.containsKey(year)) {
+ SPECIAL_FESTIVAL.put(year, getSpecialFestivals(year));
+ }
+ String[] specialFestivals = SPECIAL_FESTIVAL.get(year);
+ String text = year + getString(month, day);
+ String solar = "";
+ assert specialFestivals != null;
+ for (String special : specialFestivals) {
+ if (special.contains(text)) {
+ solar = special.replace(text, "");
+ break;
+ }
+ }
+ return solar;
+ }
+
+
+ /**
+ * 获取每年的母亲节和父亲节和感恩节
+ * 特殊计算方式的节日
+ *
+ * @param year 年
+ * @return 获取每年的母亲节和父亲节、感恩节
+ */
+ private static String[] getSpecialFestivals(int year) {
+ String[] festivals = new String[3];
+ java.util.Calendar date = java.util.Calendar.getInstance();
+ date.set(year, 4, 1);
+ int week = date.get(java.util.Calendar.DAY_OF_WEEK);
+ int startDiff = 7 - week + 1;
+ if (startDiff == 7) {
+ festivals[0] = dateToString(year, 5, startDiff + 1) + SPECIAL_FESTIVAL_STR[0];
+ } else {
+ festivals[0] = dateToString(year, 5, startDiff + 7 + 1) + SPECIAL_FESTIVAL_STR[0];
+ }
+ date.set(year, 5, 1);
+ week = date.get(java.util.Calendar.DAY_OF_WEEK);
+ startDiff = 7 - week + 1;
+ if (startDiff == 7) {
+ festivals[1] = dateToString(year, 6, startDiff + 7 + 1) + SPECIAL_FESTIVAL_STR[1];
+ } else {
+ festivals[1] = dateToString(year, 6, startDiff + 7 + 7 + 1) + SPECIAL_FESTIVAL_STR[1];
+ }
+
+ date.set(year, 10, 1);
+ week = date.get(java.util.Calendar.DAY_OF_WEEK);
+ startDiff = 7 - week + 1;
+ if (startDiff <= 2) {
+ festivals[2] = dateToString(year, 11, startDiff + 21 + 5) + SPECIAL_FESTIVAL_STR[2];
+ } else {
+ festivals[2] = dateToString(year, 11, startDiff + 14 + 5) + SPECIAL_FESTIVAL_STR[2];
+ }
+ return festivals;
+ }
+
+
+ private static String dateToString(int year, int month, int day) {
+ return year + getString(month, day);
+ }
+
+ /**
+ * 初始化各种农历、节日
+ *
+ * @param calendar calendar
+ */
+ public static void setupLunarCalendar(Calendar calendar) {
+ int year = calendar.getYear();
+ int month = calendar.getMonth();
+ int day = calendar.getDay();
+ calendar.setWeekend(CalendarUtil.isWeekend(calendar));
+ calendar.setWeek(CalendarUtil.getWeekFormCalendar(calendar));
+
+ Calendar lunarCalendar = new Calendar();
+ calendar.setLunarCalendar(lunarCalendar);
+ int[] lunar = LunarUtil.solarToLunar(year, month, day);
+ lunarCalendar.setYear(lunar[0]);
+ lunarCalendar.setMonth(lunar[1]);
+ lunarCalendar.setDay(lunar[2]);
+ calendar.setLeapYear(CalendarUtil.isLeapYear(year));
+ if (lunar[3] == 1) {//如果是闰月
+ calendar.setLeapMonth(lunar[1]);
+ lunarCalendar.setLeapMonth(lunar[1]);
+ }
+ String solarTerm = LunarCalendar.getSolarTerm(year, month, day);
+ String gregorian = LunarCalendar.gregorianFestival(month, day);
+ String festival = getTraditionFestival(lunar[0], lunar[1], lunar[2]);
+ String lunarText = LunarCalendar.numToChinese(lunar[1], lunar[2], lunar[3]);
+ if (TextUtils.isEmpty(gregorian)) {
+ gregorian = getSpecialFestival(year, month, day);
+ }
+ calendar.setSolarTerm(solarTerm);
+ calendar.setGregorianFestival(gregorian);
+ calendar.setTraditionFestival(festival);
+ lunarCalendar.setTraditionFestival(festival);
+ lunarCalendar.setSolarTerm(solarTerm);
+ if (!TextUtils.isEmpty(solarTerm)) {
+ calendar.setLunar(solarTerm);
+ } else if (!TextUtils.isEmpty(gregorian)) {
+ calendar.setLunar(gregorian);
+ } else if (!TextUtils.isEmpty(festival)) {
+ calendar.setLunar(festival);
+ } else {
+ calendar.setLunar(lunarText);
+ }
+ lunarCalendar.setLunar(lunarText);
+ }
+
+ /**
+ * 获取农历节日
+ *
+ * @param calendar calendar
+ * @return 获取农历节日
+ */
+ public static String getLunarText(Calendar calendar) {
+ return getLunarText(calendar.getYear(), calendar.getMonth(), calendar.getDay());
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/LunarUtil.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/LunarUtil.java
new file mode 100644
index 00000000..9b9eee8a
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/LunarUtil.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+/**
+ * 农历计算方法
+ */
+public final class LunarUtil {
+
+
+ private static int[] LUNAR_MONTH_DAYS = {1887, 0x1694, 0x16aa, 0x4ad5, 0xab6, 0xc4b7, 0x4ae, 0xa56, 0xb52a, 0x1d2a,
+ 0xd54, 0x75aa, 0x156a, 0x1096d, 0x95c, 0x14ae, 0xaa4d, 0x1a4c, 0x1b2a, 0x8d55, 0xad4, 0x135a, 0x495d, 0x95c,
+ 0xd49b, 0x149a, 0x1a4a, 0xbaa5, 0x16a8, 0x1ad4, 0x52da, 0x12b6, 0xe937, 0x92e, 0x1496, 0xb64b, 0xd4a, 0xda8,
+ 0x95b5, 0x56c, 0x12ae, 0x492f, 0x92e, 0xcc96, 0x1a94, 0x1d4a, 0xada9, 0xb5a, 0x56c, 0x726e, 0x125c, 0xf92d,
+ 0x192a, 0x1a94, 0xdb4a, 0x16aa, 0xad4, 0x955b, 0x4ba, 0x125a, 0x592b, 0x152a, 0xf695, 0xd94, 0x16aa, 0xaab5,
+ 0x9b4, 0x14b6, 0x6a57, 0xa56, 0x1152a, 0x1d2a, 0xd54, 0xd5aa, 0x156a, 0x96c, 0x94ae, 0x14ae, 0xa4c, 0x7d26,
+ 0x1b2a, 0xeb55, 0xad4, 0x12da, 0xa95d, 0x95a, 0x149a, 0x9a4d, 0x1a4a, 0x11aa5, 0x16a8, 0x16d4, 0xd2da,
+ 0x12b6, 0x936, 0x9497, 0x1496, 0x1564b, 0xd4a, 0xda8, 0xd5b4, 0x156c, 0x12ae, 0xa92f, 0x92e, 0xc96, 0x6d4a,
+ 0x1d4a, 0x10d65, 0xb58, 0x156c, 0xb26d, 0x125c, 0x192c, 0x9a95, 0x1a94, 0x1b4a, 0x4b55, 0xad4, 0xf55b,
+ 0x4ba, 0x125a, 0xb92b, 0x152a, 0x1694, 0x96aa, 0x15aa, 0x12ab5, 0x974, 0x14b6, 0xca57, 0xa56, 0x1526,
+ 0x8e95, 0xd54, 0x15aa, 0x49b5, 0x96c, 0xd4ae, 0x149c, 0x1a4c, 0xbd26, 0x1aa6, 0xb54, 0x6d6a, 0x12da,
+ 0x1695d, 0x95a, 0x149a, 0xda4b, 0x1a4a, 0x1aa4, 0xbb54, 0x16b4, 0xada, 0x495b, 0x936, 0xf497, 0x1496,
+ 0x154a, 0xb6a5, 0xda4, 0x15b4, 0x6ab6, 0x126e, 0x1092f, 0x92e, 0xc96, 0xcd4a, 0x1d4a, 0xd64, 0x956c, 0x155c,
+ 0x125c, 0x792e, 0x192c, 0xfa95, 0x1a94, 0x1b4a, 0xab55, 0xad4, 0x14da, 0x8a5d, 0xa5a, 0x1152b, 0x152a,
+ 0x1694, 0xd6aa, 0x15aa, 0xab4, 0x94ba, 0x14b6, 0xa56, 0x7527, 0xd26, 0xee53, 0xd54, 0x15aa, 0xa9b5, 0x96c,
+ 0x14ae, 0x8a4e, 0x1a4c, 0x11d26, 0x1aa4, 0x1b54, 0xcd6a, 0xada, 0x95c, 0x949d, 0x149a, 0x1a2a, 0x5b25,
+ 0x1aa4, 0xfb52, 0x16b4, 0xaba, 0xa95b, 0x936, 0x1496, 0x9a4b, 0x154a, 0x136a5, 0xda4, 0x15ac};
+
+ private static int[] SOLAR = {1887, 0xec04c, 0xec23f, 0xec435, 0xec649, 0xec83e, 0xeca51, 0xecc46, 0xece3a,
+ 0xed04d, 0xed242, 0xed436, 0xed64a, 0xed83f, 0xeda53, 0xedc48, 0xede3d, 0xee050, 0xee244, 0xee439, 0xee64d,
+ 0xee842, 0xeea36, 0xeec4a, 0xeee3e, 0xef052, 0xef246, 0xef43a, 0xef64e, 0xef843, 0xefa37, 0xefc4b, 0xefe41,
+ 0xf0054, 0xf0248, 0xf043c, 0xf0650, 0xf0845, 0xf0a38, 0xf0c4d, 0xf0e42, 0xf1037, 0xf124a, 0xf143e, 0xf1651,
+ 0xf1846, 0xf1a3a, 0xf1c4e, 0xf1e44, 0xf2038, 0xf224b, 0xf243f, 0xf2653, 0xf2848, 0xf2a3b, 0xf2c4f, 0xf2e45,
+ 0xf3039, 0xf324d, 0xf3442, 0xf3636, 0xf384a, 0xf3a3d, 0xf3c51, 0xf3e46, 0xf403b, 0xf424e, 0xf4443, 0xf4638,
+ 0xf484c, 0xf4a3f, 0xf4c52, 0xf4e48, 0xf503c, 0xf524f, 0xf5445, 0xf5639, 0xf584d, 0xf5a42, 0xf5c35, 0xf5e49,
+ 0xf603e, 0xf6251, 0xf6446, 0xf663b, 0xf684f, 0xf6a43, 0xf6c37, 0xf6e4b, 0xf703f, 0xf7252, 0xf7447, 0xf763c,
+ 0xf7850, 0xf7a45, 0xf7c39, 0xf7e4d, 0xf8042, 0xf8254, 0xf8449, 0xf863d, 0xf8851, 0xf8a46, 0xf8c3b, 0xf8e4f,
+ 0xf9044, 0xf9237, 0xf944a, 0xf963f, 0xf9853, 0xf9a47, 0xf9c3c, 0xf9e50, 0xfa045, 0xfa238, 0xfa44c, 0xfa641,
+ 0xfa836, 0xfaa49, 0xfac3d, 0xfae52, 0xfb047, 0xfb23a, 0xfb44e, 0xfb643, 0xfb837, 0xfba4a, 0xfbc3f, 0xfbe53,
+ 0xfc048, 0xfc23c, 0xfc450, 0xfc645, 0xfc839, 0xfca4c, 0xfcc41, 0xfce36, 0xfd04a, 0xfd23d, 0xfd451, 0xfd646,
+ 0xfd83a, 0xfda4d, 0xfdc43, 0xfde37, 0xfe04b, 0xfe23f, 0xfe453, 0xfe648, 0xfe83c, 0xfea4f, 0xfec44, 0xfee38,
+ 0xff04c, 0xff241, 0xff436, 0xff64a, 0xff83e, 0xffa51, 0xffc46, 0xffe3a, 0x10004e, 0x100242, 0x100437,
+ 0x10064b, 0x100841, 0x100a53, 0x100c48, 0x100e3c, 0x10104f, 0x101244, 0x101438, 0x10164c, 0x101842,
+ 0x101a35, 0x101c49, 0x101e3d, 0x102051, 0x102245, 0x10243a, 0x10264e, 0x102843, 0x102a37, 0x102c4b,
+ 0x102e3f, 0x103053, 0x103247, 0x10343b, 0x10364f, 0x103845, 0x103a38, 0x103c4c, 0x103e42, 0x104036,
+ 0x104249, 0x10443d, 0x104651, 0x104846, 0x104a3a, 0x104c4e, 0x104e43, 0x105038, 0x10524a, 0x10543e,
+ 0x105652, 0x105847, 0x105a3b, 0x105c4f, 0x105e45, 0x106039, 0x10624c, 0x106441, 0x106635, 0x106849,
+ 0x106a3d, 0x106c51, 0x106e47, 0x10703c, 0x10724f, 0x107444, 0x107638, 0x10784c, 0x107a3f, 0x107c53,
+ 0x107e48};
+
+ private static int getBitInt(int data, int length, int shift) {
+ return (data & (((1 << length) - 1) << shift)) >> shift;
+ }
+
+ private static long solarToInt(int y, int m, int d) {
+ m = (m + 9) % 12;
+ y = y - m / 10;
+ return 365 * y + y / 4 - y / 100 + y / 400 + (m * 306 + 5) / 10 + (d - 1);
+ }
+
+ private static int[] solarFromInt(long g) {
+ long y = (10000 * g + 14780) / 3652425;
+ long ddd = g - (365 * y + y / 4 - y / 100 + y / 400);
+ if (ddd < 0) {
+ y--;
+ ddd = g - (365 * y + y / 4 - y / 100 + y / 400);
+ }
+ long mi = (100 * ddd + 52) / 3060;
+ long mm = (mi + 2) % 12 + 1;
+ y = y + (mi + 2) / 12;
+ long dd = ddd - (mi * 306 + 5) / 10 + 1;
+ int[] solar = new int[4];
+ solar[0] = (int) y;
+ solar[1] = (int) mm;
+ solar[2] = (int) dd;
+ return solar;
+ }
+
+ /**
+ * 公历转农历 Solar To Lunar
+ *
+ * @param year 公历年
+ * @param month 公历月
+ * @param day 公历日
+ * @return [0]农历年 [1]农历月 [2]农历日 [3]是否闰月 0 false : 1 true
+ */
+ @SuppressWarnings("all")
+ public static int[] solarToLunar(int year, int month, int day) {
+ int[] lunarInt = new int[4];
+ int index = year - SOLAR[0];
+ int data = (year << 9) | (month << 5) | (day);
+ int solar11;
+ if (SOLAR[index] > data) {
+ index--;
+ }
+ solar11 = SOLAR[index];
+ int y = getBitInt(solar11, 12, 9);
+ int m = getBitInt(solar11, 4, 5);
+ int d = getBitInt(solar11, 5, 0);
+ long offset = solarToInt(year, month, day) - solarToInt(y, m, d);
+
+ int days = LUNAR_MONTH_DAYS[index];
+ int leap = getBitInt(days, 4, 13);
+
+ int lunarY = index + SOLAR[0];
+ int lunarM = 1;
+ int lunarD;
+ offset += 1;
+
+ for (int i = 0; i < 13; i++) {
+ int dm = getBitInt(days, 1, 12 - i) == 1 ? 30 : 29;
+ if (offset > dm) {
+ lunarM++;
+ offset -= dm;
+ } else {
+ break;
+ }
+ }
+ lunarD = (int) (offset);
+ lunarInt[0] = lunarY;
+ lunarInt[1] = lunarM;
+ lunarInt[3] = 0;
+
+ if (leap != 0 && lunarM > leap) {
+ lunarInt[1] = lunarM - 1;
+ if (lunarM == leap + 1) {
+ lunarInt[3] = 1;
+ }
+ }
+ lunarInt[2] = lunarD;
+ return lunarInt;
+ }
+
+
+ /**
+ * 农历转公历
+ *
+ * @param lunarYear 农历年
+ * @param lunarMonth 农历月
+ * @param lunarDay 农历天
+ * @param isLeap 是否是闰年 0 false : 1 true
+ * @return [0]新历年 [1]新历月 [2]新历日 [3]是否闰月 0 false : 1 true
+ */
+ @SuppressWarnings("unused")
+ public static int[] lunarToSolar(int lunarYear, int lunarMonth, int lunarDay, boolean isLeap) {
+ int days = LUNAR_MONTH_DAYS[lunarYear - LUNAR_MONTH_DAYS[0]];
+ int leap = getBitInt(days, 4, 13);
+ int offset = 0;
+ int loop = leap;
+ if (!isLeap) {
+ if (lunarMonth <= leap || leap == 0) {
+ loop = lunarMonth - 1;
+ } else {
+ loop = lunarMonth;
+ }
+ }
+ for (int i = 0; i < loop; i++) {
+ offset += getBitInt(days, 1, 12 - i) == 1 ? 30 : 29;
+ }
+ offset += lunarDay;
+
+ int solar11 = SOLAR[lunarYear - SOLAR[0]];
+
+ int y = getBitInt(solar11, 12, 9);
+ int m = getBitInt(solar11, 4, 5);
+ int d = getBitInt(solar11, 5, 0);
+
+ return solarFromInt(solarToInt(y, m, d) + offset - 1);
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/Month.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/Month.java
new file mode 100644
index 00000000..59095301
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/Month.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import java.io.Serializable;
+
+@SuppressWarnings("unused")
+final class Month implements Serializable {
+ private int diff;//日期偏移
+ private int count;
+ private int month;
+ private int year;
+
+ int getDiff() {
+ return diff;
+ }
+
+ void setDiff(int diff) {
+ this.diff = diff;
+ }
+
+ int getCount() {
+ return count;
+ }
+
+ void setCount(int count) {
+ this.count = count;
+ }
+
+ int getMonth() {
+ return month;
+ }
+
+ void setMonth(int month) {
+ this.month = month;
+ }
+
+ int getYear() {
+ return year;
+ }
+
+ void setYear(int year) {
+ this.year = year;
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/MonthView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/MonthView.java
new file mode 100644
index 00000000..8b7e90d5
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/MonthView.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.view.View;
+
+/**
+ * 月视图基础控件,可自由继承实现
+ * Created by huanghaibin on 2017/11/15.
+ */
+public abstract class MonthView extends BaseMonthView {
+
+ public MonthView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mLineCount == 0)
+ return;
+ mItemWidth = (getWidth() -
+ mDelegate.getCalendarPaddingLeft() -
+ mDelegate.getCalendarPaddingRight()) / 7;
+ onPreviewHook();
+ int count = mLineCount * 7;
+ int d = 0;
+ for (int i = 0; i < mLineCount; i++) {
+ for (int j = 0; j < 7; j++) {
+ Calendar calendar = mItems.get(d);
+ if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ONLY_CURRENT_MONTH) {
+ if (d > mItems.size() - mNextDiff) {
+ return;
+ }
+ if (!calendar.isCurrentMonth()) {
+ ++d;
+ continue;
+ }
+ } else if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_FIT_MONTH) {
+ if (d >= count) {
+ return;
+ }
+ }
+ draw(canvas, calendar, i, j, d);
+ ++d;
+ }
+ }
+ }
+
+
+ /**
+ * 开始绘制
+ *
+ * @param canvas canvas
+ * @param calendar 对应日历
+ * @param i i
+ * @param j j
+ * @param d d
+ */
+ private void draw(Canvas canvas, Calendar calendar, int i, int j, int d) {
+ int x = j * mItemWidth + mDelegate.getCalendarPaddingLeft();
+ int y = i * mItemHeight;
+ onLoopStart(x, y);
+ boolean isSelected = d == mCurrentItem;
+ boolean hasScheme = calendar.hasScheme();
+
+ if (hasScheme) {
+ //标记的日子
+ boolean isDrawSelected = false;//是否继续绘制选中的onDrawScheme
+ if (isSelected) {
+ isDrawSelected = onDrawSelected(canvas, calendar, x, y, true);
+ }
+ if (isDrawSelected || !isSelected) {
+ //将画笔设置为标记颜色
+ mSchemePaint.setColor(calendar.getSchemeColor() != 0 ? calendar.getSchemeColor() : mDelegate.getSchemeThemeColor());
+ onDrawScheme(canvas, calendar, x, y);
+ }
+ } else {
+ if (isSelected) {
+ onDrawSelected(canvas, calendar, x, y, false);
+ }
+ }
+ onDrawText(canvas, calendar, x, y, hasScheme, isSelected);
+ }
+
+
+ @Override
+ public void onClick(View v) {
+ if (!isClick) {
+ return;
+ }
+ Calendar calendar = getIndex();
+
+ if (calendar == null) {
+ return;
+ }
+
+ if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ONLY_CURRENT_MONTH &&
+ !calendar.isCurrentMonth()) {
+ return;
+ }
+
+ if (onCalendarIntercept(calendar)) {
+ mDelegate.mCalendarInterceptListener.onCalendarInterceptClick(calendar, true);
+ return;
+ }
+
+
+ if (!isInRange(calendar)) {
+ if (mDelegate.mCalendarSelectListener != null) {
+ mDelegate.mCalendarSelectListener.onCalendarOutOfRange(calendar);
+ }
+ return;
+ }
+
+ mCurrentItem = mItems.indexOf(calendar);
+
+ if (!calendar.isCurrentMonth() && mMonthViewPager != null) {
+ int cur = mMonthViewPager.getCurrentItem();
+ int position = mCurrentItem < 7 ? cur - 1 : cur + 1;
+ mMonthViewPager.setCurrentItem(position);
+ }
+
+ if (mDelegate.mInnerListener != null) {
+ mDelegate.mInnerListener.onMonthDateSelected(calendar, true);
+ }
+
+ if (mParentLayout != null) {
+ if (calendar.isCurrentMonth()) {
+ mParentLayout.updateSelectPosition(mItems.indexOf(calendar));
+ } else {
+ mParentLayout.updateSelectWeek(CalendarUtil.getWeekFromDayInMonth(calendar, mDelegate.getWeekStart()));
+ }
+
+ }
+
+ if (mDelegate.mCalendarSelectListener != null) {
+ mDelegate.mCalendarSelectListener.onCalendarSelect(calendar, true);
+ }
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (mDelegate.mCalendarLongClickListener == null)
+ return false;
+ if (!isClick) {
+ return false;
+ }
+ Calendar calendar = getIndex();
+ if (calendar == null) {
+ return false;
+ }
+
+ if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ONLY_CURRENT_MONTH &&
+ !calendar.isCurrentMonth()) {
+ return false;
+ }
+
+
+ if (onCalendarIntercept(calendar)) {
+ mDelegate.mCalendarInterceptListener.onCalendarInterceptClick(calendar, true);
+ return false;
+ }
+
+ boolean isCalendarInRange = isInRange(calendar);
+
+ if (!isCalendarInRange) {
+ if (mDelegate.mCalendarLongClickListener != null) {
+ mDelegate.mCalendarLongClickListener.onCalendarLongClickOutOfRange(calendar);
+ }
+ return true;
+ }
+
+ if (mDelegate.isPreventLongPressedSelected()) {
+ if (mDelegate.mCalendarLongClickListener != null) {
+ mDelegate.mCalendarLongClickListener.onCalendarLongClick(calendar);
+ }
+ return true;
+ }
+
+
+ mCurrentItem = mItems.indexOf(calendar);
+
+ if (!calendar.isCurrentMonth() && mMonthViewPager != null) {
+ int cur = mMonthViewPager.getCurrentItem();
+ int position = mCurrentItem < 7 ? cur - 1 : cur + 1;
+ mMonthViewPager.setCurrentItem(position);
+ }
+
+ if (mDelegate.mInnerListener != null) {
+ mDelegate.mInnerListener.onMonthDateSelected(calendar, true);
+ }
+
+ if (mParentLayout != null) {
+ if (calendar.isCurrentMonth()) {
+ mParentLayout.updateSelectPosition(mItems.indexOf(calendar));
+ } else {
+ mParentLayout.updateSelectWeek(CalendarUtil.getWeekFromDayInMonth(calendar, mDelegate.getWeekStart()));
+ }
+
+ }
+
+ if (mDelegate.mCalendarSelectListener != null) {
+ mDelegate.mCalendarSelectListener.onCalendarSelect(calendar, true);
+ }
+
+ if (mDelegate.mCalendarLongClickListener != null) {
+ mDelegate.mCalendarLongClickListener.onCalendarLongClick(calendar);
+ }
+ invalidate();
+ return true;
+ }
+
+ /**
+ * 绘制选中的日期
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @return 是否绘制onDrawScheme,true or false
+ */
+ protected abstract boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme);
+
+ /**
+ * 绘制标记的日期,这里可以是背景色,标记色什么的
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ */
+ protected abstract void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y);
+
+
+ /**
+ * 绘制日历文本
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme 是否是标记的日期
+ * @param isSelected 是否选中
+ */
+ protected abstract void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected);
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/MonthViewPager.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/MonthViewPager.java
new file mode 100644
index 00000000..904eeed6
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/MonthViewPager.java
@@ -0,0 +1,646 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+import java.lang.reflect.Constructor;
+import java.util.List;
+
+
+/**
+ * 月份切换ViewPager,自定义适应高度
+ */
+public final class MonthViewPager extends ViewPager {
+
+ private boolean isUpdateMonthView;
+
+ private int mMonthCount;
+
+ private CalendarViewDelegate mDelegate;
+
+ private int mNextViewHeight, mPreViewHeight, mCurrentViewHeight;
+
+ CalendarLayout mParentLayout;
+
+ WeekViewPager mWeekPager;
+
+ WeekBar mWeekBar;
+
+ /**
+ * 是否使用滚动到某一天
+ */
+ private boolean isUsingScrollToCalendar = false;
+
+ public MonthViewPager(Context context) {
+ this(context, null);
+ }
+
+ public MonthViewPager(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ /**
+ * 初始化
+ *
+ * @param delegate delegate
+ */
+ void setup(CalendarViewDelegate delegate) {
+ this.mDelegate = delegate;
+
+ updateMonthViewHeight(mDelegate.getCurrentDay().getYear(),
+ mDelegate.getCurrentDay().getMonth());
+
+ ViewGroup.LayoutParams params = getLayoutParams();
+ params.height = mCurrentViewHeight;
+ setLayoutParams(params);
+ init();
+ }
+
+ /**
+ * 初始化
+ */
+ private void init() {
+ mMonthCount = 12 * (mDelegate.getMaxYear() - mDelegate.getMinYear())
+ - mDelegate.getMinYearMonth() + 1 +
+ mDelegate.getMaxYearMonth();
+ setAdapter(new MonthViewPagerAdapter());
+ addOnPageChangeListener(new OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ALL_MONTH) {
+ return;
+ }
+ int height;
+ if (position < getCurrentItem()) {//右滑-1
+ height = (int) ((mPreViewHeight)
+ * (1 - positionOffset) +
+ mCurrentViewHeight
+ * positionOffset);
+ } else {//左滑+!
+ height = (int) ((mCurrentViewHeight)
+ * (1 - positionOffset) +
+ (mNextViewHeight)
+ * positionOffset);
+ }
+ ViewGroup.LayoutParams params = getLayoutParams();
+ params.height = height;
+ setLayoutParams(params);
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ Calendar calendar = CalendarUtil.getFirstCalendarFromMonthViewPager(position, mDelegate);
+ if (getVisibility() == VISIBLE) {
+ if (!mDelegate.isShowYearSelectedLayout &&
+ mDelegate.mIndexCalendar != null &&
+ calendar.getYear() != mDelegate.mIndexCalendar.getYear() &&
+ mDelegate.mYearChangeListener != null) {
+ mDelegate.mYearChangeListener.onYearChange(calendar.getYear());
+ }
+ mDelegate.mIndexCalendar = calendar;
+ }
+ //月份改变事件
+ if (mDelegate.mMonthChangeListener != null) {
+ mDelegate.mMonthChangeListener.onMonthChange(calendar.getYear(), calendar.getMonth());
+ }
+
+ //周视图显示的时候就需要动态改变月视图高度
+ if (mWeekPager.getVisibility() == VISIBLE) {
+ updateMonthViewHeight(calendar.getYear(), calendar.getMonth());
+ return;
+ }
+
+
+ if (mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_DEFAULT) {
+ if (!calendar.isCurrentMonth()) {
+ mDelegate.mSelectedCalendar = calendar;
+ } else {
+ mDelegate.mSelectedCalendar = CalendarUtil.getRangeEdgeCalendar(calendar, mDelegate);
+ }
+ mDelegate.mIndexCalendar = mDelegate.mSelectedCalendar;
+ } else {
+ if (mDelegate.mSelectedStartRangeCalendar != null &&
+ mDelegate.mSelectedStartRangeCalendar.isSameMonth(mDelegate.mIndexCalendar)) {
+ mDelegate.mIndexCalendar = mDelegate.mSelectedStartRangeCalendar;
+ } else {
+ if (calendar.isSameMonth(mDelegate.mSelectedCalendar)) {
+ mDelegate.mIndexCalendar = mDelegate.mSelectedCalendar;
+ }
+ }
+ }
+
+ mDelegate.updateSelectCalendarScheme();
+ if (!isUsingScrollToCalendar && mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_DEFAULT) {
+ mWeekBar.onDateSelected(mDelegate.mSelectedCalendar, mDelegate.getWeekStart(), false);
+ if (mDelegate.mCalendarSelectListener != null) {
+ mDelegate.mCalendarSelectListener.onCalendarSelect(mDelegate.mSelectedCalendar, false);
+ }
+ }
+
+ BaseMonthView view = findViewWithTag(position);
+ if (view != null) {
+ int index = view.getSelectedIndex(mDelegate.mIndexCalendar);
+ if (mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_DEFAULT) {
+ view.mCurrentItem = index;
+ }
+ if (index >= 0 && mParentLayout != null) {
+ mParentLayout.updateSelectPosition(index);
+ }
+ view.invalidate();
+ }
+ mWeekPager.updateSelected(mDelegate.mIndexCalendar, false);
+ updateMonthViewHeight(calendar.getYear(), calendar.getMonth());
+ isUsingScrollToCalendar = false;
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ });
+ }
+
+ /**
+ * 更新月视图的高度
+ *
+ * @param year year
+ * @param month month
+ */
+ private void updateMonthViewHeight(int year, int month) {
+ if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ALL_MONTH) {//非动态高度就不需要了
+ mCurrentViewHeight = 6 * mDelegate.getCalendarItemHeight();
+ ViewGroup.LayoutParams params = getLayoutParams();
+ params.height = mCurrentViewHeight;
+ return;
+ }
+
+ if (mParentLayout != null) {
+ if (getVisibility() != VISIBLE) {//如果已经显示周视图,则需要动态改变月视图高度,否则显示就有bug
+ ViewGroup.LayoutParams params = getLayoutParams();
+ params.height = CalendarUtil.getMonthViewHeight(year, month,
+ mDelegate.getCalendarItemHeight(), mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ setLayoutParams(params);
+ }
+ mParentLayout.updateContentViewTranslateY();
+ }
+ mCurrentViewHeight = CalendarUtil.getMonthViewHeight(year, month,
+ mDelegate.getCalendarItemHeight(), mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ if (month == 1) {
+ mPreViewHeight = CalendarUtil.getMonthViewHeight(year - 1, 12,
+ mDelegate.getCalendarItemHeight(), mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ mNextViewHeight = CalendarUtil.getMonthViewHeight(year, 2,
+ mDelegate.getCalendarItemHeight(), mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ } else {
+ mPreViewHeight = CalendarUtil.getMonthViewHeight(year, month - 1,
+ mDelegate.getCalendarItemHeight(), mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ if (month == 12) {
+ mNextViewHeight = CalendarUtil.getMonthViewHeight(year + 1, 1,
+ mDelegate.getCalendarItemHeight(), mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ } else {
+ mNextViewHeight = CalendarUtil.getMonthViewHeight(year, month + 1,
+ mDelegate.getCalendarItemHeight(), mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ }
+ }
+ }
+
+ /**
+ * 刷新
+ */
+ void notifyDataSetChanged() {
+ mMonthCount = 12 * (mDelegate.getMaxYear() - mDelegate.getMinYear())
+ - mDelegate.getMinYearMonth() + 1 +
+ mDelegate.getMaxYearMonth();
+ notifyAdapterDataSetChanged();
+ }
+
+ /**
+ * 更新月视图Class
+ */
+ void updateMonthViewClass() {
+ isUpdateMonthView = true;
+ notifyAdapterDataSetChanged();
+ isUpdateMonthView = false;
+ }
+
+ /**
+ * 更新日期范围
+ */
+ final void updateRange() {
+ isUpdateMonthView = true;
+ notifyDataSetChanged();
+ isUpdateMonthView = false;
+ if (getVisibility() != VISIBLE) {
+ return;
+ }
+ isUsingScrollToCalendar = false;
+ Calendar calendar = mDelegate.mSelectedCalendar;
+ int y = calendar.getYear() - mDelegate.getMinYear();
+ int position = 12 * y + calendar.getMonth() - mDelegate.getMinYearMonth();
+ setCurrentItem(position, false);
+ BaseMonthView view = findViewWithTag(position);
+ if (view != null) {
+ view.setSelectedCalendar(mDelegate.mIndexCalendar);
+ view.invalidate();
+ if (mParentLayout != null) {
+ mParentLayout.updateSelectPosition(view.getSelectedIndex(mDelegate.mIndexCalendar));
+ }
+ }
+ if (mParentLayout != null) {
+ int week = CalendarUtil.getWeekFromDayInMonth(calendar, mDelegate.getWeekStart());
+ mParentLayout.updateSelectWeek(week);
+ }
+
+
+ if (mDelegate.mInnerListener != null) {
+ mDelegate.mInnerListener.onMonthDateSelected(calendar, false);
+ }
+
+ if (mDelegate.mCalendarSelectListener != null) {
+ mDelegate.mCalendarSelectListener.onCalendarSelect(calendar, false);
+ }
+ updateSelected();
+ }
+
+ /**
+ * 滚动到指定日期
+ *
+ * @param year 年
+ * @param month 月
+ * @param day 日
+ * @param invokeListener 调用日期事件
+ */
+ void scrollToCalendar(int year, int month, int day, boolean smoothScroll, boolean invokeListener) {
+ isUsingScrollToCalendar = true;
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setCurrentDay(calendar.equals(mDelegate.getCurrentDay()));
+ LunarCalendar.setupLunarCalendar(calendar);
+ mDelegate.mIndexCalendar = calendar;
+ mDelegate.mSelectedCalendar = calendar;
+ mDelegate.updateSelectCalendarScheme();
+ int y = calendar.getYear() - mDelegate.getMinYear();
+ int position = 12 * y + calendar.getMonth() - mDelegate.getMinYearMonth();
+ int curItem = getCurrentItem();
+ if (curItem == position) {
+ isUsingScrollToCalendar = false;
+ }
+ setCurrentItem(position, smoothScroll);
+
+ BaseMonthView view = findViewWithTag(position);
+ if (view != null) {
+ view.setSelectedCalendar(mDelegate.mIndexCalendar);
+ view.invalidate();
+ if (mParentLayout != null) {
+ mParentLayout.updateSelectPosition(view.getSelectedIndex(mDelegate.mIndexCalendar));
+ }
+ }
+ if (mParentLayout != null) {
+ int week = CalendarUtil.getWeekFromDayInMonth(calendar, mDelegate.getWeekStart());
+ mParentLayout.updateSelectWeek(week);
+ }
+
+ if (mDelegate.mCalendarSelectListener != null && invokeListener) {
+ mDelegate.mCalendarSelectListener.onCalendarSelect(calendar, false);
+ }
+ if (mDelegate.mInnerListener != null) {
+ mDelegate.mInnerListener.onMonthDateSelected(calendar, false);
+ }
+
+ updateSelected();
+ }
+
+ /**
+ * 滚动到当前日期
+ */
+ void scrollToCurrent(boolean smoothScroll) {
+ isUsingScrollToCalendar = true;
+ int position = 12 * (mDelegate.getCurrentDay().getYear() - mDelegate.getMinYear()) +
+ mDelegate.getCurrentDay().getMonth() - mDelegate.getMinYearMonth();
+ int curItem = getCurrentItem();
+ if (curItem == position) {
+ isUsingScrollToCalendar = false;
+ }
+
+ setCurrentItem(position, smoothScroll);
+
+ BaseMonthView view = findViewWithTag(position);
+ if (view != null) {
+ view.setSelectedCalendar(mDelegate.getCurrentDay());
+ view.invalidate();
+ if (mParentLayout != null) {
+ mParentLayout.updateSelectPosition(view.getSelectedIndex(mDelegate.getCurrentDay()));
+ }
+ }
+
+ if (mDelegate.mCalendarSelectListener != null && getVisibility() == VISIBLE) {
+ mDelegate.mCalendarSelectListener.onCalendarSelect(mDelegate.mSelectedCalendar, false);
+ }
+ }
+
+ /**
+ * 获取当前月份数据
+ *
+ * @return 获取当前月份数据
+ */
+ List getCurrentMonthCalendars() {
+ BaseMonthView view = findViewWithTag(getCurrentItem());
+ if (view == null) {
+ return null;
+ }
+ return view.mItems;
+ }
+
+ /**
+ * 更新为默认选择模式
+ */
+ void updateDefaultSelect() {
+ BaseMonthView view = findViewWithTag(getCurrentItem());
+ if (view != null) {
+ int index = view.getSelectedIndex(mDelegate.mSelectedCalendar);
+ view.mCurrentItem = index;
+ if (index >= 0 && mParentLayout != null) {
+ mParentLayout.updateSelectPosition(index);
+ }
+ view.invalidate();
+ }
+ }
+
+
+ /**
+ * 更新选择效果
+ */
+ void updateSelected() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseMonthView view = (BaseMonthView) getChildAt(i);
+ view.setSelectedCalendar(mDelegate.mSelectedCalendar);
+ view.invalidate();
+ }
+ }
+
+ /**
+ * 更新字体颜色大小
+ */
+ final void updateStyle() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseMonthView view = (BaseMonthView) getChildAt(i);
+ view.updateStyle();
+ view.invalidate();
+ }
+ }
+
+ /**
+ * 更新标记日期
+ */
+ void updateScheme() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseMonthView view = (BaseMonthView) getChildAt(i);
+ view.update();
+ }
+ }
+
+ /**
+ * 更新当前日期,夜间过度的时候调用这个函数,一般不需要调用
+ */
+ void updateCurrentDate() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseMonthView view = (BaseMonthView) getChildAt(i);
+ view.updateCurrentDate();
+ }
+ }
+
+
+ /**
+ * 更新显示模式
+ */
+ void updateShowMode() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseMonthView view = (BaseMonthView) getChildAt(i);
+ view.updateShowMode();
+ view.requestLayout();
+ }
+ if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ALL_MONTH) {
+ mCurrentViewHeight = 6 * mDelegate.getCalendarItemHeight();
+ mNextViewHeight = mCurrentViewHeight;
+ mPreViewHeight = mCurrentViewHeight;
+ } else {
+ updateMonthViewHeight(mDelegate.mSelectedCalendar.getYear(), mDelegate.mSelectedCalendar.getMonth());
+ }
+ ViewGroup.LayoutParams params = getLayoutParams();
+ params.height = mCurrentViewHeight;
+ setLayoutParams(params);
+ if (mParentLayout != null) {
+ mParentLayout.updateContentViewTranslateY();
+ }
+ }
+
+ /**
+ * 更新周起始
+ */
+ void updateWeekStart() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseMonthView view = (BaseMonthView) getChildAt(i);
+ view.updateWeekStart();
+ view.requestLayout();
+ }
+
+ updateMonthViewHeight(mDelegate.mSelectedCalendar.getYear(), mDelegate.mSelectedCalendar.getMonth());
+ ViewGroup.LayoutParams params = getLayoutParams();
+ params.height = mCurrentViewHeight;
+ setLayoutParams(params);
+ if (mParentLayout != null) {
+ int i = CalendarUtil.getWeekFromDayInMonth(mDelegate.mSelectedCalendar, mDelegate.getWeekStart());
+ mParentLayout.updateSelectWeek(i);
+ }
+ updateSelected();
+ }
+
+ /**
+ * 更新高度
+ */
+ final void updateItemHeight() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseMonthView view = (BaseMonthView) getChildAt(i);
+ view.updateItemHeight();
+ view.requestLayout();
+ }
+
+ int year = mDelegate.mIndexCalendar.getYear();
+ int month = mDelegate.mIndexCalendar.getMonth();
+ mCurrentViewHeight = CalendarUtil.getMonthViewHeight(year, month,
+ mDelegate.getCalendarItemHeight(), mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ if (month == 1) {
+ mPreViewHeight = CalendarUtil.getMonthViewHeight(year - 1, 12,
+ mDelegate.getCalendarItemHeight(), mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ mNextViewHeight = CalendarUtil.getMonthViewHeight(year, 2,
+ mDelegate.getCalendarItemHeight(), mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ } else {
+ mPreViewHeight = CalendarUtil.getMonthViewHeight(year, month - 1,
+ mDelegate.getCalendarItemHeight(), mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ if (month == 12) {
+ mNextViewHeight = CalendarUtil.getMonthViewHeight(year + 1, 1,
+ mDelegate.getCalendarItemHeight(), mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ } else {
+ mNextViewHeight = CalendarUtil.getMonthViewHeight(year, month + 1,
+ mDelegate.getCalendarItemHeight(), mDelegate.getWeekStart(),
+ mDelegate.getMonthViewShowMode());
+ }
+ }
+ ViewGroup.LayoutParams params = getLayoutParams();
+ params.height = mCurrentViewHeight;
+ setLayoutParams(params);
+ }
+
+ /**
+ * 清除选择范围
+ */
+ final void clearSelectRange() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseMonthView view = (BaseMonthView) getChildAt(i);
+ view.invalidate();
+ }
+ }
+
+ /**
+ * 清除单选选择
+ */
+ final void clearSingleSelect() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseMonthView view = (BaseMonthView) getChildAt(i);
+ view.mCurrentItem = -1;
+ view.invalidate();
+ }
+ }
+
+ /**
+ * 清除单选选择
+ */
+ final void clearMultiSelect() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseMonthView view = (BaseMonthView) getChildAt(i);
+ view.mCurrentItem = -1;
+ view.invalidate();
+ }
+ }
+
+ private void notifyAdapterDataSetChanged() {
+ if (getAdapter() == null) {
+ return;
+ }
+ getAdapter().notifyDataSetChanged();
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return mDelegate.isMonthViewScrollable() && super.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return mDelegate.isMonthViewScrollable() && super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
+ public void setCurrentItem(int item) {
+ setCurrentItem(item, true);
+ }
+
+ @Override
+ public void setCurrentItem(int item, boolean smoothScroll) {
+ if (Math.abs(getCurrentItem() - item) > 1) {
+ super.setCurrentItem(item, false);
+ } else {
+ super.setCurrentItem(item, smoothScroll);
+ }
+ }
+
+ /**
+ * 日历卡月份Adapter
+ */
+ private final class MonthViewPagerAdapter extends PagerAdapter {
+
+ @Override
+ public int getCount() {
+ return mMonthCount;
+ }
+
+ @Override
+ public int getItemPosition(@NonNull Object object) {
+ return isUpdateMonthView ? POSITION_NONE : super.getItemPosition(object);
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, @NonNull Object object) {
+ return view.equals(object);
+ }
+
+ @NonNull
+ @Override
+ public Object instantiateItem(@NonNull ViewGroup container, int position) {
+ int year = (position + mDelegate.getMinYearMonth() - 1) / 12 + mDelegate.getMinYear();
+ int month = (position + mDelegate.getMinYearMonth() - 1) % 12 + 1;
+ BaseMonthView view;
+ try {
+ Constructor constructor = mDelegate.getMonthViewClass().getConstructor(Context.class);
+ view = (BaseMonthView) constructor.newInstance(getContext());
+ } catch (Exception e) {
+ e.printStackTrace();
+ return new DefaultMonthView(getContext());
+ }
+ view.mMonthViewPager = MonthViewPager.this;
+ view.mParentLayout = mParentLayout;
+ view.setup(mDelegate);
+ view.setTag(position);
+ view.initMonthWithDate(year, month);
+ view.setSelectedCalendar(mDelegate.mSelectedCalendar);
+ container.addView(view);
+ return view;
+ }
+
+ @Override
+ public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
+ BaseView view = (BaseView) object;
+ view.onDestroy();
+ container.removeView(view);
+ }
+ }
+
+
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/MultiMonthView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/MultiMonthView.java
new file mode 100644
index 00000000..3973694c
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/MultiMonthView.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.view.View;
+
+/**
+ * 多选月视图
+ * Created by huanghaibin on 2018/9/11.
+ */
+public abstract class MultiMonthView extends BaseMonthView {
+
+ public MultiMonthView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mLineCount == 0)
+ return;
+
+ mItemWidth = (getWidth() -
+ mDelegate.getCalendarPaddingLeft() -
+ mDelegate.getCalendarPaddingRight()) / 7;
+
+ onPreviewHook();
+ int count = mLineCount * 7;
+ int d = 0;
+ for (int i = 0; i < mLineCount; i++) {
+ for (int j = 0; j < 7; j++) {
+ Calendar calendar = mItems.get(d);
+ if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ONLY_CURRENT_MONTH) {
+ if (d > mItems.size() - mNextDiff) {
+ return;
+ }
+ if (!calendar.isCurrentMonth()) {
+ ++d;
+ continue;
+ }
+ } else if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_FIT_MONTH) {
+ if (d >= count) {
+ return;
+ }
+ }
+ draw(canvas, calendar, d, i, j);
+ ++d;
+ }
+ }
+ }
+
+ /**
+ * 开始绘制
+ *
+ * @param canvas canvas
+ * @param calendar 对应日历
+ * @param i i
+ * @param j j
+ */
+ private void draw(Canvas canvas, Calendar calendar, int calendarIndex, int i, int j) {
+ int x = j * mItemWidth + mDelegate.getCalendarPaddingLeft();
+ int y = i * mItemHeight;
+ onLoopStart(x, y);
+ boolean isSelected = isCalendarSelected(calendar);
+ boolean hasScheme = calendar.hasScheme();
+ boolean isPreSelected = isSelectPreCalendar(calendar, calendarIndex);
+ boolean isNextSelected = isSelectNextCalendar(calendar, calendarIndex);
+
+ if (hasScheme) {
+ //标记的日子
+ boolean isDrawSelected = false;//是否继续绘制选中的onDrawScheme
+ if (isSelected) {
+ isDrawSelected = onDrawSelected(canvas, calendar, x, y, true, isPreSelected, isNextSelected);
+ }
+ if (isDrawSelected || !isSelected) {
+ //将画笔设置为标记颜色
+ mSchemePaint.setColor(calendar.getSchemeColor() != 0 ? calendar.getSchemeColor() : mDelegate.getSchemeThemeColor());
+ onDrawScheme(canvas, calendar, x, y, true);
+ }
+ } else {
+ if (isSelected) {
+ onDrawSelected(canvas, calendar, x, y, false, isPreSelected, isNextSelected);
+ }
+ }
+ onDrawText(canvas, calendar, x, y, hasScheme, isSelected);
+ }
+
+ /**
+ * 日历是否被选中
+ *
+ * @param calendar calendar
+ * @return 日历是否被选中
+ */
+ protected boolean isCalendarSelected(Calendar calendar) {
+ return !onCalendarIntercept(calendar) && mDelegate.mSelectedCalendars.containsKey(calendar.toString());
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (!isClick) {
+ return;
+ }
+ Calendar calendar = getIndex();
+
+ if (calendar == null) {
+ return;
+ }
+
+ if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ONLY_CURRENT_MONTH
+ && !calendar.isCurrentMonth()) {
+ return;
+ }
+
+ if (onCalendarIntercept(calendar)) {
+ mDelegate.mCalendarInterceptListener.onCalendarInterceptClick(calendar, true);
+ return;
+ }
+
+ if (!isInRange(calendar)) {
+ if (mDelegate.mCalendarMultiSelectListener != null) {
+ mDelegate.mCalendarMultiSelectListener.onCalendarMultiSelectOutOfRange(calendar);
+ }
+ return;
+ }
+
+ String key = calendar.toString();
+
+ if (mDelegate.mSelectedCalendars.containsKey(key)) {
+ mDelegate.mSelectedCalendars.remove(key);
+ } else {
+ if (mDelegate.mSelectedCalendars.size() >= mDelegate.getMaxMultiSelectSize()) {
+ if (mDelegate.mCalendarMultiSelectListener != null) {
+ mDelegate.mCalendarMultiSelectListener.onMultiSelectOutOfSize(calendar,
+ mDelegate.getMaxMultiSelectSize());
+ }
+ return;
+ }
+ mDelegate.mSelectedCalendars.put(key, calendar);
+ }
+
+ mCurrentItem = mItems.indexOf(calendar);
+
+ if (!calendar.isCurrentMonth() && mMonthViewPager != null) {
+ int cur = mMonthViewPager.getCurrentItem();
+ int position = mCurrentItem < 7 ? cur - 1 : cur + 1;
+ mMonthViewPager.setCurrentItem(position);
+ }
+
+ if (mDelegate.mInnerListener != null) {
+ mDelegate.mInnerListener.onMonthDateSelected(calendar, true);
+ }
+
+ if (mParentLayout != null) {
+ if (calendar.isCurrentMonth()) {
+ mParentLayout.updateSelectPosition(mItems.indexOf(calendar));
+ } else {
+ mParentLayout.updateSelectWeek(CalendarUtil.getWeekFromDayInMonth(calendar, mDelegate.getWeekStart()));
+ }
+ }
+ if (mDelegate.mCalendarMultiSelectListener != null) {
+ mDelegate.mCalendarMultiSelectListener.onCalendarMultiSelect(
+ calendar,
+ mDelegate.mSelectedCalendars.size(),
+ mDelegate.getMaxMultiSelectSize());
+ }
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ return false;
+ }
+
+ /**
+ * 上一个日期是否选中
+ *
+ * @param calendar 当前日期
+ * @param calendarIndex 当前位置
+ * @return 上一个日期是否选中
+ */
+ protected final boolean isSelectPreCalendar(Calendar calendar, int calendarIndex) {
+ Calendar preCalendar;
+ if (calendarIndex == 0) {
+ preCalendar = CalendarUtil.getPreCalendar(calendar);
+ mDelegate.updateCalendarScheme(preCalendar);
+ } else {
+ preCalendar = mItems.get(calendarIndex - 1);
+ }
+
+ return isCalendarSelected(preCalendar);
+ }
+
+ /**
+ * 下一个日期是否选中
+ *
+ * @param calendar 当前日期
+ * @param calendarIndex 当前位置
+ * @return 下一个日期是否选中
+ */
+ protected final boolean isSelectNextCalendar(Calendar calendar, int calendarIndex) {
+ Calendar nextCalendar;
+ if (calendarIndex == mItems.size() - 1) {
+ nextCalendar = CalendarUtil.getNextCalendar(calendar);
+ mDelegate.updateCalendarScheme(nextCalendar);
+ } else {
+ nextCalendar = mItems.get(calendarIndex + 1);
+ }
+
+ return isCalendarSelected(nextCalendar);
+ }
+
+ /**
+ * 绘制选中的日期
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @param isSelectedPre 上一个日期是否选中
+ * @param isSelectedNext 下一个日期是否选中
+ * @return 是否继续绘制onDrawScheme,true or false
+ */
+ protected abstract boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme,
+ boolean isSelectedPre, boolean isSelectedNext);
+
+ /**
+ * 绘制标记的日期,这里可以是背景色,标记色什么的
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param isSelected 是否选中
+ */
+ protected abstract void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y, boolean isSelected);
+
+
+ /**
+ * 绘制日历文本
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme 是否是标记的日期
+ * @param isSelected 是否选中
+ */
+ protected abstract void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected);
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/MultiWeekView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/MultiWeekView.java
new file mode 100644
index 00000000..db731c0a
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/MultiWeekView.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.view.View;
+
+/**
+ * 多选周视图
+ * Created by huanghaibin on 2018/9/11.
+ */
+public abstract class MultiWeekView extends BaseWeekView {
+
+ public MultiWeekView(Context context) {
+ super(context);
+ }
+
+ /**
+ * 绘制日历文本
+ *
+ * @param canvas canvas
+ */
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mItems.size() == 0)
+ return;
+ mItemWidth = (getWidth() -
+ mDelegate.getCalendarPaddingLeft() -
+ mDelegate.getCalendarPaddingRight()) / 7;
+ onPreviewHook();
+
+ for (int i = 0; i < 7; i++) {
+ int x = i * mItemWidth + mDelegate.getCalendarPaddingLeft();
+ onLoopStart(x);
+ Calendar calendar = mItems.get(i);
+ boolean isSelected = isCalendarSelected(calendar);
+ boolean isPreSelected = isSelectPreCalendar(calendar, i);
+ boolean isNextSelected = isSelectNextCalendar(calendar, i);
+ boolean hasScheme = calendar.hasScheme();
+ if (hasScheme) {
+ boolean isDrawSelected = false;//是否继续绘制选中的onDrawScheme
+ if (isSelected) {
+ isDrawSelected = onDrawSelected(canvas, calendar, x, true, isPreSelected, isNextSelected);
+ }
+ if (isDrawSelected || !isSelected) {
+ //将画笔设置为标记颜色
+ mSchemePaint.setColor(calendar.getSchemeColor() != 0 ? calendar.getSchemeColor() : mDelegate.getSchemeThemeColor());
+ onDrawScheme(canvas, calendar, x, isSelected);
+ }
+ } else {
+ if (isSelected) {
+ onDrawSelected(canvas, calendar, x, false, isPreSelected, isNextSelected);
+ }
+ }
+ onDrawText(canvas, calendar, x, hasScheme, isSelected);
+ }
+ }
+
+
+ /**
+ * 日历是否被选中
+ *
+ * @param calendar calendar
+ * @return 日历是否被选中
+ */
+ protected boolean isCalendarSelected(Calendar calendar) {
+ return !onCalendarIntercept(calendar) && mDelegate.mSelectedCalendars.containsKey(calendar.toString());
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (!isClick) {
+ return;
+ }
+ Calendar calendar = getIndex();
+ if (calendar == null) {
+ return;
+ }
+ if (onCalendarIntercept(calendar)) {
+ mDelegate.mCalendarInterceptListener.onCalendarInterceptClick(calendar, true);
+ return;
+ }
+ if (!isInRange(calendar)) {
+ if (mDelegate.mCalendarMultiSelectListener != null) {
+ mDelegate.mCalendarMultiSelectListener.onCalendarMultiSelectOutOfRange(calendar);
+ }
+ return;
+ }
+
+
+ String key = calendar.toString();
+
+ if (mDelegate.mSelectedCalendars.containsKey(key)) {
+ mDelegate.mSelectedCalendars.remove(key);
+ } else {
+ if (mDelegate.mSelectedCalendars.size() >= mDelegate.getMaxMultiSelectSize()) {
+ if (mDelegate.mCalendarMultiSelectListener != null) {
+ mDelegate.mCalendarMultiSelectListener.onMultiSelectOutOfSize(calendar,
+ mDelegate.getMaxMultiSelectSize());
+ }
+ return;
+ }
+ mDelegate.mSelectedCalendars.put(key, calendar);
+ }
+
+ mCurrentItem = mItems.indexOf(calendar);
+
+ if (mDelegate.mInnerListener != null) {
+ mDelegate.mInnerListener.onWeekDateSelected(calendar, true);
+ }
+ if (mParentLayout != null) {
+ int i = CalendarUtil.getWeekFromDayInMonth(calendar, mDelegate.getWeekStart());
+ mParentLayout.updateSelectWeek(i);
+ }
+
+ if (mDelegate.mCalendarMultiSelectListener != null) {
+ mDelegate.mCalendarMultiSelectListener.onCalendarMultiSelect(
+ calendar,
+ mDelegate.mSelectedCalendars.size(),
+ mDelegate.getMaxMultiSelectSize());
+ }
+
+ invalidate();
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ return false;
+ }
+
+ /**
+ * 上一个日期是否选中
+ *
+ * @param calendar 当前日期
+ * @param calendarIndex 当前位置
+ * @return 上一个日期是否选中
+ */
+ protected final boolean isSelectPreCalendar(Calendar calendar, int calendarIndex) {
+ Calendar preCalendar;
+ if (calendarIndex == 0) {
+ preCalendar = CalendarUtil.getPreCalendar(calendar);
+ mDelegate.updateCalendarScheme(preCalendar);
+ } else {
+ preCalendar = mItems.get(calendarIndex - 1);
+ }
+ return isCalendarSelected(preCalendar);
+ }
+
+ /**
+ * 下一个日期是否选中
+ *
+ * @param calendar 当前日期
+ * @param calendarIndex 当前位置
+ * @return 下一个日期是否选中
+ */
+ protected final boolean isSelectNextCalendar(Calendar calendar, int calendarIndex) {
+ Calendar nextCalendar;
+ if (calendarIndex == mItems.size() - 1) {
+ nextCalendar = CalendarUtil.getNextCalendar(calendar);
+ mDelegate.updateCalendarScheme(nextCalendar);
+ } else {
+ nextCalendar = mItems.get(calendarIndex + 1);
+ }
+ return isCalendarSelected(nextCalendar);
+ }
+
+ /**
+ * 绘制选中的日期
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @param isSelectedPre 上一个日期是否选中
+ * @param isSelectedNext 下一个日期是否选中
+ * @return 是否绘制 onDrawScheme
+ */
+ protected abstract boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme,
+ boolean isSelectedPre, boolean isSelectedNext);
+
+ /**
+ * 绘制标记的日期
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param isSelected 是否选中
+ */
+ protected abstract void onDrawScheme(Canvas canvas, Calendar calendar, int x, boolean isSelected);
+
+
+ /**
+ * 绘制日历文本
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param hasScheme 是否是标记的日期
+ * @param isSelected 是否选中
+ */
+ protected abstract void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected);
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/RangeMonthView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/RangeMonthView.java
new file mode 100644
index 00000000..042c296e
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/RangeMonthView.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.view.View;
+
+/**
+ * 范围选择月视图
+ * Created by huanghaibin on 2018/9/11.
+ */
+public abstract class RangeMonthView extends BaseMonthView {
+
+ public RangeMonthView(Context context) {
+ super(context);
+ }
+
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mLineCount == 0)
+ return;
+ mItemWidth = (getWidth() -
+ mDelegate.getCalendarPaddingLeft() -
+ mDelegate.getCalendarPaddingRight()) / 7;
+ onPreviewHook();
+ int count = mLineCount * 7;
+ int d = 0;
+ for (int i = 0; i < mLineCount; i++) {
+ for (int j = 0; j < 7; j++) {
+ Calendar calendar = mItems.get(d);
+ if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ONLY_CURRENT_MONTH) {
+ if (d > mItems.size() - mNextDiff) {
+ return;
+ }
+ if (!calendar.isCurrentMonth()) {
+ ++d;
+ continue;
+ }
+ } else if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_FIT_MONTH) {
+ if (d >= count) {
+ return;
+ }
+ }
+ draw(canvas, calendar, d, i, j);
+ ++d;
+ }
+ }
+ }
+
+ /**
+ * 开始绘制
+ *
+ * @param canvas canvas
+ * @param calendar 对应日历
+ * @param i i
+ * @param j j
+ */
+ private void draw(Canvas canvas, Calendar calendar, int calendarIndex, int i, int j) {
+ int x = j * mItemWidth + mDelegate.getCalendarPaddingLeft();
+ int y = i * mItemHeight;
+ onLoopStart(x, y);
+ boolean isSelected = isCalendarSelected(calendar);
+ boolean hasScheme = calendar.hasScheme();
+ boolean isPreSelected = isSelectPreCalendar(calendar, calendarIndex);
+ boolean isNextSelected = isSelectNextCalendar(calendar, calendarIndex);
+
+ if (hasScheme) {
+ //标记的日子
+ boolean isDrawSelected = false;//是否继续绘制选中的onDrawScheme
+ if (isSelected) {
+ isDrawSelected = onDrawSelected(canvas, calendar, x, y, true, isPreSelected, isNextSelected);
+ }
+ if (isDrawSelected || !isSelected) {
+ //将画笔设置为标记颜色
+ mSchemePaint.setColor(calendar.getSchemeColor() != 0 ? calendar.getSchemeColor() : mDelegate.getSchemeThemeColor());
+ onDrawScheme(canvas, calendar, x, y, true);
+ }
+ } else {
+ if (isSelected) {
+ onDrawSelected(canvas, calendar, x, y, false, isPreSelected, isNextSelected);
+ }
+ }
+ onDrawText(canvas, calendar, x, y, hasScheme, isSelected);
+ }
+
+ /**
+ * 日历是否被选中
+ *
+ * @param calendar calendar
+ * @return 日历是否被选中
+ */
+ protected boolean isCalendarSelected(Calendar calendar) {
+ if (mDelegate.mSelectedStartRangeCalendar == null) {
+ return false;
+ }
+ if (onCalendarIntercept(calendar)) {
+ return false;
+ }
+ if (mDelegate.mSelectedEndRangeCalendar == null) {
+ return calendar.compareTo(mDelegate.mSelectedStartRangeCalendar) == 0;
+ }
+ return calendar.compareTo(mDelegate.mSelectedStartRangeCalendar) >= 0 &&
+ calendar.compareTo(mDelegate.mSelectedEndRangeCalendar) <= 0;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (!isClick) {
+ return;
+ }
+ Calendar calendar = getIndex();
+
+ if (calendar == null) {
+ return;
+ }
+
+ if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ONLY_CURRENT_MONTH
+ && !calendar.isCurrentMonth()) {
+ return;
+ }
+
+ if (onCalendarIntercept(calendar)) {
+ mDelegate.mCalendarInterceptListener.onCalendarInterceptClick(calendar, true);
+ return;
+ }
+
+ if (!isInRange(calendar)) {
+ if (mDelegate.mCalendarRangeSelectListener != null) {
+ mDelegate.mCalendarRangeSelectListener.onCalendarSelectOutOfRange(calendar);
+ }
+ return;
+ }
+
+ //优先判断各种直接return的情况,减少代码深度
+ if (mDelegate.mSelectedStartRangeCalendar != null && mDelegate.mSelectedEndRangeCalendar == null) {
+ int minDiffer = CalendarUtil.differ(calendar, mDelegate.mSelectedStartRangeCalendar);
+ if (minDiffer >= 0 && mDelegate.getMinSelectRange() != -1 && mDelegate.getMinSelectRange() > minDiffer + 1) {
+ if (mDelegate.mCalendarRangeSelectListener != null) {
+ mDelegate.mCalendarRangeSelectListener.onSelectOutOfRange(calendar, true);
+ }
+ return;
+ } else if (mDelegate.getMaxSelectRange() != -1 && mDelegate.getMaxSelectRange() <
+ CalendarUtil.differ(calendar, mDelegate.mSelectedStartRangeCalendar) + 1) {
+ if (mDelegate.mCalendarRangeSelectListener != null) {
+ mDelegate.mCalendarRangeSelectListener.onSelectOutOfRange(calendar, false);
+ }
+ return;
+ }
+ }
+
+ if (mDelegate.mSelectedStartRangeCalendar == null || mDelegate.mSelectedEndRangeCalendar != null) {
+ mDelegate.mSelectedStartRangeCalendar = calendar;
+ mDelegate.mSelectedEndRangeCalendar = null;
+ } else {
+ int compare = calendar.compareTo(mDelegate.mSelectedStartRangeCalendar);
+ if (mDelegate.getMinSelectRange() == -1 && compare <= 0) {
+ mDelegate.mSelectedStartRangeCalendar = calendar;
+ mDelegate.mSelectedEndRangeCalendar = null;
+ } else if (compare < 0) {
+ mDelegate.mSelectedStartRangeCalendar = calendar;
+ mDelegate.mSelectedEndRangeCalendar = null;
+ } else if (compare == 0 &&
+ mDelegate.getMinSelectRange() == 1) {
+ mDelegate.mSelectedEndRangeCalendar = calendar;
+ } else {
+ mDelegate.mSelectedEndRangeCalendar = calendar;
+ }
+
+ }
+
+ mCurrentItem = mItems.indexOf(calendar);
+
+ if (!calendar.isCurrentMonth() && mMonthViewPager != null) {
+ int cur = mMonthViewPager.getCurrentItem();
+ int position = mCurrentItem < 7 ? cur - 1 : cur + 1;
+ mMonthViewPager.setCurrentItem(position);
+ }
+
+ if (mDelegate.mInnerListener != null) {
+ mDelegate.mInnerListener.onMonthDateSelected(calendar, true);
+ }
+
+ if (mParentLayout != null) {
+ if (calendar.isCurrentMonth()) {
+ mParentLayout.updateSelectPosition(mItems.indexOf(calendar));
+ } else {
+ mParentLayout.updateSelectWeek(CalendarUtil.getWeekFromDayInMonth(calendar, mDelegate.getWeekStart()));
+ }
+ }
+ if (mDelegate.mCalendarRangeSelectListener != null) {
+ mDelegate.mCalendarRangeSelectListener.onCalendarRangeSelect(calendar,
+ mDelegate.mSelectedEndRangeCalendar != null);
+ }
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ return false;
+ }
+
+ /**
+ * 上一个日期是否选中
+ *
+ * @param calendar 当前日期
+ * @param calendarIndex 当前位置
+ * @return 上一个日期是否选中
+ */
+ protected final boolean isSelectPreCalendar(Calendar calendar, int calendarIndex) {
+
+ Calendar preCalendar;
+ if (calendarIndex == 0) {
+ preCalendar = CalendarUtil.getPreCalendar(calendar);
+ mDelegate.updateCalendarScheme(preCalendar);
+ } else {
+ preCalendar = mItems.get(calendarIndex - 1);
+ }
+
+ return mDelegate.mSelectedStartRangeCalendar != null &&
+ isCalendarSelected(preCalendar);
+ }
+
+ /**
+ * 下一个日期是否选中
+ *
+ * @param calendar 当前日期
+ * @param calendarIndex 当前位置
+ * @return 下一个日期是否选中
+ */
+ protected final boolean isSelectNextCalendar(Calendar calendar, int calendarIndex) {
+
+ Calendar nextCalendar;
+ if (calendarIndex == mItems.size() - 1) {
+ nextCalendar = CalendarUtil.getNextCalendar(calendar);
+ mDelegate.updateCalendarScheme(nextCalendar);
+ } else {
+ nextCalendar = mItems.get(calendarIndex + 1);
+ }
+
+ return mDelegate.mSelectedStartRangeCalendar != null &&
+ isCalendarSelected(nextCalendar);
+ }
+
+ /**
+ * 绘制选中的日期
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @param isSelectedPre 上一个日期是否选中
+ * @param isSelectedNext 下一个日期是否选中
+ * @return 是否继续绘制onDrawScheme,true or false
+ */
+ protected abstract boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme,
+ boolean isSelectedPre, boolean isSelectedNext);
+
+ /**
+ * 绘制标记的日期,这里可以是背景色,标记色什么的
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param isSelected 是否选中
+ */
+ protected abstract void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y, boolean isSelected);
+
+
+ /**
+ * 绘制日历文本
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme 是否是标记的日期
+ * @param isSelected 是否选中
+ */
+ protected abstract void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected);
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/RangeWeekView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/RangeWeekView.java
new file mode 100644
index 00000000..435c9245
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/RangeWeekView.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.view.View;
+
+/**
+ * 范围选择周视图
+ * Created by huanghaibin on 2018/9/11.
+ */
+public abstract class RangeWeekView extends BaseWeekView {
+
+ public RangeWeekView(Context context) {
+ super(context);
+ }
+
+ /**
+ * 绘制日历文本
+ *
+ * @param canvas canvas
+ */
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mItems.size() == 0)
+ return;
+ mItemWidth = (getWidth() -
+ mDelegate.getCalendarPaddingLeft() -
+ mDelegate.getCalendarPaddingRight()) / 7;
+ onPreviewHook();
+
+ for (int i = 0; i < 7; i++) {
+ int x = i * mItemWidth + mDelegate.getCalendarPaddingLeft();
+ onLoopStart(x);
+ Calendar calendar = mItems.get(i);
+ boolean isSelected = isCalendarSelected(calendar);
+ boolean isPreSelected = isSelectPreCalendar(calendar, i);
+ boolean isNextSelected = isSelectNextCalendar(calendar, i);
+ boolean hasScheme = calendar.hasScheme();
+ if (hasScheme) {
+ boolean isDrawSelected = false;//是否继续绘制选中的onDrawScheme
+ if (isSelected) {
+ isDrawSelected = onDrawSelected(canvas, calendar, x, true, isPreSelected, isNextSelected);
+ }
+ if (isDrawSelected || !isSelected) {
+ //将画笔设置为标记颜色
+ mSchemePaint.setColor(calendar.getSchemeColor() != 0 ? calendar.getSchemeColor() : mDelegate.getSchemeThemeColor());
+ onDrawScheme(canvas, calendar, x, isSelected);
+ }
+ } else {
+ if (isSelected) {
+ onDrawSelected(canvas, calendar, x, false, isPreSelected, isNextSelected);
+ }
+ }
+ onDrawText(canvas, calendar, x, hasScheme, isSelected);
+ }
+ }
+
+
+ /**
+ * 日历是否被选中
+ *
+ * @param calendar calendar
+ * @return 日历是否被选中
+ */
+ protected boolean isCalendarSelected(Calendar calendar) {
+ if (mDelegate.mSelectedStartRangeCalendar == null) {
+ return false;
+ }
+ if (onCalendarIntercept(calendar)) {
+ return false;
+ }
+ if (mDelegate.mSelectedEndRangeCalendar == null) {
+ return calendar.compareTo(mDelegate.mSelectedStartRangeCalendar) == 0;
+ }
+ return calendar.compareTo(mDelegate.mSelectedStartRangeCalendar) >= 0 &&
+ calendar.compareTo(mDelegate.mSelectedEndRangeCalendar) <= 0;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (!isClick) {
+ return;
+ }
+ Calendar calendar = getIndex();
+ if (calendar == null) {
+ return;
+ }
+ if (onCalendarIntercept(calendar)) {
+ mDelegate.mCalendarInterceptListener.onCalendarInterceptClick(calendar, true);
+ return;
+ }
+ if (!isInRange(calendar)) {
+ if (mDelegate.mCalendarRangeSelectListener != null) {
+ mDelegate.mCalendarRangeSelectListener.onCalendarSelectOutOfRange(calendar);
+ }
+ return;
+ }
+
+ //优先判断各种直接return的情况,减少代码深度
+ if (mDelegate.mSelectedStartRangeCalendar != null && mDelegate.mSelectedEndRangeCalendar == null) {
+ int minDiffer = CalendarUtil.differ(calendar, mDelegate.mSelectedStartRangeCalendar);
+ if (minDiffer >= 0 && mDelegate.getMinSelectRange() != -1 && mDelegate.getMinSelectRange() > minDiffer + 1) {
+ if (mDelegate.mCalendarRangeSelectListener != null) {
+ mDelegate.mCalendarRangeSelectListener.onSelectOutOfRange(calendar, true);
+ }
+ return;
+ } else if (mDelegate.getMaxSelectRange() != -1 && mDelegate.getMaxSelectRange() <
+ CalendarUtil.differ(calendar, mDelegate.mSelectedStartRangeCalendar) + 1) {
+ if (mDelegate.mCalendarRangeSelectListener != null) {
+ mDelegate.mCalendarRangeSelectListener.onSelectOutOfRange(calendar, false);
+ }
+ return;
+ }
+ }
+
+ if (mDelegate.mSelectedStartRangeCalendar == null || mDelegate.mSelectedEndRangeCalendar != null) {
+ mDelegate.mSelectedStartRangeCalendar = calendar;
+ mDelegate.mSelectedEndRangeCalendar = null;
+ } else {
+ int compare = calendar.compareTo(mDelegate.mSelectedStartRangeCalendar);
+ if (mDelegate.getMinSelectRange() == -1 && compare <= 0) {
+ mDelegate.mSelectedStartRangeCalendar = calendar;
+ mDelegate.mSelectedEndRangeCalendar = null;
+ } else if (compare < 0) {
+ mDelegate.mSelectedStartRangeCalendar = calendar;
+ mDelegate.mSelectedEndRangeCalendar = null;
+ } else if (compare == 0 &&
+ mDelegate.getMinSelectRange() == 1) {
+ mDelegate.mSelectedEndRangeCalendar = calendar;
+ } else {
+ mDelegate.mSelectedEndRangeCalendar = calendar;
+ }
+
+ }
+
+ mCurrentItem = mItems.indexOf(calendar);
+
+ if (mDelegate.mInnerListener != null) {
+ mDelegate.mInnerListener.onWeekDateSelected(calendar, true);
+ }
+ if (mParentLayout != null) {
+ int i = CalendarUtil.getWeekFromDayInMonth(calendar, mDelegate.getWeekStart());
+ mParentLayout.updateSelectWeek(i);
+ }
+
+ if (mDelegate.mCalendarRangeSelectListener != null) {
+ mDelegate.mCalendarRangeSelectListener.onCalendarRangeSelect(calendar,
+ mDelegate.mSelectedEndRangeCalendar != null);
+ }
+
+ invalidate();
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ return false;
+ }
+
+ /**
+ * 上一个日期是否选中
+ *
+ * @param calendar 当前日期
+ * @param calendarIndex 当前位置
+ * @return 上一个日期是否选中
+ */
+ protected final boolean isSelectPreCalendar(Calendar calendar, int calendarIndex) {
+
+ Calendar preCalendar;
+ if (calendarIndex == 0) {
+ preCalendar = CalendarUtil.getPreCalendar(calendar);
+ mDelegate.updateCalendarScheme(preCalendar);
+ } else {
+ preCalendar = mItems.get(calendarIndex - 1);
+ }
+ return mDelegate.mSelectedStartRangeCalendar != null &&
+ isCalendarSelected(preCalendar);
+ }
+
+ /**
+ * 下一个日期是否选中
+ *
+ * @param calendar 当前日期
+ * @param calendarIndex 当前位置
+ * @return 下一个日期是否选中
+ */
+ protected final boolean isSelectNextCalendar(Calendar calendar, int calendarIndex) {
+ Calendar nextCalendar;
+ if (calendarIndex == mItems.size() - 1) {
+ nextCalendar = CalendarUtil.getNextCalendar(calendar);
+ mDelegate.updateCalendarScheme(nextCalendar);
+ } else {
+ nextCalendar = mItems.get(calendarIndex + 1);
+ }
+ return mDelegate.mSelectedStartRangeCalendar != null &&
+ isCalendarSelected(nextCalendar);
+ }
+
+ /**
+ * 绘制选中的日期
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @param isSelectedPre 上一个日期是否选中
+ * @param isSelectedNext 下一个日期是否选中
+ * @return 是否绘制 onDrawScheme
+ */
+ protected abstract boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme,
+ boolean isSelectedPre, boolean isSelectedNext);
+
+ /**
+ * 绘制标记的日期
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param isSelected 是否选中
+ */
+ protected abstract void onDrawScheme(Canvas canvas, Calendar calendar, int x, boolean isSelected);
+
+
+ /**
+ * 绘制日历文本
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param hasScheme 是否是标记的日期
+ * @param isSelected 是否选中
+ */
+ protected abstract void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected);
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/SolarTermUtil.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/SolarTermUtil.java
new file mode 100644
index 00000000..a816cac7
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/SolarTermUtil.java
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+
+/**
+ * http://www.cnblogs.com/moodlxs/archive/2010/12/18/2345392.html
+ * 24节气计算公式,参考该博客实现
+ */
+@SuppressWarnings("all")
+public final class SolarTermUtil {
+
+
+ static void init(Context context) {
+ SOLAR_TERMS = context.getResources().getStringArray(R.array.solar_term);
+ }
+
+ /**
+ * 24节气
+ */
+ private static String SOLAR_TERMS[] = null;
+
+ /**
+ * 每弧度的角秒数
+ */
+ private static final double SECOND_PER_RAD = 180 * 3600 / Math.PI;
+
+ /**
+ * 每弧度的角度数
+ */
+ private static final double ANGLE_PER_RAD = 180 / Math.PI;
+
+ /**
+ * 日历计算
+ * 2000年前儒略日数(2000-1-1)
+ */
+ private static final double J2000 = 2451545;
+
+ /**
+ * 黄赤交角系数表
+ */
+ private static final double H_C_ANGLE_TABLE[] = {0, 50287.92262, 111.24406,
+ 0.07699, -0.23479, -0.00178, 0.00018, 0.00001};
+
+
+ /**
+ * 世界时与原子时之差计算表
+ */
+ private static final double[] DTS = {
+ -4000, 108371.7, -13036.80, 392.000, 0.0000, -500, 17201.0,
+ -627.82, 16.170, -0.3413, -150, 12200.6, -346.41, 5.403, -0.1593,
+ 150, 9113.8, -328.13, -1.647, 0.0377, 500, 5707.5, -391.41, 0.915,
+ 0.3145, 900, 2203.4, -283.45, 13.034, -0.1778, 1300, 490.1, -57.35,
+ 2.085, -0.0072, 1600, 120.0, -9.81, -1.532, 0.1403, 1700, 10.2,
+ -0.91, 0.510, -0.0370, 1800, 13.4, -0.72, 0.202, -0.0193, 1830,
+ 7.8, -1.81, 0.416, -0.0247, 1860, 8.3, -0.13, -0.406, 0.0292, 1880,
+ -5.4, 0.32, -0.183, 0.0173, 1900, -2.3, 2.06, 0.169, -0.0135, 1920,
+ 21.2, 1.69, -0.304, 0.0167, 1940, 24.2, 1.22, -0.064, 0.0031, 1960,
+ 33.2, 0.51, 0.231, -0.0109, 1980, 51.0, 1.29, -0.026, 0.0032, 2000,
+ 64.7, -1.66, 5.224, -0.2905, 2150, 279.4, 732.95, 429.579, 0.0158,
+ 6000};
+
+ /**
+ * 离心率
+ */
+ private static final double GXC_E[] = {0.016708634, -0.000042037,
+ -0.0000001267};
+
+ /**
+ * 近点
+ */
+ private static final double GXC_P[] = {102.93735 / ANGLE_PER_RAD, 1.71946 / ANGLE_PER_RAD,
+ 0.00046 / ANGLE_PER_RAD};
+
+ /**
+ * 太平黄经
+ */
+ private static final double GXC_L[] = {280.4664567 / ANGLE_PER_RAD,
+ 36000.76982779 / ANGLE_PER_RAD, 0.0003032028 / ANGLE_PER_RAD, 1 / 49931000 / ANGLE_PER_RAD,
+ -1 / 153000000 / ANGLE_PER_RAD};
+
+ /**
+ * 光行差常数
+ */
+ private static final double GXC_K = 20.49552 / SECOND_PER_RAD;
+
+
+ /**
+ * 向下取整
+ *
+ * @param v v
+ * @return 取整数部分
+ */
+ private static double doubleFloor(double v) {
+ v = Math.floor(v);
+ if (v < 0)
+ return v + 1;
+
+ return v;
+ }
+
+
+ /**
+ * 对超过0-2PI的角度转为0-2PI
+ *
+ * @param v v
+ * @return 对超过0-2PI的角度转为0-2PI
+ */
+ private static double rad2mrad(double v) {
+ v = v % (2 * Math.PI);
+ if (v < 0)
+ return v + 2 * Math.PI;
+
+ return v;
+ }
+
+ /**
+ * 计算世界时与原子时之差,传入年
+ *
+ * @param year 年
+ * @return 计算世界时与原子时之差
+ */
+ private static double worldTimeDiff(double year) {
+ int i;
+ for (i = 0; i < 100; i += 5)
+ if (year < DTS[i + 5] || i == 95)
+ break;
+
+ double t1 = (year - DTS[i]) / (DTS[i + 5] - DTS[i]) * 10;
+ double t2 = t1 * t1;
+ double t3 = t2 * t1;
+ return DTS[i + 1] + DTS[i + 2] * t1 + DTS[i + 3] * t2 + DTS[i + 4] * t3;
+ }
+
+ /**
+ * 传入儒略日(J2000起算),计算UTC与原子时的差(单位:日)
+ *
+ * @param julian 儒略日
+ * @return 计算UTC与原子时的差
+ */
+ private static double atomTimeDiff(double julian) {
+ return worldTimeDiff(julian / 365.2425 + 2000) / 86400.0;
+ }
+
+ /**
+ * 公历转儒略日,UTC=1表示原日期是UTC
+ *
+ * @param UTC UTC
+ * @return 公历转儒略日, UTC=1表示原日期是UTC
+ */
+ private static double toJulian(Time time, boolean UTC) {
+ double y = time.year; // 取出年月
+ double m = time.month;
+ double n = 0;
+
+ if (m <= 2) {
+ m += 12;
+ y--;
+ }
+
+ if (time.year * 372 + time.month * 31 + time.day >= 588829) {
+ // 判断是否为格里高利历日1582*372+10*31+15
+ n = doubleFloor(y / 100);
+ n = 2 - n + doubleFloor(n / 4);// 加百年闰
+ }
+
+ n += doubleFloor(365.2500001 * (y + 4716)); // 加上年引起的偏移日数
+ n += doubleFloor(30.6 * (m + 1)) + time.day; // 加上月引起的偏移日数及日偏移数
+ n += ((time.second / 60 + time.minute) / 60 + time.hour) / 24 - 1524.5;
+ if (UTC)
+ return n + atomTimeDiff(n - J2000);
+
+ return n;
+ }
+
+ /**
+ * 儒略日数转公历,UTC=1表示目标公历是UTC
+ *
+ * @param jd jd
+ * @param UTC UTC
+ */
+ @SuppressWarnings("all")
+ private static Time setFromJulian(double jd, boolean UTC) {
+ Time time = new Time();
+ if (UTC)
+ jd -= atomTimeDiff(jd - J2000);
+
+ jd += 0.5;
+
+ // 取得日数的整数部份A及小数部分F
+ double A = doubleFloor(jd);
+ double F = jd - A;
+ double D;
+
+ if (A > 2299161) {
+ D = doubleFloor((A - 1867216.25) / 36524.25);
+ A += 1 + D - doubleFloor(D / 4);
+ }
+ A += 1524; // 向前移4年零2个月
+ time.year = doubleFloor((A - 122.1) / 365.25);// 年
+ D = A - doubleFloor(365.25 * time.year); // 去除整年日数后余下日数
+ time.month = doubleFloor(D / 30.6001); // 月数
+ time.day = D - doubleFloor(time.month * 30.6001);// 去除整月日数后余下日数
+ time.year -= 4716;
+ time.month--;
+ if (time.month > 12)
+ time.month -= 12;
+ if (time.month <= 2)
+ time.year++;
+ // 日的小数转为时分秒
+ F *= 24;
+ time.hour = doubleFloor(F);
+ F -= time.hour;
+ F *= 60;
+ time.minute = doubleFloor(F);
+ F -= time.minute;
+ F *= 60;
+ time.second = F;
+ return time;
+ }
+
+
+ /**
+ * 补岁差
+ *
+ * @param jd jd
+ * @param zb zb
+ */
+ private static void precession(double jd, double[] zb) {
+ int i;
+ double t = 1, v = 0, t1 = jd / 365250;
+ for (i = 1; i < 8; i++) {
+ t *= t1;
+ v += H_C_ANGLE_TABLE[i] * t;
+ }
+ zb[0] = rad2mrad(zb[0] + (v + 2.9965 * t1) / SECOND_PER_RAD);
+ }
+
+
+ /**
+ * 恒星周年光行差计算(黄道坐标中)
+ *
+ * @param t t
+ * @param zb zb
+ */
+ private static void addGxc(double t, double[] zb) {
+ double t1 = t / 36525;
+ double t2 = t1 * t1;
+ double t3 = t2 * t1;
+ double t4 = t3 * t1;
+ double L = GXC_L[0] + GXC_L[1] * t1 + GXC_L[2] * t2 + GXC_L[3] * t3
+ + GXC_L[4] * t4;
+ double p = GXC_P[0] + GXC_P[1] * t1 + GXC_P[2] * t2;
+ double e = GXC_E[0] + GXC_E[1] * t1 + GXC_E[2] * t2;
+ double dL = L - zb[0], dP = p - zb[0];
+ zb[0] -= GXC_K * (Math.cos(dL) - e * Math.cos(dP)) / Math.cos(zb[1]);
+ zb[1] -= GXC_K * Math.sin(zb[1]) * (Math.sin(dL) - e * Math.sin(dP));
+ zb[0] = rad2mrad(zb[0]);
+ }
+
+ /**
+ * 章动计算
+ */
+ private static final double nutB[] = {// 章动表
+ 2.1824391966, -33.757045954, 0.0000362262, 3.7340E-08, -2.8793E-10,
+ -171996, -1742, 92025, 89, 3.5069406862, 1256.663930738,
+ 0.0000105845, 6.9813E-10, -2.2815E-10, -13187, -16, 5736, -31,
+ 1.3375032491, 16799.418221925, -0.0000511866, 6.4626E-08,
+ -5.3543E-10, -2274, -2, 977, -5, 4.3648783932, -67.514091907,
+ 0.0000724525, 7.4681E-08, -5.7586E-10, 2062, 2, -895, 5,
+ 0.0431251803, -628.301955171, 0.0000026820, 6.5935E-10, 5.5705E-11,
+ -1426, 34, 54, -1, 2.3555557435, 8328.691425719, 0.0001545547,
+ 2.5033E-07, -1.1863E-09, 712, 1, -7, 0, 3.4638155059,
+ 1884.965885909, 0.0000079025, 3.8785E-11, -2.8386E-10, -517, 12,
+ 224, -6, 5.4382493597, 16833.175267879, -0.0000874129, 2.7285E-08,
+ -2.4750E-10, -386, -4, 200, 0, 3.6930589926, 25128.109647645,
+ 0.0001033681, 3.1496E-07, -1.7218E-09, -301, 0, 129, -1,
+ 3.5500658664, 628.361975567, 0.0000132664, 1.3575E-09, -1.7245E-10,
+ 217, -5, -95, 3};
+
+
+ /**
+ * 计算黄经章动及交角章动
+ *
+ * @param t t
+ * @return 计算黄经章动及交角章动
+ */
+ private static Nutation nutation(double t) {
+ Nutation d = new Nutation();
+ d.Lon = 0;
+ d.Obl = 0;
+ t /= 36525;
+ double c, t1 = t, t2 = t1 * t1, t3 = t2 * t1, t4 = t3 * t1;// t5=t4*t1;
+ for (int i = 0; i < nutB.length; i += 9) {
+ c = nutB[i] + nutB[i + 1] * t1 + nutB[i + 2] * t2 + nutB[i + 3]
+ * t3 + nutB[i + 4] * t4;
+ d.Lon += (nutB[i + 5] + nutB[i + 6] * t / 10) * Math.sin(c); // 黄经章动
+ d.Obl += (nutB[i + 7] + nutB[i + 8] * t / 10) * Math.cos(c); // 交角章动
+ }
+ d.Lon /= SECOND_PER_RAD * 10000; // 黄经章动
+ d.Obl /= SECOND_PER_RAD * 10000; // 交角章动
+ return d;
+ }
+
+
+ //
+ /***************************************
+ * 如果用记事本查看此代码,请在"格式"菜单中去除"自动换行"
+ * E10是关于地球的,格式如下:
+ * 它是一个数组,每3个数看作一条记录,每条记录的3个数记为A,B,C
+ * rec=A*cos(B+C*t); 式中t是J2000起算的儒略千年数
+ * 每条记录的计算结果(即rec)取和即得地球的日心黄经的周期量L0
+ * E11格式如下: rec = A*cos*(B+C*t) *t, 取和后得泊松量L1
+ * E12格式如下: rec = A*cos*(B+C*t) *t*t, 取和后得泊松量L2
+ * E13格式如下: rec = A*cos*(B+C*t) *t*t*t, 取和后得泊松量L3
+ * 最后地球的地心黄经:L = L0+L1+L2+L3+...
+ * E20,E21,E22,E23...用于计算黄纬
+ * M10,M11等是关于月球的,参数的用法请阅读Mnn()函数
+ *****************************************/
+ //地球运动VSOP87参数
+ private static final double E10[] = { //黄经周期项
+ 1.75347045673, 0.00000000000, 0.0000000000, 0.03341656456, 4.66925680417, 6283.0758499914, 0.00034894275, 4.62610241759, 12566.1516999828, 0.00003417571, 2.82886579606, 3.5231183490,
+ 0.00003497056, 2.74411800971, 5753.3848848968, 0.00003135896, 3.62767041758, 77713.7714681205, 0.00002676218, 4.41808351397, 7860.4193924392, 0.00002342687, 6.13516237631, 3930.2096962196,
+ 0.00001273166, 2.03709655772, 529.6909650946, 0.00001324292, 0.74246356352, 11506.7697697936, 0.00000901855, 2.04505443513, 26.2983197998, 0.00001199167, 1.10962944315, 1577.3435424478,
+ 0.00000857223, 3.50849156957, 398.1490034082, 0.00000779786, 1.17882652114, 5223.6939198022, 0.00000990250, 5.23268129594, 5884.9268465832, 0.00000753141, 2.53339053818, 5507.5532386674,
+ 0.00000505264, 4.58292563052, 18849.2275499742, 0.00000492379, 4.20506639861, 775.5226113240, 0.00000356655, 2.91954116867, 0.0673103028, 0.00000284125, 1.89869034186, 796.2980068164,
+ 0.00000242810, 0.34481140906, 5486.7778431750, 0.00000317087, 5.84901952218, 11790.6290886588, 0.00000271039, 0.31488607649, 10977.0788046990, 0.00000206160, 4.80646606059, 2544.3144198834,
+ 0.00000205385, 1.86947813692, 5573.1428014331, 0.00000202261, 2.45767795458, 6069.7767545534, 0.00000126184, 1.08302630210, 20.7753954924, 0.00000155516, 0.83306073807, 213.2990954380,
+ 0.00000115132, 0.64544911683, 0.9803210682, 0.00000102851, 0.63599846727, 4694.0029547076, 0.00000101724, 4.26679821365, 7.1135470008, 0.00000099206, 6.20992940258, 2146.1654164752,
+ 0.00000132212, 3.41118275555, 2942.4634232916, 0.00000097607, 0.68101272270, 155.4203994342, 0.00000085128, 1.29870743025, 6275.9623029906, 0.00000074651, 1.75508916159, 5088.6288397668,
+ 0.00000101895, 0.97569221824, 15720.8387848784, 0.00000084711, 3.67080093025, 71430.6956181291, 0.00000073547, 4.67926565481, 801.8209311238, 0.00000073874, 3.50319443167, 3154.6870848956,
+ 0.00000078756, 3.03698313141, 12036.4607348882, 0.00000079637, 1.80791330700, 17260.1546546904, 0.00000085803, 5.98322631256, 161000.6857376741, 0.00000056963, 2.78430398043, 6286.5989683404,
+ 0.00000061148, 1.81839811024, 7084.8967811152, 0.00000069627, 0.83297596966, 9437.7629348870, 0.00000056116, 4.38694880779, 14143.4952424306, 0.00000062449, 3.97763880587, 8827.3902698748,
+ 0.00000051145, 0.28306864501, 5856.4776591154, 0.00000055577, 3.47006009062, 6279.5527316424, 0.00000041036, 5.36817351402, 8429.2412664666, 0.00000051605, 1.33282746983, 1748.0164130670,
+ 0.00000051992, 0.18914945834, 12139.5535091068, 0.00000049000, 0.48735065033, 1194.4470102246, 0.00000039200, 6.16832995016, 10447.3878396044, 0.00000035566, 1.77597314691, 6812.7668150860,
+ 0.00000036770, 6.04133859347, 10213.2855462110, 0.00000036596, 2.56955238628, 1059.3819301892, 0.00000033291, 0.59309499459, 17789.8456197850, 0.00000035954, 1.70876111898, 2352.8661537718};
+ private static final double E11[] = { //黄经泊松1项
+ 6283.31966747491, 0.00000000000, 0.0000000000, 0.00206058863, 2.67823455584, 6283.0758499914, 0.00004303430, 2.63512650414, 12566.1516999828, 0.00000425264, 1.59046980729, 3.5231183490,
+ 0.00000108977, 2.96618001993, 1577.3435424478, 0.00000093478, 2.59212835365, 18849.2275499742, 0.00000119261, 5.79557487799, 26.2983197998, 0.00000072122, 1.13846158196, 529.6909650946,
+ 0.00000067768, 1.87472304791, 398.1490034082, 0.00000067327, 4.40918235168, 5507.5532386674, 0.00000059027, 2.88797038460, 5223.6939198022, 0.00000055976, 2.17471680261, 155.4203994342,
+ 0.00000045407, 0.39803079805, 796.2980068164, 0.00000036369, 0.46624739835, 775.5226113240, 0.00000028958, 2.64707383882, 7.1135470008, 0.00000019097, 1.84628332577, 5486.7778431750,
+ 0.00000020844, 5.34138275149, 0.9803210682, 0.00000018508, 4.96855124577, 213.2990954380, 0.00000016233, 0.03216483047, 2544.3144198834, 0.00000017293, 2.99116864949, 6275.9623029906};
+ private static final double E12[] = { //黄经泊松2项
+ 0.00052918870, 0.00000000000, 0.0000000000, 0.00008719837, 1.07209665242, 6283.0758499914, 0.00000309125, 0.86728818832, 12566.1516999828, 0.00000027339, 0.05297871691, 3.5231183490,
+ 0.00000016334, 5.18826691036, 26.2983197998, 0.00000015752, 3.68457889430, 155.4203994342, 0.00000009541, 0.75742297675, 18849.2275499742, 0.00000008937, 2.05705419118, 77713.7714681205,
+ 0.00000006952, 0.82673305410, 775.5226113240, 0.00000005064, 4.66284525271, 1577.3435424478};
+ private static final double E13[] = {0.00000289226, 5.84384198723, 6283.0758499914, 0.00000034955, 0.00000000000, 0.0000000000, 0.00000016819, 5.48766912348, 12566.1516999828};
+ private static final double E14[] = {0.00000114084, 3.14159265359, 0.0000000000, 0.00000007717, 4.13446589358, 6283.0758499914, 0.00000000765, 3.83803776214, 12566.1516999828};
+ private static final double E15[] = {0.00000000878, 3.14159265359, 0.0000000000};
+ private static final double E20[] = { //黄纬周期项
+ 0.00000279620, 3.19870156017, 84334.6615813083, 0.00000101643, 5.42248619256, 5507.5532386674, 0.00000080445, 3.88013204458, 5223.6939198022, 0.00000043806, 3.70444689758, 2352.8661537718,
+ 0.00000031933, 4.00026369781, 1577.3435424478, 0.00000022724, 3.98473831560, 1047.7473117547, 0.00000016392, 3.56456119782, 5856.4776591154, 0.00000018141, 4.98367470263, 6283.0758499914,
+ 0.00000014443, 3.70275614914, 9437.7629348870, 0.00000014304, 3.41117857525, 10213.2855462110};
+ private static final double E21[] = {0.00000009030, 3.89729061890, 5507.5532386674, 0.00000006177, 1.73038850355, 5223.6939198022};
+ private static final double E30[] = { //距离周期项
+ 1.00013988799, 0.00000000000, 0.0000000000, 0.01670699626, 3.09846350771, 6283.0758499914, 0.00013956023, 3.05524609620, 12566.1516999828, 0.00003083720, 5.19846674381, 77713.7714681205,
+ 0.00001628461, 1.17387749012, 5753.3848848968, 0.00001575568, 2.84685245825, 7860.4193924392, 0.00000924799, 5.45292234084, 11506.7697697936, 0.00000542444, 4.56409149777, 3930.2096962196};
+ private static final double E31[] = {0.00103018608, 1.10748969588, 6283.0758499914, 0.00001721238, 1.06442301418, 12566.1516999828, 0.00000702215, 3.14159265359, 0.0000000000};
+ private static final double E32[] = {0.00004359385, 5.78455133738, 6283.0758499914};
+ private static final double E33[] = {0.00000144595, 4.27319435148, 6283.0758499914};
+ //月球运动参数
+ private static final double M10[] = {
+ 22639.5858800, 2.3555545723, 8328.6914247251, 1.5231275E-04, 2.5041111E-07, -1.1863391E-09, 4586.4383203, 8.0413790709, 7214.0628654588, -2.1850087E-04, -1.8646419E-07, 8.7760973E-10, 2369.9139357, 10.3969336431, 15542.7542901840, -6.6188121E-05, 6.3946925E-08, -3.0872935E-10, 769.0257187, 4.7111091445, 16657.3828494503, 3.0462550E-04, 5.0082223E-07, -2.3726782E-09,
+ -666.4175399, -0.0431256817, 628.3019552485, -2.6638815E-06, 6.1639211E-10, -5.4439728E-11, -411.5957339, 3.2558104895, 16866.9323152810, -1.2804259E-04, -9.8998954E-09, 4.0433461E-11, 211.6555524, 5.6858244986, -1114.6285592663, -3.7081362E-04, -4.3687530E-07, 2.0639488E-09, 205.4359530, 8.0845047526, 6585.7609102104, -2.1583699E-04, -1.8708058E-07, 9.3204945E-10,
+ 191.9561973, 12.7524882154, 23871.4457149091, 8.6124629E-05, 3.1435804E-07, -1.4950684E-09, 164.7286185, 10.4400593249, 14914.4523349355, -6.3524240E-05, 6.3330532E-08, -2.5428962E-10, -147.3213842, -2.3986802540, -7700.3894694766, -1.5497663E-04, -2.4979472E-07, 1.1318993E-09, -124.9881185, 5.1984668216, 7771.3771450920, -3.3094061E-05, 3.1973462E-08, -1.5436468E-10,
+ -109.3803637, 2.3124288905, 8956.9933799736, 1.4964887E-04, 2.5102751E-07, -1.2407788E-09, 55.1770578, 7.1411231536, -1324.1780250970, 6.1854469E-05, 7.3846820E-08, -3.4916281E-10, -45.0996092, 5.6113650618, 25195.6237400061, 2.4270161E-05, 2.4051122E-07, -1.1459056E-09, 39.5333010, -0.9002559173, -8538.2408905558, 2.8035534E-04, 2.6031101E-07, -1.2267725E-09,
+ 38.4298346, 18.4383127140, 22756.8171556428, -2.8468899E-04, -1.2251727E-07, 5.6888037E-10, 36.1238141, 7.0666637168, 24986.0742741754, 4.5693825E-04, 7.5123334E-07, -3.5590172E-09, 30.7725751, 16.0827581417, 14428.1257309177, -4.3700174E-04, -3.7292838E-07, 1.7552195E-09, -28.3971008, 7.9982533891, 7842.3648207073, -2.2116475E-04, -1.8584780E-07, 8.2317000E-10,
+ -24.3582283, 10.3538079614, 16171.0562454324, -6.8852003E-05, 6.4563317E-08, -3.6316908E-10, -18.5847068, 2.8429122493, -557.3142796331, -1.8540681E-04, -2.1843765E-07, 1.0319744E-09, 17.9544674, 5.1553411398, 8399.6791003405, -3.5757942E-05, 3.2589854E-08, -2.0880440E-10, 14.5302779, 12.7956138971, 23243.1437596606, 8.8788511E-05, 3.1374165E-07, -1.4406287E-09,
+ 14.3796974, 15.1080427876, 32200.1371396342, 2.3843738E-04, 5.6476915E-07, -2.6814075E-09, 14.2514576, -24.0810366320, -2.3011998397, 1.5231275E-04, 2.5041111E-07, -1.1863391E-09, 13.8990596, 20.7938672862, 31085.5085803679, -1.3237624E-04, 1.2789385E-07, -6.1745870E-10, 13.1940636, 3.3302699264, -9443.3199839914, -5.2312637E-04, -6.8728642E-07, 3.2502879E-09,
+ -9.6790568, -4.7542348263, -16029.0808942018, -3.0728938E-04, -5.0020584E-07, 2.3182384E-09, -9.3658635, 11.2971895604, 24080.9951807398, -3.4654346E-04, -1.9636409E-07, 9.1804319E-10, 8.6055318, 5.7289501804, -1742.9305145148, -3.6814974E-04, -4.3749170E-07, 2.1183885E-09, -8.4530982, 7.5540213938, 16100.0685698171, 1.1921869E-04, 2.8238458E-07, -1.3407038E-09,
+ 8.0501724, 10.4831850066, 14286.1503796870, -6.0860358E-05, 6.2714140E-08, -1.9984990E-10, -7.6301553, 4.6679834628, 17285.6848046987, 3.0196162E-04, 5.0143862E-07, -2.4271179E-09, -7.4474952, -0.0862513635, 1256.6039104970, -5.3277630E-06, 1.2327842E-09, -1.0887946E-10, 7.3712011, 8.1276304344, 5957.4589549619, -2.1317311E-04, -1.8769697E-07, 9.8648918E-10,
+ 7.0629900, 0.9591375719, 33.7570471374, -3.0829302E-05, -3.6967043E-08, 1.7385419E-10, -6.3831491, 9.4966777258, 7004.5133996281, 2.1416722E-04, 3.2425793E-07, -1.5355019E-09, -5.7416071, 13.6527441326, 32409.6866054649, -1.9423071E-04, 5.4047029E-08, -2.6829589E-10, 4.3740095, 18.4814383957, 22128.5152003943, -2.8202511E-04, -1.2313366E-07, 6.2332010E-10,
+ -3.9976134, 7.9669196340, 33524.3151647312, 1.7658291E-04, 4.9092233E-07, -2.3322447E-09, -3.2096876, 13.2398458924, 14985.4400105508, -2.5159493E-04, -1.5449073E-07, 7.2324505E-10, -2.9145404, 12.7093625336, 24499.7476701576, 8.3460748E-05, 3.1497443E-07, -1.5495082E-09, 2.7318890, 16.1258838235, 13799.8237756692, -4.3433786E-04, -3.7354477E-07, 1.8096592E-09,
+ -2.5679459, -2.4418059357, -7072.0875142282, -1.5764051E-04, -2.4917833E-07, 1.0774596E-09, -2.5211990, 7.9551277074, 8470.6667759558, -2.2382863E-04, -1.8523141E-07, 7.6873027E-10, 2.4888871, 5.6426988169, -486.3266040178, -3.7347750E-04, -4.3625891E-07, 2.0095091E-09, 2.1460741, 7.1842488353, -1952.4799803455, 6.4518350E-05, 7.3230428E-08, -2.9472308E-10,
+ 1.9777270, 23.1494218585, 39414.2000050930, 1.9936508E-05, 3.7830496E-07, -1.8037978E-09, 1.9336825, 9.4222182890, 33314.7656989005, 6.0925100E-04, 1.0016445E-06, -4.7453563E-09, 1.8707647, 20.8369929680, 30457.2066251194, -1.2971236E-04, 1.2727746E-07, -5.6301898E-10, -1.7529659, 0.4873576771, -8886.0057043583, -3.3771956E-04, -4.6884877E-07, 2.2183135E-09,
+ -1.4371624, 7.0979974718, -695.8760698485, 5.9190587E-05, 7.4463212E-08, -4.0360254E-10, -1.3725701, 1.4552986550, -209.5494658307, 4.3266809E-04, 5.1072212E-07, -2.4131116E-09, 1.2618162, 7.5108957121, 16728.3705250656, 1.1655481E-04, 2.8300097E-07, -1.3951435E-09};
+ private static final double M11[] = {
+ 1.6768000, -0.0431256817, 628.3019552485, -2.6638815E-06, 6.1639211E-10, -5.4439728E-11, 0.5164200, 11.2260974062, 6585.7609102104, -2.1583699E-04, -1.8708058E-07, 9.3204945E-10, 0.4138300, 13.5816519784, 14914.4523349355, -6.3524240E-05, 6.3330532E-08, -2.5428962E-10, 0.3711500, 5.5402729076, 7700.3894694766, 1.5497663E-04, 2.4979472E-07, -1.1318993E-09,
+ 0.2756000, 2.3124288905, 8956.9933799736, 1.4964887E-04, 2.5102751E-07, -1.2407788E-09, 0.2459863, -25.6198212459, -2.3011998397, 1.5231275E-04, 2.5041111E-07, -1.1863391E-09, 0.0711800, 7.9982533891, 7842.3648207073, -2.2116475E-04, -1.8584780E-07, 8.2317000E-10, 0.0612800, 10.3538079614, 16171.0562454324, -6.8852003E-05, 6.4563317E-08, -3.6316908E-10};
+ private static final double M12[] = {0.0048700, -0.0431256817, 628.3019552485, -2.6638815E-06, 6.1639211E-10, -5.4439728E-11, 0.0022800, -27.1705318325, -2.3011998397, 1.5231275E-04, 2.5041111E-07, -1.1863391E-09, 0.0015000, 11.2260974062, 6585.7609102104, -2.1583699E-04, -1.8708058E-07, 9.3204945E-10};
+ private static final double M20[] = {
+ 18461.2400600, 1.6279052448, 8433.4661576405, -6.4021295E-05, -4.9499477E-09, 2.0216731E-11, 1010.1671484, 3.9834598170, 16762.1575823656, 8.8291456E-05, 2.4546117E-07, -1.1661223E-09, 999.6936555, 0.7276493275, -104.7747329154, 2.1633405E-04, 2.5536106E-07, -1.2065558E-09, 623.6524746, 8.7690283983, 7109.2881325435, -2.1668263E-06, 6.8896872E-08, -3.2894608E-10,
+ 199.4837596, 9.6692843156, 15647.5290230993, -2.8252217E-04, -1.9141414E-07, 8.9782646E-10, 166.5741153, 6.4134738261, -1219.4032921817, -1.5447958E-04, -1.8151424E-07, 8.5739300E-10, 117.2606951, 12.0248388879, 23976.2204478244, -1.3020942E-04, 5.8996977E-08, -2.8851262E-10, 61.9119504, 6.3390143893, 25090.8490070907, 2.4060421E-04, 4.9587228E-07, -2.3524614E-09,
+ 33.3572027, 11.1245829706, 15437.9795572686, 1.5014592E-04, 3.1930799E-07, -1.5152852E-09, 31.7596709, 3.0832038997, 8223.9166918098, 3.6864680E-04, 5.0577218E-07, -2.3928949E-09, 29.5766003, 8.8121540801, 6480.9861772950, 4.9705523E-07, 6.8280480E-08, -2.7450635E-10, 15.5662654, 4.0579192538, -9548.0947169068, -3.0679233E-04, -4.3192536E-07, 2.0437321E-09,
+ 15.1215543, 14.3803934601, 32304.9118725496, 2.2103334E-05, 3.0940809E-07, -1.4748517E-09, -12.0941511, 8.7259027166, 7737.5900877920, -4.8307078E-06, 6.9513264E-08, -3.8338581E-10, 8.8681426, 9.7124099974, 15019.2270678508, -2.7985829E-04, -1.9203053E-07, 9.5226618E-10, 8.0450400, 0.6687636586, 8399.7091105030, -3.3191993E-05, 3.2017096E-08, -1.5363746E-10,
+ 7.9585542, 12.0679645696, 23347.9184925760, -1.2754553E-04, 5.8380585E-08, -2.3407289E-10, 7.4345550, 6.4565995078, -1847.7052474301, -1.5181570E-04, -1.8213063E-07, 9.1183272E-10, -6.7314363, -4.0265854988, -16133.8556271171, -9.0955337E-05, -2.4484477E-07, 1.1116826E-09, 6.5795750, 16.8104074692, 14323.3509980023, -2.2066770E-04, -1.1756732E-07, 5.4866364E-10,
+ -6.4600721, 1.5847795630, 9061.7681128890, -6.6685176E-05, -4.3335556E-09, -3.4222998E-11, -6.2964773, 4.8837157343, 25300.3984729215, -1.9206388E-04, -1.4849843E-08, 6.0650192E-11, -5.6323538, -0.7707750092, 733.0766881638, -2.1899793E-04, -2.5474467E-07, 1.1521161E-09, -5.3683961, 6.8263720663, 16204.8433027325, -9.7115356E-05, 2.7023515E-08, -1.3414795E-10,
+ -5.3112784, 3.9403341353, 17390.4595376141, 8.5627574E-05, 2.4607756E-07, -1.2205621E-09, -5.0759179, 0.6845236457, 523.5272223331, 2.1367016E-04, 2.5597745E-07, -1.2609955E-09, -4.8396143, -1.6710309265, -7805.1642023920, 6.1357413E-05, 5.5663398E-09, -7.4656459E-11, -4.8057401, 3.5705615768, -662.0890125485, 3.0927234E-05, 3.6923410E-08, -1.7458141E-10,
+ 3.9840545, 8.6945689615, 33419.5404318159, 3.9291696E-04, 7.4628340E-07, -3.5388005E-09, 3.6744619, 19.1659620415, 22652.0424227274, -6.8354947E-05, 1.3284380E-07, -6.3767543E-10, 2.9984815, 20.0662179587, 31190.2833132833, -3.4871029E-04, -1.2746721E-07, 5.8909710E-10, 2.7986413, -2.5281611620, -16971.7070481963, 3.4437664E-04, 2.6526096E-07, -1.2469893E-09,
+ 2.4138774, 17.7106633865, 22861.5918885581, -5.0102304E-04, -3.7787833E-07, 1.7754362E-09, 2.1863132, 5.5132179088, -9757.6441827375, 1.2587576E-04, 7.8796768E-08, -3.6937954E-10, 2.1461692, 13.4801375428, 23766.6709819937, 3.0245868E-04, 5.6971910E-07, -2.7016242E-09, 1.7659832, 11.1677086523, 14809.6776020201, 1.5280981E-04, 3.1869159E-07, -1.4608454E-09,
+ -1.6244212, 7.3137297434, 7318.8375983742, -4.3483492E-04, -4.4182525E-07, 2.0841655E-09, 1.5813036, 5.4387584720, 16552.6081165349, 5.2095955E-04, 7.5618329E-07, -3.5792340E-09, 1.5197528, 16.7359480324, 40633.6032972747, 1.7441609E-04, 5.5981921E-07, -2.6611908E-09, 1.5156341, 1.7023646816, -17876.7861416319, -4.5910508E-04, -6.8233647E-07, 3.2300712E-09,
+ 1.5102092, 5.4977296450, 8399.6847301375, -3.3094061E-05, 3.1973462E-08, -1.5436468E-10, -1.3178223, 9.6261586339, 16275.8309783478, -2.8518605E-04, -1.9079775E-07, 8.4338673E-10, -1.2642739, 11.9817132061, 24604.5224030729, -1.3287330E-04, 5.9613369E-08, -3.4295235E-10, 1.1918723, 22.4217725310, 39518.9747380084, -1.9639754E-04, 1.2294390E-07, -5.9724197E-10,
+ 1.1346110, 14.4235191419, 31676.6099173011, 2.4767216E-05, 3.0879170E-07, -1.4204120E-09, 1.0857810, 8.8552797618, 5852.6842220465, 3.1609367E-06, 6.7664088E-08, -2.2006663E-10, -1.0193852, 7.2392703065, 33629.0898976466, -3.9751134E-05, 2.3556127E-07, -1.1256889E-09, -0.8227141, 11.0814572888, 16066.2815125171, 1.4748204E-04, 3.1992438E-07, -1.5697249E-09,
+ 0.8042238, 3.5274358950, -33.7870573000, 2.8263353E-05, 3.7539802E-08, -2.2902113E-10, 0.8025939, 6.7832463846, 16833.1452579809, -9.9779237E-05, 2.7639907E-08, -1.8858767E-10, -0.7931866, -6.3821400710, -24462.5470518423, -2.4326809E-04, -4.9525589E-07, 2.2980217E-09, -0.7910153, 6.3703481443, -591.1013369332, -1.5714346E-04, -1.8089785E-07, 8.0295327E-10,
+ -0.6674056, 9.1819266386, 24533.5347274576, 5.5197395E-05, 2.7743463E-07, -1.3204870E-09, 0.6502226, 4.1010449356, -10176.3966721553, -3.0412845E-04, -4.3254175E-07, 2.0981718E-09, -0.6388131, 6.2958887075, 25719.1509623392, 2.3794032E-04, 4.9648867E-07, -2.4069012E-09};
+ private static final double M21[] = {
+ 0.0743000, 11.9537467337, 6480.9861772950, 4.9705523E-07, 6.8280480E-08, -2.7450635E-10, 0.0304300, 8.7259027166, 7737.5900877920, -4.8307078E-06, 6.9513264E-08, -3.8338581E-10, 0.0222900, 12.8540026510, 15019.2270678508, -2.7985829E-04, -1.9203053E-07, 9.5226618E-10, 0.0199900, 15.2095572232, 23347.9184925760, -1.2754553E-04, 5.8380585E-08, -2.3407289E-10,
+ 0.0186900, 9.5981921614, -1847.7052474301, -1.5181570E-04, -1.8213063E-07, 9.1183272E-10, 0.0169600, 7.1681781524, 16133.8556271171, 9.0955337E-05, 2.4484477E-07, -1.1116826E-09, 0.0162300, 1.5847795630, 9061.7681128890, -6.6685176E-05, -4.3335556E-09, -3.4222998E-11, 0.0141900, -0.7707750092, 733.0766881638, -2.1899793E-04, -2.5474467E-07, 1.1521161E-09};
+ private static final double M30[] = {
+ 385000.5290396, 1.5707963268, 0.0000000000, 0.0000000E+00, 0.0000000E+00, 0.0000000E+00, -20905.3551378, 3.9263508990, 8328.6914247251, 1.5231275E-04, 2.5041111E-07, -1.1863391E-09, -3699.1109330, 9.6121753977, 7214.0628654588, -2.1850087E-04, -1.8646419E-07, 8.7760973E-10, -2955.9675626, 11.9677299699, 15542.7542901840, -6.6188121E-05, 6.3946925E-08, -3.0872935E-10,
+ -569.9251264, 6.2819054713, 16657.3828494503, 3.0462550E-04, 5.0082223E-07, -2.3726782E-09, 246.1584797, 7.2566208254, -1114.6285592663, -3.7081362E-04, -4.3687530E-07, 2.0639488E-09, -204.5861179, 12.0108556517, 14914.4523349355, -6.3524240E-05, 6.3330532E-08, -2.5428962E-10, -170.7330791, 14.3232845422, 23871.4457149091, 8.6124629E-05, 3.1435804E-07, -1.4950684E-09,
+ -152.1378118, 9.6553010794, 6585.7609102104, -2.1583699E-04, -1.8708058E-07, 9.3204945E-10, -129.6202242, -0.8278839272, -7700.3894694766, -1.5497663E-04, -2.4979472E-07, 1.1318993E-09, 108.7427014, 6.7692631483, 7771.3771450920, -3.3094061E-05, 3.1973462E-08, -1.5436468E-10, 104.7552944, 3.8832252173, 8956.9933799736, 1.4964887E-04, 2.5102751E-07, -1.2407788E-09,
+ 79.6605685, 0.6705404095, -8538.2408905558, 2.8035534E-04, 2.6031101E-07, -1.2267725E-09, 48.8883284, 1.5276706450, 628.3019552485, -2.6638815E-06, 6.1639211E-10, -5.4439728E-11, -34.7825237, 20.0091090408, 22756.8171556428, -2.8468899E-04, -1.2251727E-07, 5.6888037E-10, 30.8238599, 11.9246042882, 16171.0562454324, -6.8852003E-05, 6.4563317E-08, -3.6316908E-10,
+ 24.2084985, 9.5690497159, 7842.3648207073, -2.2116475E-04, -1.8584780E-07, 8.2317000E-10, -23.2104305, 8.6374600436, 24986.0742741754, 4.5693825E-04, 7.5123334E-07, -3.5590172E-09, -21.6363439, 17.6535544685, 14428.1257309177, -4.3700174E-04, -3.7292838E-07, 1.7552195E-09, -16.6747239, 6.7261374666, 8399.6791003405, -3.5757942E-05, 3.2589854E-08, -2.0880440E-10,
+ 14.4026890, 4.9010662531, -9443.3199839914, -5.2312637E-04, -6.8728642E-07, 3.2502879E-09, -12.8314035, 14.3664102239, 23243.1437596606, 8.8788511E-05, 3.1374165E-07, -1.4406287E-09, -11.6499478, 22.3646636130, 31085.5085803679, -1.3237624E-04, 1.2789385E-07, -6.1745870E-10, -10.4447578, 16.6788391144, 32200.1371396342, 2.3843738E-04, 5.6476915E-07, -2.6814075E-09,
+ 10.3211071, 8.7119194804, -1324.1780250970, 6.1854469E-05, 7.3846820E-08, -3.4916281E-10, 10.0562033, 7.2997465071, -1742.9305145148, -3.6814974E-04, -4.3749170E-07, 2.1183885E-09, -9.8844667, 12.0539813334, 14286.1503796870, -6.0860358E-05, 6.2714140E-08, -1.9984990E-10, 8.7515625, 6.3563649081, -9652.8694498221, -9.0458282E-05, -1.7656429E-07, 8.3717626E-10,
+ -8.3791067, 4.4137085761, -557.3142796331, -1.8540681E-04, -2.1843765E-07, 1.0319744E-09, -7.0026961, -3.1834384995, -16029.0808942018, -3.0728938E-04, -5.0020584E-07, 2.3182384E-09, 6.3220032, 9.1248177206, 16100.0685698171, 1.1921869E-04, 2.8238458E-07, -1.3407038E-09, 5.7508579, 6.2387797896, 17285.6848046987, 3.0196162E-04, 5.0143862E-07, -2.4271179E-09,
+ -4.9501349, 9.6984267611, 5957.4589549619, -2.1317311E-04, -1.8769697E-07, 9.8648918E-10, -4.4211770, 3.0260949818, -209.5494658307, 4.3266809E-04, 5.1072212E-07, -2.4131116E-09, 4.1311145, 11.0674740526, 7004.5133996281, 2.1416722E-04, 3.2425793E-07, -1.5355019E-09, -3.9579827, 20.0522347225, 22128.5152003943, -2.8202511E-04, -1.2313366E-07, 6.2332010E-10,
+ 3.2582371, 14.8106422192, 14985.4400105508, -2.5159493E-04, -1.5449073E-07, 7.2324505E-10, -3.1483020, 4.8266068163, 16866.9323152810, -1.2804259E-04, -9.8998954E-09, 4.0433461E-11, 2.6164092, 14.2801588604, 24499.7476701576, 8.3460748E-05, 3.1497443E-07, -1.5495082E-09, 2.3536310, 9.5259240342, 8470.6667759558, -2.2382863E-04, -1.8523141E-07, 7.6873027E-10,
+ -2.1171283, -0.8710096090, -7072.0875142282, -1.5764051E-04, -2.4917833E-07, 1.0774596E-09, -1.8970368, 17.6966801503, 13799.8237756692, -4.3433786E-04, -3.7354477E-07, 1.8096592E-09, -1.7385258, 2.0581540038, -8886.0057043583, -3.3771956E-04, -4.6884877E-07, 2.2183135E-09, -1.5713944, 22.4077892948, 30457.2066251194, -1.2971236E-04, 1.2727746E-07, -5.6301898E-10,
+ -1.4225541, 24.7202181853, 39414.2000050930, 1.9936508E-05, 3.7830496E-07, -1.8037978E-09, -1.4189284, 17.1661967915, 23314.1314352759, -9.9282182E-05, 9.5920387E-08, -4.6309403E-10, 1.1655364, 3.8400995356, 9585.2953352221, 1.4698499E-04, 2.5164390E-07, -1.2952185E-09, -1.1169371, 10.9930146158, 33314.7656989005, 6.0925100E-04, 1.0016445E-06, -4.7453563E-09,
+ 1.0656723, 1.4845449633, 1256.6039104970, -5.3277630E-06, 1.2327842E-09, -1.0887946E-10, 1.0586190, 11.9220903668, 8364.7398411275, -2.1850087E-04, -1.8646419E-07, 8.7760973E-10, -0.9333176, 9.0816920389, 16728.3705250656, 1.1655481E-04, 2.8300097E-07, -1.3951435E-09, 0.8624328, 12.4550876470, 6656.7485858257, -4.0390768E-04, -4.0490184E-07, 1.9095841E-09,
+ 0.8512404, 4.3705828944, 70.9876756153, -1.8807069E-04, -2.1782126E-07, 9.7753467E-10, -0.8488018, 16.7219647962, 31571.8351843857, 2.4110126E-04, 5.6415276E-07, -2.6269678E-09, -0.7956264, 3.5134526588, -9095.5551701890, 9.4948529E-05, 4.1873358E-08, -1.9479814E-10};
+ private static final double M31[] = {
+ 0.5139500, 12.0108556517, 14914.4523349355, -6.3524240E-05, 6.3330532E-08, -2.5428962E-10, 0.3824500, 9.6553010794, 6585.7609102104, -2.1583699E-04, -1.8708058E-07, 9.3204945E-10, 0.3265400, 3.9694765808, 7700.3894694766, 1.5497663E-04, 2.4979472E-07, -1.1318993E-09, 0.2639600, 0.7416325637, 8956.9933799736, 1.4964887E-04, 2.5102751E-07, -1.2407788E-09,
+ 0.1230200, -1.6139220085, 628.3019552485, -2.6638815E-06, 6.1639211E-10, -5.4439728E-11, 0.0775400, 8.7830116346, 16171.0562454324, -6.8852003E-05, 6.4563317E-08, -3.6316908E-10, 0.0606800, 6.4274570623, 7842.3648207073, -2.2116475E-04, -1.8584780E-07, 8.2317000E-10, 0.0497000, 12.0539813334, 14286.1503796870, -6.0860358E-05, 6.2714140E-08, -1.9984990E-10};
+ private static final double M1n[] = {3.81034392032, 8.39968473021E+03, -3.31919929753E-05, //月球平黄经系数
+ 3.20170955005E-08, -1.53637455544E-10};
+
+
+ private static double EnnT = 0; // 调用Enn前先设置EnnT时间变量
+
+ /**
+ * 计算E10,E11,E20等,即:某一组周期项或泊松项算出,计算前先设置EnnT时间
+ *
+ * @param F F
+ * @return 计算E10, E11, E20等
+ */
+ private static double Enn(double[] F) {
+ double v = 0;
+ for (int i = 0; i < F.length; i += 3)
+ v += F[i] * Math.cos(F[i + 1] + EnnT * F[i + 2]);
+ return v;
+ }
+
+ /**
+ * 返回地球位置,日心Date黄道分点坐标
+ *
+ * @param jd jd
+ * @return 返回地球位置, 日心Date黄道分点坐标
+ */
+ private static double[] earCal(double jd) {
+ EnnT = jd / 365250;
+ double llr[] = new double[3];
+ double t1 = EnnT, t2 = t1 * t1, t3 = t2 * t1, t4 = t3 * t1, t5 = t4
+ * t1;
+ llr[0] = Enn(E10) + Enn(E11) * t1 + Enn(E12) * t2 + Enn(E13) * t3
+ + Enn(E14) * t4 + Enn(E15) * t5;
+ llr[1] = Enn(E20) + Enn(E21) * t1;
+ llr[2] = Enn(E30) + Enn(E31) * t1 + Enn(E32) * t2 + Enn(E33) * t3;
+ llr[0] = rad2mrad(llr[0]);
+ return llr;
+ }
+
+
+ // ==================月位置计算===================
+ private static double MnnT = 0; // 调用Mnn前先设置MnnT时间变量
+
+ /**
+ * 计算M10,M11,M20等,计算前先设置MnnT时间
+ *
+ * @param F F
+ * @return 计算M10, M11, M20等, 计算前先设置MnnT时间
+ */
+ private static double Mnn(double[] F) {
+ double v = 0, t1 = MnnT, t2 = t1 * t1, t3 = t2 * t1, t4 = t3 * t1;
+ for (int i = 0; i < F.length; i += 6)
+ v += F[i]
+ * Math.sin(F[i + 1] + t1 * F[i + 2] + t2 * F[i + 3] + t3
+ * F[i + 4] + t4 * F[i + 5]);
+ return v;
+ }
+
+
+ /**
+ * 返回月球位置,返回地心Date黄道坐标
+ *
+ * @param julian 儒略历
+ * @return return 地心黄道坐标
+ */
+ private static double[] moonCoord(double julian) {
+ MnnT = julian / 36525;
+ double t1 = MnnT, t2 = t1 * t1, t3 = t2 * t1, t4 = t3 * t1;
+ double[] llr = new double[3];
+ llr[0] = (Mnn(M10) + Mnn(M11) * t1 + Mnn(M12) * t2) / SECOND_PER_RAD;
+ llr[1] = (Mnn(M20) + Mnn(M21) * t1) / SECOND_PER_RAD;
+ llr[2] = (Mnn(M30) + Mnn(M31) * t1) * 0.999999949827;
+ llr[0] = llr[0] + M1n[0] + M1n[1] * t1 + M1n[2] * t2 + M1n[3] * t3
+ + M1n[4] * t4;
+ llr[0] = rad2mrad(llr[0]); // 地心Date黄道原点坐标(不含岁差)
+ precession(julian, llr); // 补岁差
+ return llr;
+ }
+
+
+ /**
+ * 地心坐标中的日月位置计算
+ *
+ * @param lx lx=1时计算t时刻日月角距与jiao的差, lx=0计算t时刻太阳黄经与jiao的差
+ * @param time time
+ * @param angle angle
+ * @return 地心坐标中的日月位置计算
+ */
+ private static double angleDiff(int lx, double time, double angle) {
+ double[] sun = earCal(time); // 计算太阳真位置(先算出日心坐标中地球的位置)
+ sun[0] += Math.PI;
+ sun[1] = -sun[1]; // 转为地心坐标
+ addGxc(time, sun); // 补周年光行差
+ if (lx == 0) {
+ Nutation d = nutation(time);
+ sun[0] += d.Lon; // 补黄经章动
+ return rad2mrad(angle - sun[0]);
+ }
+ double[] moon = moonCoord(time); // 日月角差与章动无关
+ return rad2mrad(angle - (moon[0] - sun[0]));
+ }
+
+ /**
+ * 已知位置反求时间,对于节气计算,应满足t在t1到t1+360天之间,对于Y年第n个节气(n=0是春分),t1可取值Y*365.2422+n*15.2,
+ * 对于朔望计算,应满足t在t1到t1+25天之间,在此范围之外,求右边的根
+ *
+ * @param t1 J2000起算儒略日数,传入的t1是指定角度对应真时刻t的前一些天
+ * @param angle 已知角度(jiao)求时间(t)
+ * @param lx lx=0是太阳黄经达某角度的时刻计算(用于节气计算)
+ * lx=1是日月角距达某角度的时刻计算(用于定朔望等)
+ * @return 已知位置反求时间
+ */
+ private static double getTimeFromAngle(double t1, double angle, int lx) {
+ double t2 = t1, t = 0, v;
+ if (lx == 0)
+ t2 += 360; // 在t1到t2范围内求解(范气360天范围),结果置于t
+ else
+ t2 += 25;
+ angle *= Math.PI / 180; // 待搜索目标角
+ // 利用截弦法计算
+ double v1 = angleDiff(lx, t1, angle); // v1,v2为t1,t2时对应的黄经
+ double v2 = angleDiff(lx, t2, angle);
+ if (v1 < v2)
+ v2 -= 2 * Math.PI; // 减2pi作用是将周期性角度转为连续角度
+ double k = 1, k2; // k是截弦的斜率
+ for (int i = 0; i < 10; i++) { // 快速截弦求根,通常截弦三四次就已达所需精度
+ k2 = (v2 - v1) / (t2 - t1); // 算出斜率
+ if (Math.abs(k2) > 1e-15)
+ k = k2; // 差商可能为零,应排除
+ t = t1 - v1 / k;
+ v = angleDiff(lx, t, angle);// 直线逼近法求根(直线方程的根)
+ if (v > 1)
+ v -= 2 * Math.PI; // 一次逼近后,v1就已接近0,如果很大,则应减1周
+ if (Math.abs(v) < 1e-8)
+ break; // 已达精度
+ t1 = t2;
+ v1 = v2;
+ t2 = t;
+ v2 = v; // 下一次截弦
+ }
+ return t;
+ }
+
+
+ /**
+ * 获得某一年24节气
+ *
+ * @param year 年
+ * @return 24节气
+ */
+
+ public static String[] getSolarTerms(int year) {
+ String[] solarTerms = new String[24];
+ String[] preOffset = getSolarTermsPreOffset(year - 1);
+ String[] nextOffset = getSolarTermsNextOffset(year - 1);
+ System.arraycopy(preOffset, 0, solarTerms, 0, preOffset.length);
+ System.arraycopy(nextOffset, 0, solarTerms, 22, nextOffset.length);
+ double jd = 365.2422 * (year - 2000), q;
+ for (int i = 0; i < 19; i++) {
+ q = getTimeFromAngle(jd + i * 15.2, i * 15, 0);
+ q = q + J2000 + (double) 8 / 24; // 计算第i个节气(i=0是春分),结果转为北京时
+ Time time = setFromJulian(q, true);
+ solarTerms[i + 3] = time.toString() + SOLAR_TERMS[i];
+ }
+
+ return solarTerms;
+ }
+
+
+ /**
+ * 要获得2018年24节气需要传入2017年
+ *
+ * @param year 要获得2018年24节气需要传入2017年
+ * @return 返回 立春 雨水 惊蛰
+ */
+ private static String[] getSolarTermsPreOffset(int year) {
+ String[] solarTerms = new String[3];
+ double jd = 365.2422 * (year - 2000), q;
+ for (int i = 21; i < 24; i++) {
+ q = getTimeFromAngle(jd + i * 15.2, i * 15, 0);
+ q = q + J2000 + (double) 8 / 24; // 计算第i个节气(i=0是春分)
+ Time time = setFromJulian(q, true);
+ solarTerms[i - 21] = time.toString() + SOLAR_TERMS[i];
+ }
+ return solarTerms;
+ }
+
+ /**
+ * 要获得2018年24节气需要传入2017年
+ *
+ * @param year 要获得2018年24节气需要传入2017年
+ * @return 返回 小寒大寒
+ */
+ private static String[] getSolarTermsNextOffset(int year) {
+ String[] solarTerms = new String[2];
+ double jd = 365.2422 * (year - 2000), q;
+ for (int i = 19; i < 21; i++) {
+ q = getTimeFromAngle(jd + i * 15.2, i * 15, 0);
+ q = q + J2000 + (double) 8 / 24; // 计算第i个节气(i=0是春分)
+ Time time = setFromJulian(q, true);
+ solarTerms[i - 19] = time.toString() + SOLAR_TERMS[i];
+ }
+ return solarTerms;
+ }
+
+ /**
+ * 章动
+ */
+ private static class Nutation {
+ /**
+ * 章动角
+ */
+ private double Lon;
+
+ /**
+ * 交角
+ */
+ private double Obl;
+ }
+
+
+ private static class Time {
+ private double year;
+ private double month;
+ private double day;
+ private double hour;
+ private double minute;
+ private double second;
+
+ @Override
+ public String toString() {
+ return doubleToString(year) + doubleToString(month) + doubleToString(day);
+ }
+ }
+
+ private static String doubleToString(double value) {
+ int v = (int) value;
+ return value < 10 ? "0" + v : String.valueOf(v);
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/TrunkBranchAnnals.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/TrunkBranchAnnals.java
new file mode 100644
index 00000000..6c165cb0
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/TrunkBranchAnnals.java
@@ -0,0 +1,90 @@
+package com.haibin.calendarview;
+
+import android.content.Context;
+
+/**
+ * 干支纪年算法
+ * Created by huanghaibin on 2019/2/12.
+ */
+@SuppressWarnings("unused")
+public final class TrunkBranchAnnals {
+
+ /**
+ * 天干字符串
+ */
+ private static String[] TRUNK_STR = null;
+
+ /**
+ * 地支字符串
+ */
+ private static String[] BRANCH_STR = null;
+
+ /**
+ * 单独使用请先调用这个方法
+ * @param context context
+ */
+ public static void init(Context context) {
+ if (TRUNK_STR != null) {
+ return;
+ }
+ TRUNK_STR = context.getResources().getStringArray(R.array.trunk_string_array);
+ BRANCH_STR = context.getResources().getStringArray(R.array.branch_string_array);
+
+ }
+
+ /**
+ * 获取某一年对应天干文字
+ *
+ * @param year 年份
+ * @return 天干由甲到癸,每10一轮回
+ */
+ @SuppressWarnings("all")
+ public static String getTrunkString(int year) {
+ return TRUNK_STR[getTrunkInt(year)];
+ }
+
+ /**
+ * 获取某一年对应天干,
+ *
+ * @param year 年份
+ * @return 4 5 6 7 8 9 10 1 2 3
+ */
+ @SuppressWarnings("all")
+ public static int getTrunkInt(int year) {
+ int trunk = year % 10;
+ return trunk == 0 ? 9 : trunk - 1;
+ }
+
+ /**
+ * 获取某一年对应地支文字
+ *
+ * @param year 年份
+ * @return 地支由子到亥,每12一轮回
+ */
+ @SuppressWarnings("all")
+ public static String getBranchString(int year) {
+ return BRANCH_STR[getBranchInt(year)];
+ }
+
+ /**
+ * 获取某一年对应地支
+ *
+ * @param year 年份
+ * @return 4 5 6 7 8 9 10 11 12 1 2 3
+ */
+ @SuppressWarnings("all")
+ public static int getBranchInt(int year) {
+ int branch = year % 12;
+ return branch == 0 ? 11 : branch - 1;
+ }
+
+ /**
+ * 获取干支纪年
+ *
+ * @param year 年份
+ * @return 干支纪年
+ */
+ public static String getTrunkBranchYear(int year) {
+ return String.format("%s%s", getTrunkString(year), getBranchString(year));
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/WeekBar.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/WeekBar.java
new file mode 100644
index 00000000..772ad1d5
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/WeekBar.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * 星期栏,如果你要使用星期栏自定义,切记XML使用 merge,不要使用LinearLayout
+ * Created by huanghaibin on 2017/11/30.
+ */
+public class WeekBar extends LinearLayout {
+ private CalendarViewDelegate mDelegate;
+
+ public WeekBar(Context context) {
+ super(context);
+ if ("com.haibin.calendarview.WeekBar".equals(getClass().getName())) {
+ LayoutInflater.from(context).inflate(R.layout.cv_week_bar, this, true);
+ }
+ }
+
+ /**
+ * 传递属性
+ *
+ * @param delegate delegate
+ */
+ void setup(CalendarViewDelegate delegate) {
+ this.mDelegate = delegate;
+ if ("com.haibin.calendarview.WeekBar".equalsIgnoreCase(getClass().getName())) {
+ setTextSize(mDelegate.getWeekTextSize());
+ setTextColor(delegate.getWeekTextColor());
+ setBackgroundColor(delegate.getWeekBackground());
+ setPadding(delegate.getCalendarPaddingLeft(), 0, delegate.getCalendarPaddingRight(), 0);
+ }
+ }
+
+ /**
+ * 设置文本颜色,使用自定义布局需要重写这个方法,避免出问题
+ * 如果这里报错了,请确定你自定义XML文件跟布局是不是使用merge,而不是LinearLayout
+ *
+ * @param color color
+ */
+ protected void setTextColor(int color) {
+ for (int i = 0; i < getChildCount(); i++) {
+ ((TextView) getChildAt(i)).setTextColor(color);
+ }
+ }
+
+
+ /**
+ * 设置文本大小
+ *
+ * @param size size
+ */
+ protected void setTextSize(int size) {
+ for (int i = 0; i < getChildCount(); i++) {
+ ((TextView) getChildAt(i)).setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+ }
+ }
+
+ /**
+ * 日期选择事件,这里提供这个回调,可以方便定制WeekBar需要
+ *
+ * @param calendar calendar 选择的日期
+ * @param weekStart 周起始
+ * @param isClick isClick 点击
+ */
+ protected void onDateSelected(Calendar calendar, int weekStart, boolean isClick) {
+
+ }
+
+ /**
+ * 当周起始发生变化,使用自定义布局需要重写这个方法,避免出问题
+ *
+ * @param weekStart 周起始
+ */
+ protected void onWeekStartChange(int weekStart) {
+ if (!"com.haibin.calendarview.WeekBar".equalsIgnoreCase(getClass().getName())) {
+ return;
+ }
+ for (int i = 0; i < getChildCount(); i++) {
+ ((TextView) getChildAt(i)).setText(getWeekString(i, weekStart));
+ }
+ }
+
+
+ /**
+ * 通过View的位置和周起始获取星期的对应坐标
+ *
+ * @param calendar calendar
+ * @param weekStart weekStart
+ * @return 通过View的位置和周起始获取星期的对应坐标
+ */
+ protected int getViewIndexByCalendar(Calendar calendar, int weekStart) {
+ int week = calendar.getWeek() + 1;
+ if (weekStart == CalendarViewDelegate.WEEK_START_WITH_SUN) {
+ return week - 1;
+ }
+ if (weekStart == CalendarViewDelegate.WEEK_START_WITH_MON) {
+ return week == CalendarViewDelegate.WEEK_START_WITH_SUN ? 6 : week - 2;
+ }
+ return week == CalendarViewDelegate.WEEK_START_WITH_SAT ? 0 : week;
+ }
+
+ /**
+ * 或者周文本,这个方法仅供父类使用
+ *
+ * @param index index
+ * @param weekStart weekStart
+ * @return 或者周文本
+ */
+ private String getWeekString(int index, int weekStart) {
+ String[] weeks = getContext().getResources().getStringArray(R.array.week_string_array);
+
+ if (weekStart == CalendarViewDelegate.WEEK_START_WITH_SUN) {
+ return weeks[index];
+ }
+ if (weekStart == CalendarViewDelegate.WEEK_START_WITH_MON) {
+ return weeks[index == 6 ? 0 : index + 1];
+ }
+ return weeks[index == 0 ? 6 : index - 1];
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mDelegate != null) {
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(mDelegate.getWeekBarHeight(), MeasureSpec.EXACTLY);
+ } else {
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(CalendarUtil.dipToPx(getContext(), 40), MeasureSpec.EXACTLY);
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/WeekView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/WeekView.java
new file mode 100644
index 00000000..1d6d115a
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/WeekView.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.view.View;
+
+/**
+ * 周视图,因为日历UI采用热插拔实现,所以这里必须继承实现,达到UI一致即可
+ * Created by huanghaibin on 2017/11/21.
+ */
+public abstract class WeekView extends BaseWeekView {
+
+ public WeekView(Context context) {
+ super(context);
+ }
+
+ /**
+ * 绘制日历文本
+ *
+ * @param canvas canvas
+ */
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mItems.size() == 0)
+ return;
+ mItemWidth = (getWidth() -
+ mDelegate.getCalendarPaddingLeft() -
+ mDelegate.getCalendarPaddingRight()) / 7;
+ onPreviewHook();
+
+ for (int i = 0; i < mItems.size(); i++) {
+ int x = i * mItemWidth + mDelegate.getCalendarPaddingLeft();
+ onLoopStart(x);
+ Calendar calendar = mItems.get(i);
+ boolean isSelected = i == mCurrentItem;
+ boolean hasScheme = calendar.hasScheme();
+ if (hasScheme) {
+ boolean isDrawSelected = false;//是否继续绘制选中的onDrawScheme
+ if (isSelected) {
+ isDrawSelected = onDrawSelected(canvas, calendar, x, true);
+ }
+ if (isDrawSelected || !isSelected) {
+ //将画笔设置为标记颜色
+ mSchemePaint.setColor(calendar.getSchemeColor() != 0 ?
+ calendar.getSchemeColor() : mDelegate.getSchemeThemeColor());
+ onDrawScheme(canvas, calendar, x);
+ }
+ } else {
+ if (isSelected) {
+ onDrawSelected(canvas, calendar, x, false);
+ }
+ }
+ onDrawText(canvas, calendar, x, hasScheme, isSelected);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (!isClick) {
+ return;
+ }
+ Calendar calendar = getIndex();
+ if (calendar == null) {
+ return;
+ }
+ if (onCalendarIntercept(calendar)) {
+ mDelegate.mCalendarInterceptListener.onCalendarInterceptClick(calendar, true);
+ return;
+ }
+ if (!isInRange(calendar)) {
+ if (mDelegate.mCalendarSelectListener != null) {
+ mDelegate.mCalendarSelectListener.onCalendarOutOfRange(calendar);
+ }
+ return;
+ }
+
+ mCurrentItem = mItems.indexOf(calendar);
+
+ if (mDelegate.mInnerListener != null) {
+ mDelegate.mInnerListener.onWeekDateSelected(calendar, true);
+ }
+ if (mParentLayout != null) {
+ int i = CalendarUtil.getWeekFromDayInMonth(calendar, mDelegate.getWeekStart());
+ mParentLayout.updateSelectWeek(i);
+ }
+
+ if (mDelegate.mCalendarSelectListener != null) {
+ mDelegate.mCalendarSelectListener.onCalendarSelect(calendar, true);
+ }
+
+ invalidate();
+ }
+
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (mDelegate.mCalendarLongClickListener == null)
+ return false;
+ if (!isClick) {
+ return false;
+ }
+ Calendar calendar = getIndex();
+ if (calendar == null) {
+ return false;
+ }
+ if (onCalendarIntercept(calendar)) {
+ mDelegate.mCalendarInterceptListener.onCalendarInterceptClick(calendar, true);
+ return true;
+ }
+ boolean isCalendarInRange = isInRange(calendar);
+
+ if (!isCalendarInRange) {
+ if (mDelegate.mCalendarLongClickListener != null) {
+ mDelegate.mCalendarLongClickListener.onCalendarLongClickOutOfRange(calendar);
+ }
+ return true;
+ }
+
+ if (mDelegate.isPreventLongPressedSelected()) {//如果启用拦截长按事件不选择日期
+ if (mDelegate.mCalendarLongClickListener != null) {
+ mDelegate.mCalendarLongClickListener.onCalendarLongClick(calendar);
+ }
+ return true;
+ }
+
+
+ mCurrentItem = mItems.indexOf(calendar);
+
+ mDelegate.mIndexCalendar = mDelegate.mSelectedCalendar;
+
+ if (mDelegate.mInnerListener != null) {
+ mDelegate.mInnerListener.onWeekDateSelected(calendar, true);
+ }
+ if (mParentLayout != null) {
+ int i = CalendarUtil.getWeekFromDayInMonth(calendar, mDelegate.getWeekStart());
+ mParentLayout.updateSelectWeek(i);
+ }
+
+ if (mDelegate.mCalendarSelectListener != null) {
+ mDelegate.mCalendarSelectListener.onCalendarSelect(calendar, true);
+ }
+
+ if (mDelegate.mCalendarLongClickListener != null) {
+ mDelegate.mCalendarLongClickListener.onCalendarLongClick(calendar);
+ }
+
+ invalidate();
+ return true;
+ }
+
+ /**
+ * 绘制选中的日期
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @return 是否绘制 onDrawScheme
+ */
+ protected abstract boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme);
+
+ /**
+ * 绘制标记的日期
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ */
+ protected abstract void onDrawScheme(Canvas canvas, Calendar calendar, int x);
+
+
+ /**
+ * 绘制日历文本
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param hasScheme 是否是标记的日期
+ * @param isSelected 是否选中
+ */
+ protected abstract void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected);
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/WeekViewPager.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/WeekViewPager.java
new file mode 100644
index 00000000..fcc36cf7
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/WeekViewPager.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+import java.lang.reflect.Constructor;
+import java.util.List;
+
+/**
+ * 周视图滑动ViewPager,需要动态固定高度
+ * 周视图是连续不断的视图,因此不能简单的得出每年都有52+1周,这样会计算重叠的部分
+ * WeekViewPager需要和CalendarView关联:
+ */
+
+public final class WeekViewPager extends ViewPager {
+ private boolean isUpdateWeekView;
+ private int mWeekCount;
+ private CalendarViewDelegate mDelegate;
+
+ /**
+ * 日历布局,需要在日历下方放自己的布局
+ */
+ CalendarLayout mParentLayout;
+
+ /**
+ * 是否使用滚动到某一天
+ */
+ private boolean isUsingScrollToCalendar = false;
+
+ public WeekViewPager(Context context) {
+ this(context, null);
+ }
+
+ public WeekViewPager(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ void setup(CalendarViewDelegate delegate) {
+ this.mDelegate = delegate;
+ init();
+ }
+
+ private void init() {
+ mWeekCount = CalendarUtil.getWeekCountBetweenBothCalendar(
+ mDelegate.getMinYear(),
+ mDelegate.getMinYearMonth(),
+ mDelegate.getMinYearDay(),
+ mDelegate.getMaxYear(),
+ mDelegate.getMaxYearMonth(),
+ mDelegate.getMaxYearDay(),
+ mDelegate.getWeekStart());
+ setAdapter(new WeekViewPagerAdapter());
+ addOnPageChangeListener(new OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ //默认的显示星期四,周视图切换就显示星期4
+ if (getVisibility() != VISIBLE) {
+ isUsingScrollToCalendar = false;
+ return;
+ }
+ if (isUsingScrollToCalendar) {
+ isUsingScrollToCalendar = false;
+ return;
+ }
+ BaseWeekView view = findViewWithTag(position);
+ if (view != null) {
+ view.performClickCalendar(mDelegate.getSelectMode() != CalendarViewDelegate.SELECT_MODE_DEFAULT ?
+ mDelegate.mIndexCalendar : mDelegate.mSelectedCalendar, !isUsingScrollToCalendar);
+ if (mDelegate.mWeekChangeListener != null) {
+ mDelegate.mWeekChangeListener.onWeekChange(getCurrentWeekCalendars());
+ }
+ }
+ isUsingScrollToCalendar = false;
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ });
+ }
+
+ /**
+ * 获取当前周数据
+ *
+ * @return 获取当前周数据
+ */
+ List getCurrentWeekCalendars() {
+ List calendars = CalendarUtil.getWeekCalendars(mDelegate.mIndexCalendar,
+ mDelegate);
+ mDelegate.addSchemesFromMap(calendars);
+ return calendars;
+ }
+
+
+ /**
+ * 更新周视图
+ */
+ void notifyDataSetChanged() {
+ mWeekCount = CalendarUtil.getWeekCountBetweenBothCalendar(
+ mDelegate.getMinYear(),
+ mDelegate.getMinYearMonth(),
+ mDelegate.getMinYearDay(),
+ mDelegate.getMaxYear(),
+ mDelegate.getMaxYearMonth(),
+ mDelegate.getMaxYearDay(),
+ mDelegate.getWeekStart());
+ notifyAdapterDataSetChanged();
+ }
+
+ /**
+ * 更新周视图布局
+ */
+ void updateWeekViewClass() {
+ isUpdateWeekView = true;
+ notifyAdapterDataSetChanged();
+ isUpdateWeekView = false;
+ }
+
+ /**
+ * 更新日期范围
+ */
+ void updateRange() {
+ isUpdateWeekView = true;
+ notifyDataSetChanged();
+ isUpdateWeekView = false;
+ if (getVisibility() != VISIBLE) {
+ return;
+ }
+ isUsingScrollToCalendar = true;
+ Calendar calendar = mDelegate.mSelectedCalendar;
+ updateSelected(calendar, false);
+ if (mDelegate.mInnerListener != null) {
+ mDelegate.mInnerListener.onWeekDateSelected(calendar, false);
+ }
+
+ if (mDelegate.mCalendarSelectListener != null) {
+ mDelegate.mCalendarSelectListener.onCalendarSelect(calendar, false);
+ }
+
+ int i = CalendarUtil.getWeekFromDayInMonth(calendar, mDelegate.getWeekStart());
+ mParentLayout.updateSelectWeek(i);
+ }
+
+ /**
+ * 滚动到指定日期
+ *
+ * @param year 年
+ * @param month 月
+ * @param day 日
+ * @param invokeListener 调用日期事件
+ */
+ void scrollToCalendar(int year, int month, int day, boolean smoothScroll, boolean invokeListener) {
+ isUsingScrollToCalendar = true;
+ Calendar calendar = new Calendar();
+ calendar.setYear(year);
+ calendar.setMonth(month);
+ calendar.setDay(day);
+ calendar.setCurrentDay(calendar.equals(mDelegate.getCurrentDay()));
+ LunarCalendar.setupLunarCalendar(calendar);
+ mDelegate.mIndexCalendar = calendar;
+ mDelegate.mSelectedCalendar = calendar;
+ mDelegate.updateSelectCalendarScheme();
+ updateSelected(calendar, smoothScroll);
+ if (mDelegate.mInnerListener != null) {
+ mDelegate.mInnerListener.onWeekDateSelected(calendar, false);
+ }
+ if (mDelegate.mCalendarSelectListener != null && invokeListener) {
+ mDelegate.mCalendarSelectListener.onCalendarSelect(calendar, false);
+ }
+ int i = CalendarUtil.getWeekFromDayInMonth(calendar, mDelegate.getWeekStart());
+ mParentLayout.updateSelectWeek(i);
+ }
+
+ /**
+ * 滚动到当前
+ */
+ void scrollToCurrent(boolean smoothScroll) {
+ isUsingScrollToCalendar = true;
+ int position = CalendarUtil.getWeekFromCalendarStartWithMinCalendar(mDelegate.getCurrentDay(),
+ mDelegate.getMinYear(),
+ mDelegate.getMinYearMonth(),
+ mDelegate.getMinYearDay(),
+ mDelegate.getWeekStart()) - 1;
+ int curItem = getCurrentItem();
+ if (curItem == position) {
+ isUsingScrollToCalendar = false;
+ }
+ setCurrentItem(position, smoothScroll);
+ BaseWeekView view = findViewWithTag(position);
+ if (view != null) {
+ view.performClickCalendar(mDelegate.getCurrentDay(), false);
+ view.setSelectedCalendar(mDelegate.getCurrentDay());
+ view.invalidate();
+ }
+
+ if (mDelegate.mCalendarSelectListener != null && getVisibility() == VISIBLE) {
+ mDelegate.mCalendarSelectListener.onCalendarSelect(mDelegate.mSelectedCalendar, false);
+ }
+
+ if (getVisibility() == VISIBLE) {
+ mDelegate.mInnerListener.onWeekDateSelected(mDelegate.getCurrentDay(), false);
+ }
+ int i = CalendarUtil.getWeekFromDayInMonth(mDelegate.getCurrentDay(), mDelegate.getWeekStart());
+ mParentLayout.updateSelectWeek(i);
+ }
+
+ /**
+ * 更新任意一个选择的日期
+ */
+ void updateSelected(Calendar calendar, boolean smoothScroll) {
+ int position = CalendarUtil.getWeekFromCalendarStartWithMinCalendar(calendar,
+ mDelegate.getMinYear(),
+ mDelegate.getMinYearMonth(),
+ mDelegate.getMinYearDay(),
+ mDelegate.getWeekStart()) - 1;
+ int curItem = getCurrentItem();
+ isUsingScrollToCalendar = curItem != position;
+ setCurrentItem(position, smoothScroll);
+ BaseWeekView view = findViewWithTag(position);
+ if (view != null) {
+ view.setSelectedCalendar(calendar);
+ view.invalidate();
+ }
+ }
+
+
+ /**
+ * 更新单选模式
+ */
+ void updateSingleSelect() {
+ if (mDelegate.getSelectMode() == CalendarViewDelegate.SELECT_MODE_DEFAULT) {
+ return;
+ }
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseWeekView view = (BaseWeekView) getChildAt(i);
+ view.updateSingleSelect();
+ }
+ }
+
+ /**
+ * 更新为默认选择模式
+ */
+ void updateDefaultSelect() {
+ BaseWeekView view = findViewWithTag(getCurrentItem());
+ if (view != null) {
+ view.setSelectedCalendar(mDelegate.mSelectedCalendar);
+ view.invalidate();
+ }
+ }
+
+ /**
+ * 更新选择效果
+ */
+ void updateSelected() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseWeekView view = (BaseWeekView) getChildAt(i);
+ view.setSelectedCalendar(mDelegate.mSelectedCalendar);
+ view.invalidate();
+ }
+ }
+
+ /**
+ * 更新字体颜色大小
+ */
+ final void updateStyle() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseWeekView view = (BaseWeekView) getChildAt(i);
+ view.updateStyle();
+ view.invalidate();
+ }
+ }
+
+ /**
+ * 更新标记日期
+ */
+ void updateScheme() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseWeekView view = (BaseWeekView) getChildAt(i);
+ view.update();
+ }
+ }
+
+ /**
+ * 更新当前日期,夜间过度的时候调用这个函数,一般不需要调用
+ */
+ void updateCurrentDate() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseWeekView view = (BaseWeekView) getChildAt(i);
+ view.updateCurrentDate();
+ }
+ }
+
+ /**
+ * 更新显示模式
+ */
+ void updateShowMode() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseWeekView view = (BaseWeekView) getChildAt(i);
+ view.updateShowMode();
+ }
+ }
+
+ /**
+ * 更新周起始
+ */
+ void updateWeekStart() {
+ if (getAdapter() == null) {
+ return;
+ }
+ int count = getAdapter().getCount();
+ mWeekCount = CalendarUtil.getWeekCountBetweenBothCalendar(
+ mDelegate.getMinYear(),
+ mDelegate.getMinYearMonth(),
+ mDelegate.getMinYearDay(),
+ mDelegate.getMaxYear(),
+ mDelegate.getMaxYearMonth(),
+ mDelegate.getMaxYearDay(),
+ mDelegate.getWeekStart());
+ /*
+ * 如果count发生变化,意味着数据源变化,则必须先调用notifyDataSetChanged(),
+ * 否则会抛出异常
+ */
+ if (count != mWeekCount) {
+ isUpdateWeekView = true;
+ getAdapter().notifyDataSetChanged();
+ }
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseWeekView view = (BaseWeekView) getChildAt(i);
+ view.updateWeekStart();
+ }
+ isUpdateWeekView = false;
+ updateSelected(mDelegate.mSelectedCalendar, false);
+ }
+
+ /**
+ * 更新高度
+ */
+ final void updateItemHeight() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseWeekView view = (BaseWeekView) getChildAt(i);
+ view.updateItemHeight();
+ view.requestLayout();
+ }
+ }
+
+ /**
+ * 清除选择范围
+ */
+ final void clearSelectRange() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseWeekView view = (BaseWeekView) getChildAt(i);
+ view.invalidate();
+ }
+ }
+
+ final void clearSingleSelect() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseWeekView view = (BaseWeekView) getChildAt(i);
+ view.mCurrentItem = -1;
+ view.invalidate();
+ }
+ }
+
+ final void clearMultiSelect() {
+ for (int i = 0; i < getChildCount(); i++) {
+ BaseWeekView view = (BaseWeekView) getChildAt(i);
+ view.mCurrentItem = -1;
+ view.invalidate();
+ }
+ }
+
+ private void notifyAdapterDataSetChanged() {
+ if (getAdapter() == null) {
+ return;
+ }
+ getAdapter().notifyDataSetChanged();
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return mDelegate.isWeekViewScrollable() && super.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return mDelegate.isWeekViewScrollable() && super.onInterceptTouchEvent(ev);
+ }
+
+ /**
+ * 周视图的高度应该与日历项的高度一致
+ */
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(mDelegate.getCalendarItemHeight(), MeasureSpec.EXACTLY);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ /**
+ * 周视图切换
+ */
+ private class WeekViewPagerAdapter extends PagerAdapter {
+
+ @Override
+ public int getCount() {
+ return mWeekCount;
+ }
+
+ @Override
+ public int getItemPosition(@NonNull Object object) {
+ return isUpdateWeekView ? POSITION_NONE : super.getItemPosition(object);
+ }
+
+ @Override
+ public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
+ return view.equals(object);
+ }
+
+ @NonNull
+ @Override
+ public Object instantiateItem(@NonNull ViewGroup container, int position) {
+ Calendar calendar = CalendarUtil.getFirstCalendarStartWithMinCalendar(mDelegate.getMinYear(),
+ mDelegate.getMinYearMonth(),
+ mDelegate.getMinYearDay(),
+ position + 1,
+ mDelegate.getWeekStart());
+ BaseWeekView view;
+ try {
+ Constructor constructor = mDelegate.getWeekViewClass().getConstructor(Context.class);
+ view = (BaseWeekView) constructor.newInstance(getContext());
+ } catch (Exception e) {
+ e.printStackTrace();
+ return new DefaultWeekView(getContext());
+ }
+ view.mParentLayout = mParentLayout;
+ view.setup(mDelegate);
+ view.setup(calendar);
+ view.setTag(position);
+ view.setSelectedCalendar(mDelegate.mSelectedCalendar);
+ container.addView(view);
+ return view;
+ }
+
+ @Override
+ public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
+ BaseWeekView view = (BaseWeekView) object;
+ view.onDestroy();
+ container.removeView(view);
+ }
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/YearRecyclerView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/YearRecyclerView.java
new file mode 100644
index 00000000..ccc0fd7e
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/YearRecyclerView.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * 年份布局选择View
+ */
+public final class YearRecyclerView extends RecyclerView {
+ private CalendarViewDelegate mDelegate;
+ private YearViewAdapter mAdapter;
+ private OnMonthSelectedListener mListener;
+
+ public YearRecyclerView(Context context) {
+ this(context, null);
+ }
+
+ public YearRecyclerView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mAdapter = new YearViewAdapter(context);
+ setLayoutManager(new GridLayoutManager(context, 3));
+ setAdapter(mAdapter);
+ mAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {
+ @Override
+ public void onItemClick(int position, long itemId) {
+ if (mListener != null && mDelegate != null) {
+ Month month = mAdapter.getItem(position);
+ if (month == null) {
+ return;
+ }
+ if (!CalendarUtil.isMonthInRange(month.getYear(), month.getMonth(),
+ mDelegate.getMinYear(), mDelegate.getMinYearMonth(),
+ mDelegate.getMaxYear(), mDelegate.getMaxYearMonth())) {
+ return;
+ }
+ mListener.onMonthSelected(month.getYear(), month.getMonth());
+ if (mDelegate.mYearViewChangeListener != null) {
+ mDelegate.mYearViewChangeListener.onYearViewChange(true);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * 设置
+ *
+ * @param delegate delegate
+ */
+ final void setup(CalendarViewDelegate delegate) {
+ this.mDelegate = delegate;
+ this.mAdapter.setup(delegate);
+ }
+
+ /**
+ * 初始化年视图
+ *
+ * @param year year
+ */
+ final void init(int year) {
+ java.util.Calendar date = java.util.Calendar.getInstance();
+ for (int i = 1; i <= 12; i++) {
+ date.set(year, i - 1, 1);
+ int mDaysCount = CalendarUtil.getMonthDaysCount(year, i);
+ Month month = new Month();
+ month.setDiff(CalendarUtil.getMonthViewStartDiff(year, i, mDelegate.getWeekStart()));
+ month.setCount(mDaysCount);
+ month.setMonth(i);
+ month.setYear(year);
+ mAdapter.addItem(month);
+ }
+ }
+
+ /**
+ * 更新周起始
+ */
+ final void updateWeekStart() {
+ for (Month month : mAdapter.getItems()) {
+ month.setDiff(CalendarUtil.getMonthViewStartDiff(month.getYear(), month.getMonth(), mDelegate.getWeekStart()));
+ }
+ }
+
+ /**
+ * 更新字体颜色大小
+ */
+ final void updateStyle(){
+ for (int i = 0; i < getChildCount(); i++) {
+ YearView view = (YearView) getChildAt(i);
+ view.updateStyle();
+ view.invalidate();
+ }
+ }
+
+ /**
+ * 月份选择事件
+ *
+ * @param listener listener
+ */
+ final void setOnMonthSelectedListener(OnMonthSelectedListener listener) {
+ this.mListener = listener;
+ }
+
+
+ void notifyAdapterDataSetChanged(){
+ if(getAdapter() == null){
+ return;
+ }
+ getAdapter().notifyDataSetChanged();
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ super.onMeasure(widthSpec, heightSpec);
+ int height = MeasureSpec.getSize(heightSpec);
+ int width = MeasureSpec.getSize(widthSpec);
+ mAdapter.setYearViewSize(width / 3, height / 4);
+ }
+
+ interface OnMonthSelectedListener {
+ void onMonthSelected(int year, int month);
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/YearView.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/YearView.java
new file mode 100644
index 00000000..d324860e
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/YearView.java
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * 年视图
+ * Created by huanghaibin on 2018/10/9.
+ */
+@SuppressWarnings("unused")
+public abstract class YearView extends View {
+
+ CalendarViewDelegate mDelegate;
+
+ /**
+ * 当前月份日期的笔
+ */
+ protected Paint mCurMonthTextPaint = new Paint();
+
+ /**
+ * 其它月份日期颜色
+ */
+ protected Paint mOtherMonthTextPaint = new Paint();
+
+ /**
+ * 当前月份农历文本颜色
+ */
+ protected Paint mCurMonthLunarTextPaint = new Paint();
+
+ /**
+ * 当前月份农历文本颜色
+ */
+ protected Paint mSelectedLunarTextPaint = new Paint();
+
+ /**
+ * 其它月份农历文本颜色
+ */
+ protected Paint mOtherMonthLunarTextPaint = new Paint();
+
+ /**
+ * 其它月份农历文本颜色
+ */
+ protected Paint mSchemeLunarTextPaint = new Paint();
+
+ /**
+ * 标记的日期背景颜色画笔
+ */
+ protected Paint mSchemePaint = new Paint();
+
+ /**
+ * 被选择的日期背景色
+ */
+ protected Paint mSelectedPaint = new Paint();
+
+ /**
+ * 标记的文本画笔
+ */
+ protected Paint mSchemeTextPaint = new Paint();
+
+ /**
+ * 选中的文本画笔
+ */
+ protected Paint mSelectTextPaint = new Paint();
+
+ /**
+ * 当前日期文本颜色画笔
+ */
+ protected Paint mCurDayTextPaint = new Paint();
+
+ /**
+ * 当前日期文本颜色画笔
+ */
+ protected Paint mCurDayLunarTextPaint = new Paint();
+
+ /**
+ * 月份画笔
+ */
+ protected Paint mMonthTextPaint = new Paint();
+
+ /**
+ * 周栏画笔
+ */
+ protected Paint mWeekTextPaint = new Paint();
+
+ /**
+ * 日历项
+ */
+ List mItems;
+
+ /**
+ * 每一项的高度
+ */
+ protected int mItemHeight;
+
+ /**
+ * 每一项的宽度
+ */
+ protected int mItemWidth;
+
+ /**
+ * Text的基线
+ */
+ protected float mTextBaseLine;
+
+ /**
+ * Text的基线
+ */
+ protected float mMonthTextBaseLine;
+
+ /**
+ * Text的基线
+ */
+ protected float mWeekTextBaseLine;
+
+ /**
+ * 当前日历卡年份
+ */
+ protected int mYear;
+
+ /**
+ * 当前日历卡月份
+ */
+ protected int mMonth;
+
+ /**
+ * 下个月偏移的数量
+ */
+ protected int mNextDiff;
+
+ /**
+ * 周起始
+ */
+ protected int mWeekStart;
+
+ /**
+ * 日历的行数
+ */
+ protected int mLineCount;
+
+ public YearView(Context context) {
+ this(context, null);
+ }
+
+ public YearView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ initPaint();
+ }
+
+
+ /**
+ * 初始化配置
+ */
+ private void initPaint() {
+ mCurMonthTextPaint.setAntiAlias(true);
+ mCurMonthTextPaint.setTextAlign(Paint.Align.CENTER);
+ mCurMonthTextPaint.setColor(0xFF111111);
+ mCurMonthTextPaint.setFakeBoldText(true);
+
+ mOtherMonthTextPaint.setAntiAlias(true);
+ mOtherMonthTextPaint.setTextAlign(Paint.Align.CENTER);
+ mOtherMonthTextPaint.setColor(0xFFe1e1e1);
+ mOtherMonthTextPaint.setFakeBoldText(true);
+
+ mCurMonthLunarTextPaint.setAntiAlias(true);
+ mCurMonthLunarTextPaint.setTextAlign(Paint.Align.CENTER);
+
+ mSelectedLunarTextPaint.setAntiAlias(true);
+ mSelectedLunarTextPaint.setTextAlign(Paint.Align.CENTER);
+
+ mOtherMonthLunarTextPaint.setAntiAlias(true);
+ mOtherMonthLunarTextPaint.setTextAlign(Paint.Align.CENTER);
+
+ mMonthTextPaint.setAntiAlias(true);
+ mMonthTextPaint.setFakeBoldText(true);
+
+ mWeekTextPaint.setAntiAlias(true);
+ mWeekTextPaint.setFakeBoldText(true);
+ mWeekTextPaint.setTextAlign(Paint.Align.CENTER);
+
+ mSchemeLunarTextPaint.setAntiAlias(true);
+ mSchemeLunarTextPaint.setTextAlign(Paint.Align.CENTER);
+
+ mSchemeTextPaint.setAntiAlias(true);
+ mSchemeTextPaint.setStyle(Paint.Style.FILL);
+ mSchemeTextPaint.setTextAlign(Paint.Align.CENTER);
+ mSchemeTextPaint.setColor(0xffed5353);
+ mSchemeTextPaint.setFakeBoldText(true);
+
+ mSelectTextPaint.setAntiAlias(true);
+ mSelectTextPaint.setStyle(Paint.Style.FILL);
+ mSelectTextPaint.setTextAlign(Paint.Align.CENTER);
+ mSelectTextPaint.setColor(0xffed5353);
+ mSelectTextPaint.setFakeBoldText(true);
+
+ mSchemePaint.setAntiAlias(true);
+ mSchemePaint.setStyle(Paint.Style.FILL);
+ mSchemePaint.setStrokeWidth(2);
+ mSchemePaint.setColor(0xffefefef);
+
+ mCurDayTextPaint.setAntiAlias(true);
+ mCurDayTextPaint.setTextAlign(Paint.Align.CENTER);
+ mCurDayTextPaint.setColor(Color.RED);
+ mCurDayTextPaint.setFakeBoldText(true);
+
+ mCurDayLunarTextPaint.setAntiAlias(true);
+ mCurDayLunarTextPaint.setTextAlign(Paint.Align.CENTER);
+ mCurDayLunarTextPaint.setColor(Color.RED);
+ mCurDayLunarTextPaint.setFakeBoldText(true);
+
+ mSelectedPaint.setAntiAlias(true);
+ mSelectedPaint.setStyle(Paint.Style.FILL);
+ mSelectedPaint.setStrokeWidth(2);
+ }
+
+ /**
+ * 设置
+ *
+ * @param delegate delegate
+ */
+ final void setup(CalendarViewDelegate delegate) {
+ this.mDelegate = delegate;
+ updateStyle();
+ }
+
+ final void updateStyle(){
+ if(mDelegate == null){
+ return;
+ }
+ this.mCurMonthTextPaint.setTextSize(mDelegate.getYearViewDayTextSize());
+ this.mSchemeTextPaint.setTextSize(mDelegate.getYearViewDayTextSize());
+ this.mOtherMonthTextPaint.setTextSize(mDelegate.getYearViewDayTextSize());
+ this.mCurDayTextPaint.setTextSize(mDelegate.getYearViewDayTextSize());
+ this.mSelectTextPaint.setTextSize(mDelegate.getYearViewDayTextSize());
+
+ this.mSchemeTextPaint.setColor(mDelegate.getYearViewSchemeTextColor());
+ this.mCurMonthTextPaint.setColor(mDelegate.getYearViewDayTextColor());
+ this.mOtherMonthTextPaint.setColor(mDelegate.getYearViewDayTextColor());
+ this.mCurDayTextPaint.setColor(mDelegate.getYearViewCurDayTextColor());
+ this.mSelectTextPaint.setColor(mDelegate.getYearViewSelectTextColor());
+ this.mMonthTextPaint.setTextSize(mDelegate.getYearViewMonthTextSize());
+ this.mMonthTextPaint.setColor(mDelegate.getYearViewMonthTextColor());
+ this.mWeekTextPaint.setColor(mDelegate.getYearViewWeekTextColor());
+ this.mWeekTextPaint.setTextSize(mDelegate.getYearViewWeekTextSize());
+ }
+
+ /**
+ * 初始化年视图
+ *
+ * @param year year
+ * @param month month
+ */
+ final void init(int year, int month) {
+ mYear = year;
+ mMonth = month;
+ mNextDiff = CalendarUtil.getMonthEndDiff(mYear, mMonth, mDelegate.getWeekStart());
+ int preDiff = CalendarUtil.getMonthViewStartDiff(mYear, mMonth, mDelegate.getWeekStart());
+
+ mItems = CalendarUtil.initCalendarForMonthView(mYear, mMonth, mDelegate.getCurrentDay(), mDelegate.getWeekStart());
+
+ mLineCount = 6;
+ addSchemesFromMap();
+
+ }
+
+ /**
+ * 测量大小
+ *
+ * @param width width
+ * @param height height
+ */
+ @SuppressWarnings("IntegerDivisionInFloatingPointContext")
+ final void measureSize(int width, int height) {
+
+ Rect rect = new Rect();
+ mCurMonthTextPaint.getTextBounds("1", 0, 1, rect);
+ int textHeight = rect.height();
+ int mMinHeight = 12 * textHeight + getMonthViewTop();
+
+ int h = height >= mMinHeight ? height : mMinHeight;
+
+ getLayoutParams().width = width;
+ getLayoutParams().height = h;
+ mItemHeight = (h - getMonthViewTop()) / 6;
+
+ Paint.FontMetrics metrics = mCurMonthTextPaint.getFontMetrics();
+ mTextBaseLine = mItemHeight / 2 - metrics.descent + (metrics.bottom - metrics.top) / 2;
+
+ Paint.FontMetrics monthMetrics = mMonthTextPaint.getFontMetrics();
+ mMonthTextBaseLine = mDelegate.getYearViewMonthHeight() / 2 - monthMetrics.descent +
+ (monthMetrics.bottom - monthMetrics.top) / 2;
+
+ Paint.FontMetrics weekMetrics = mWeekTextPaint.getFontMetrics();
+ mWeekTextBaseLine = mDelegate.getYearViewWeekHeight() / 2 - weekMetrics.descent +
+ (weekMetrics.bottom - weekMetrics.top) / 2;
+
+ invalidate();
+ }
+
+ /**
+ * 添加事件标记,来自Map
+ */
+ private void addSchemesFromMap() {
+ if (mDelegate.mSchemeDatesMap == null || mDelegate.mSchemeDatesMap.size() == 0) {
+ return;
+ }
+ for (Calendar a : mItems) {
+ if (mDelegate.mSchemeDatesMap.containsKey(a.toString())) {
+ Calendar d = mDelegate.mSchemeDatesMap.get(a.toString());
+ if(d == null){
+ continue;
+ }
+ a.setScheme(TextUtils.isEmpty(d.getScheme()) ? mDelegate.getSchemeText() : d.getScheme());
+ a.setSchemeColor(d.getSchemeColor());
+ a.setSchemes(d.getSchemes());
+ } else {
+ a.setScheme("");
+ a.setSchemeColor(0);
+ a.setSchemes(null);
+ }
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mItemWidth = (getWidth() -
+ mDelegate.getYearViewMonthPaddingLeft() -
+ mDelegate.getYearViewMonthPaddingRight()) / 7;
+ onPreviewHook();
+ onDrawMonth(canvas);
+ onDrawWeek(canvas);
+ onDrawMonthView(canvas);
+ }
+
+ /**
+ * 绘制
+ *
+ * @param canvas canvas
+ */
+ private void onDrawMonth(Canvas canvas) {
+ onDrawMonth(canvas,
+ mYear, mMonth,
+ mDelegate.getYearViewMonthPaddingLeft(),
+ mDelegate.getYearViewMonthPaddingTop(),
+ getWidth() - 2 * mDelegate.getYearViewMonthPaddingRight(),
+ mDelegate.getYearViewMonthHeight() +
+ mDelegate.getYearViewMonthPaddingTop());
+ }
+
+ private int getMonthViewTop() {
+ return mDelegate.getYearViewMonthPaddingTop() +
+ mDelegate.getYearViewMonthHeight() +
+ mDelegate.getYearViewMonthPaddingBottom() +
+ mDelegate.getYearViewWeekHeight();
+ }
+
+ /**
+ * 绘制
+ *
+ * @param canvas canvas
+ */
+ private void onDrawWeek(Canvas canvas) {
+ if (mDelegate.getYearViewWeekHeight() <= 0) {
+ return;
+ }
+ int week = mDelegate.getWeekStart();
+ if (week > 0) {
+ week -= 1;
+ }
+ int width = (getWidth() -
+ mDelegate.getYearViewMonthPaddingLeft() -
+ mDelegate.getYearViewMonthPaddingRight()) / 7;
+ for (int i = 0; i < 7; i++) {
+ onDrawWeek(canvas,
+ week,
+ mDelegate.getYearViewMonthPaddingLeft() + i * width,
+ mDelegate.getYearViewMonthHeight() +
+ mDelegate.getYearViewMonthPaddingTop() +
+ mDelegate.getYearViewMonthPaddingBottom(),
+ width,
+ mDelegate.getYearViewWeekHeight());
+ week += 1;
+ if (week >= 7) {
+ week = 0;
+ }
+
+ }
+ }
+
+ /**
+ * 绘制月份数据
+ *
+ * @param canvas canvas
+ */
+ private void onDrawMonthView(Canvas canvas) {
+
+ int count = mLineCount * 7;
+ int d = 0;
+ for (int i = 0; i < mLineCount; i++) {
+ for (int j = 0; j < 7; j++) {
+ Calendar calendar = mItems.get(d);
+ if (d > mItems.size() - mNextDiff) {
+ return;
+ }
+ if (!calendar.isCurrentMonth()) {
+ ++d;
+ continue;
+ }
+ draw(canvas, calendar, i, j, d);
+ ++d;
+ }
+ }
+ }
+
+
+ /**
+ * 开始绘制
+ *
+ * @param canvas canvas
+ * @param calendar 对应日历
+ * @param i i
+ * @param j j
+ * @param d d
+ */
+ private void draw(Canvas canvas, Calendar calendar, int i, int j, int d) {
+ int x = j * mItemWidth + mDelegate.getYearViewMonthPaddingLeft();
+ int y = i * mItemHeight + getMonthViewTop();
+
+ boolean isSelected = calendar.equals(mDelegate.mSelectedCalendar);
+ boolean hasScheme = calendar.hasScheme();
+
+ if (hasScheme) {
+ //标记的日子
+ boolean isDrawSelected = false;//是否继续绘制选中的onDrawScheme
+ if (isSelected) {
+ isDrawSelected = onDrawSelected(canvas, calendar, x, y, true);
+ }
+ if (isDrawSelected || !isSelected) {
+ //将画笔设置为标记颜色
+ mSchemePaint.setColor(calendar.getSchemeColor() != 0 ? calendar.getSchemeColor() : mDelegate.getSchemeThemeColor());
+ onDrawScheme(canvas, calendar, x, y);
+ }
+ } else {
+ if (isSelected) {
+ onDrawSelected(canvas, calendar, x, y, false);
+ }
+ }
+ onDrawText(canvas, calendar, x, y, hasScheme, isSelected);
+ }
+
+ /**
+ * 开始绘制前的钩子,这里做一些初始化的操作,每次绘制只调用一次,性能高效
+ * 没有需要可忽略不实现
+ * 例如:
+ * 1、需要绘制圆形标记事件背景,可以在这里计算半径
+ * 2、绘制矩形选中效果,也可以在这里计算矩形宽和高
+ */
+ protected void onPreviewHook() {
+ // TODO: 2017/11/16
+ }
+
+
+ /**
+ * 绘制月份
+ *
+ * @param canvas canvas
+ * @param year year
+ * @param month month
+ * @param x x
+ * @param y y
+ * @param width width
+ * @param height height
+ */
+ protected abstract void onDrawMonth(Canvas canvas, int year, int month, int x, int y, int width, int height);
+
+
+ /**
+ * 绘制年视图的周栏
+ *
+ * @param canvas canvas
+ * @param week week
+ * @param x x
+ * @param y y
+ * @param width width
+ * @param height height
+ */
+ protected abstract void onDrawWeek(Canvas canvas, int week, int x, int y, int width, int height);
+
+
+ /**
+ * 绘制选中的日期
+ *
+ * @param canvas canvas
+ * @param calendar 日历日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme hasScheme 非标记的日期
+ * @return 是否绘制onDrawScheme,true or false
+ */
+ protected abstract boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme);
+
+ /**
+ * 绘制标记的日期,这里可以是背景色,标记色什么的
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ */
+ protected abstract void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y);
+
+
+ /**
+ * 绘制日历文本
+ *
+ * @param canvas canvas
+ * @param calendar 日历calendar
+ * @param x 日历Card x起点坐标
+ * @param y 日历Card y起点坐标
+ * @param hasScheme 是否是标记的日期
+ * @param isSelected 是否选中
+ */
+ protected abstract void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected);
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/YearViewAdapter.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/YearViewAdapter.java
new file mode 100644
index 00000000..b4f1328f
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/YearViewAdapter.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.lang.reflect.Constructor;
+
+final class YearViewAdapter extends BaseRecyclerAdapter {
+ private CalendarViewDelegate mDelegate;
+ private int mItemWidth, mItemHeight;
+
+ YearViewAdapter(Context context) {
+ super(context);
+ }
+
+ final void setup(CalendarViewDelegate delegate) {
+ this.mDelegate = delegate;
+ }
+
+
+ final void setYearViewSize(int width, int height) {
+ this.mItemWidth = width;
+ this.mItemHeight = height;
+ }
+
+ @Override
+ RecyclerView.ViewHolder onCreateDefaultViewHolder(ViewGroup parent, int type) {
+ YearView yearView;
+ if (TextUtils.isEmpty(mDelegate.getYearViewClassPath())) {
+ yearView = new DefaultYearView(mContext);
+ } else {
+ try {
+ Constructor constructor = mDelegate.getYearViewClass().getConstructor(Context.class);
+ yearView = (YearView) constructor.newInstance(mContext);
+ } catch (Exception e) {
+ e.printStackTrace();
+ yearView = new DefaultYearView(mContext);
+ }
+ }
+ RecyclerView.LayoutParams params = new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT,
+ RecyclerView.LayoutParams.MATCH_PARENT);
+ yearView.setLayoutParams(params);
+ return new YearViewHolder(yearView, mDelegate);
+ }
+
+ @Override
+ void onBindViewHolder(RecyclerView.ViewHolder holder, Month item, int position) {
+ YearViewHolder h = (YearViewHolder) holder;
+ YearView view = h.mYearView;
+ view.init(item.getYear(), item.getMonth());
+ view.measureSize(mItemWidth, mItemHeight);
+ }
+
+ private static class YearViewHolder extends RecyclerView.ViewHolder {
+ YearView mYearView;
+ YearViewHolder(View itemView, CalendarViewDelegate delegate) {
+ super(itemView);
+ mYearView = (YearView) itemView;
+ mYearView.setup(delegate);
+ }
+ }
+}
diff --git a/CalendarView/calendarview/src/main/java/com/haibin/calendarview/YearViewPager.java b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/YearViewPager.java
new file mode 100644
index 00000000..4674d2b9
--- /dev/null
+++ b/CalendarView/calendarview/src/main/java/com/haibin/calendarview/YearViewPager.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.haibin.calendarview;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+
+/**
+ * 年份+月份选择布局
+ * ViewPager + RecyclerView
+ */
+public final class YearViewPager extends ViewPager {
+ private int mYearCount;
+ private boolean isUpdateYearView;
+ private CalendarViewDelegate mDelegate;
+ private YearRecyclerView.OnMonthSelectedListener mListener;
+
+ public YearViewPager(Context context) {
+ this(context, null);
+ }
+
+ public YearViewPager(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+
+ void setup(CalendarViewDelegate delegate) {
+ this.mDelegate = delegate;
+ this.mYearCount = mDelegate.getMaxYear() - mDelegate.getMinYear() + 1;
+ setAdapter(new PagerAdapter() {
+ @Override
+ public int getCount() {
+ return mYearCount;
+ }
+
+ @Override
+ public int getItemPosition(@NonNull Object object) {
+ return isUpdateYearView ? POSITION_NONE : super.getItemPosition(object);
+ }
+
+ @Override
+ public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
+ return view == object;
+ }
+
+ @NonNull
+ @Override
+ public Object instantiateItem(@NonNull ViewGroup container, int position) {
+ YearRecyclerView view = new YearRecyclerView(getContext());
+ container.addView(view);
+ view.setup(mDelegate);
+ view.setOnMonthSelectedListener(mListener);
+ view.init(position + mDelegate.getMinYear());
+ return view;
+ }
+
+ @Override
+ public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
+ container.removeView((View) object);
+ }
+ });
+ setCurrentItem(mDelegate.getCurrentDay().getYear() - mDelegate.getMinYear());
+ }
+
+ @Override
+ public void setCurrentItem(int item) {
+ setCurrentItem(item, false);
+ }
+
+ @Override
+ public void setCurrentItem(int item, boolean smoothScroll) {
+ if (Math.abs(getCurrentItem() - item) > 1) {
+ super.setCurrentItem(item, false);
+ } else {
+ super.setCurrentItem(item, false);
+ }
+ }
+
+ /**
+ * 通知刷新
+ */
+ void notifyDataSetChanged() {
+ this.mYearCount = mDelegate.getMaxYear() - mDelegate.getMinYear() + 1;
+ if(getAdapter() != null){
+ getAdapter().notifyDataSetChanged();
+ }
+
+ }
+
+ /**
+ * 滚动到某年
+ *
+ * @param year year
+ * @param smoothScroll smoothScroll
+ */
+ void scrollToYear(int year, boolean smoothScroll) {
+ setCurrentItem(year - mDelegate.getMinYear(), smoothScroll);
+ }
+
+ /**
+ * 更新日期范围
+ */
+ final void updateRange() {
+ isUpdateYearView = true;
+ notifyDataSetChanged();
+ isUpdateYearView = false;
+ }
+
+ /**
+ * 更新界面
+ */
+ final void update() {
+ for (int i = 0; i < getChildCount(); i++) {
+ YearRecyclerView view = (YearRecyclerView) getChildAt(i);
+ view.notifyAdapterDataSetChanged();
+ }
+ }
+
+
+ /**
+ * 更新周起始
+ */
+ final void updateWeekStart() {
+ for (int i = 0; i < getChildCount(); i++) {
+ YearRecyclerView view = (YearRecyclerView) getChildAt(i);
+ view.updateWeekStart();
+ view.notifyAdapterDataSetChanged();
+ }
+ }
+
+ /**
+ * 更新字体颜色大小
+ */
+ final void updateStyle(){
+ for (int i = 0; i < getChildCount(); i++) {
+ YearRecyclerView view = (YearRecyclerView) getChildAt(i);
+ view.updateStyle();
+ }
+ }
+
+ final void setOnMonthSelectedListener(YearRecyclerView.OnMonthSelectedListener listener) {
+ this.mListener = listener;
+ }
+
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ //heightMeasureSpec = MeasureSpec.makeMeasureSpec(getHeight(getContext(), this), MeasureSpec.EXACTLY);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ /**
+ * 计算相对高度
+ *
+ * @param context context
+ * @param view view
+ * @return 年月视图选择器最适合的高度
+ */
+ private static int getHeight(Context context, View view) {
+ WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ assert manager != null;
+ Display display = manager.getDefaultDisplay();
+ int h = display.getHeight();
+ int[] location = new int[2];
+ view.getLocationInWindow(location);
+ view.getLocationOnScreen(location);
+ return h - location[1];
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return mDelegate.isYearViewScrollable() && super.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return mDelegate.isYearViewScrollable() && super.onInterceptTouchEvent(ev);
+ }
+}
diff --git a/CalendarView/calendarview/src/main/res/drawable-v21/cv_bg_material.xml b/CalendarView/calendarview/src/main/res/drawable-v21/cv_bg_material.xml
new file mode 100644
index 00000000..b9f9955b
--- /dev/null
+++ b/CalendarView/calendarview/src/main/res/drawable-v21/cv_bg_material.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/CalendarView/calendarview/src/main/res/drawable/cv_bg_material.xml b/CalendarView/calendarview/src/main/res/drawable/cv_bg_material.xml
new file mode 100644
index 00000000..20fd151a
--- /dev/null
+++ b/CalendarView/calendarview/src/main/res/drawable/cv_bg_material.xml
@@ -0,0 +1,13 @@
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
diff --git a/CalendarView/calendarview/src/main/res/layout/cv_layout_calendar_view.xml b/CalendarView/calendarview/src/main/res/layout/cv_layout_calendar_view.xml
new file mode 100644
index 00000000..05690606
--- /dev/null
+++ b/CalendarView/calendarview/src/main/res/layout/cv_layout_calendar_view.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/calendarview/src/main/res/layout/cv_week_bar.xml b/CalendarView/calendarview/src/main/res/layout/cv_week_bar.xml
new file mode 100644
index 00000000..6f540606
--- /dev/null
+++ b/CalendarView/calendarview/src/main/res/layout/cv_week_bar.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/calendarview/src/main/res/values-zh-rHK/strings.xml b/CalendarView/calendarview/src/main/res/values-zh-rHK/strings.xml
new file mode 100644
index 00000000..d3f746b2
--- /dev/null
+++ b/CalendarView/calendarview/src/main/res/values-zh-rHK/strings.xml
@@ -0,0 +1,206 @@
+
+ CalendarView
+
+ - 春節
+ - 二月
+ - 三月
+ - 四月
+ - 五月
+ - 六月
+ - 七月
+ - 八月
+ - 九月
+ - 十月
+ - 冬月
+ - 臘月
+
+
+
+ - 除夕
+ - 0101春節
+ - 0115元宵
+ - 0505端午
+ - 0707七夕
+ - 0815中秋
+ - 0909重陽
+
+
+
+ - 初一
+ - 初二
+ - 初三
+ - 初四
+ - 初五
+ - 初六
+ - 初七
+ - 初八
+ - 初九
+ - 初十
+ - 十一
+ - 十二
+ - 十三
+ - 十四
+ - 十五
+ - 十六
+ - 十七
+ - 十八
+ - 十九
+ - 二十
+ - 廿一
+ - 廿二
+ - 廿三
+ - 廿四
+ - 廿五
+ - 廿六
+ - 廿七
+ - 廿八
+ - 廿九
+ - 三十
+
+
+
+ - 0101元旦
+ - 0214情人節
+ - 0308婦女節
+ - 0312植樹節
+ - 0315消權日
+ - 0401愚人節
+ - 0404兒童節
+ - 0422地球日
+ - 0501勞動節
+ - 0504青年節
+ - 0701香港回歸
+ - 0801建军节
+ - 0910教師節
+ - 1001國慶日
+ - 1031萬聖節
+ - 1111光棍節
+ - 1224平安夜
+ - 1225聖誕節
+
+
+
+ - 春分
+ - 清明
+ - 穀雨
+ - 立夏
+ - 小滿
+ - 芒種
+ - 夏至
+ - 小暑
+ - 大暑
+ - 立秋
+ - 處暑
+ - 白露
+ - 秋分
+ - 寒露
+ - 霜降
+ - 立冬
+ - 小雪
+ - 大雪
+ - 冬至
+ - 小寒
+ - 大寒
+ - 立春
+ - 雨水
+ - 驚蟄
+
+
+
+ - 母親節
+ - 父親節
+ - 感恩節
+
+
+ 日
+ 一
+ 二
+ 三
+ 四
+ 五
+ 六
+
+
+
+ - 日
+ - 一
+ - 二
+ - 三
+ - 四
+ - 五
+ - 六
+
+
+
+ - 1月
+ - 2月
+ - 3月
+ - 4月
+ - 5月
+ - 6月
+ - 7月
+ - 8月
+ - 9月
+ - 10月
+ - 11月
+ - 12月
+
+
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ - 10
+
+
+
+
+ - 辛
+ - 壬
+ - 癸
+ - 甲
+ - 乙
+ - 丙
+ - 丁
+ - 戊
+ - 己
+ - 庚
+
+
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ - 10
+ - 11
+ - 12
+
+
+
+ - 酉
+ - 戌
+ - 亥
+ - 子
+ - 丑
+ - 寅
+ - 卯
+ - 辰
+ - 巳
+ - 午
+ - 未
+ - 申
+
+
+
diff --git a/CalendarView/calendarview/src/main/res/values-zh-rTW/strings.xml b/CalendarView/calendarview/src/main/res/values-zh-rTW/strings.xml
new file mode 100644
index 00000000..63fb5e81
--- /dev/null
+++ b/CalendarView/calendarview/src/main/res/values-zh-rTW/strings.xml
@@ -0,0 +1,209 @@
+
+ CalendarView
+
+ - 春節
+ - 二月
+ - 三月
+ - 四月
+ - 五月
+ - 六月
+ - 七月
+ - 八月
+ - 九月
+ - 十月
+ - 冬月
+ - 臘月
+
+
+
+ - 除夕
+ - 0101春節
+ - 0115元宵
+ - 0505端午
+ - 0707七夕
+ - 0815中秋
+ - 0909重陽
+
+
+
+ - 初一
+ - 初二
+ - 初三
+ - 初四
+ - 初五
+ - 初六
+ - 初七
+ - 初八
+ - 初九
+ - 初十
+ - 十一
+ - 十二
+ - 十三
+ - 十四
+ - 十五
+ - 十六
+ - 十七
+ - 十八
+ - 十九
+ - 二十
+ - 廿一
+ - 廿二
+ - 廿三
+ - 廿四
+ - 廿五
+ - 廿六
+ - 廿七
+ - 廿八
+ - 廿九
+ - 三十
+
+
+
+ - 0101元旦
+ - 0214情人節
+ - 0308婦女節
+ - 0312植樹節
+ - 0315消權日
+ - 0401愚人節
+ - 0404兒童節
+ - 0422地球日
+ - 0501勞動節
+ - 0903軍人節
+ - 0928教師節
+ - 0501勞動節
+ - 0504青年節
+ - 0601兒童節
+ - 0701建黨節
+ - 0801建軍節
+ - 0928教師節
+ - 1001國慶節
+ - 1031萬聖節
+ - 1111光棍節
+ - 1224平安夜
+ - 1225聖誕節
+
+
+
+ - 春分
+ - 清明
+ - 穀雨
+ - 立夏
+ - 小滿
+ - 芒種
+ - 夏至
+ - 小暑
+ - 大暑
+ - 立秋
+ - 處暑
+ - 白露
+ - 秋分
+ - 寒露
+ - 霜降
+ - 立冬
+ - 小雪
+ - 大雪
+ - 冬至
+ - 小寒
+ - 大寒
+ - 立春
+ - 雨水
+ - 驚蟄
+
+
+
+ - 母親節
+ - 父親節
+ - 感恩節
+
+
+ 日
+ 一
+ 二
+ 三
+ 四
+ 五
+ 六
+
+
+
+ - 日
+ - 一
+ - 二
+ - 三
+ - 四
+ - 五
+ - 六
+
+
+
+ - 1月
+ - 2月
+ - 3月
+ - 4月
+ - 5月
+ - 6月
+ - 7月
+ - 8月
+ - 9月
+ - 10月
+ - 11月
+ - 12月
+
+
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ - 10
+
+
+
+
+ - 辛
+ - 壬
+ - 癸
+ - 甲
+ - 乙
+ - 丙
+ - 丁
+ - 戊
+ - 己
+ - 庚
+
+
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ - 10
+ - 11
+ - 12
+
+
+
+ - 酉
+ - 戌
+ - 亥
+ - 子
+ - 丑
+ - 寅
+ - 卯
+ - 辰
+ - 巳
+ - 午
+ - 未
+ - 申
+
+
diff --git a/CalendarView/calendarview/src/main/res/values-zh/strings.xml b/CalendarView/calendarview/src/main/res/values-zh/strings.xml
new file mode 100644
index 00000000..82972c7f
--- /dev/null
+++ b/CalendarView/calendarview/src/main/res/values-zh/strings.xml
@@ -0,0 +1,216 @@
+
+ CalendarView
+
+ - 春节
+ - 二月
+ - 三月
+ - 四月
+ - 五月
+ - 六月
+ - 七月
+ - 八月
+ - 九月
+ - 十月
+ - 冬月
+ - 腊月
+
+
+
+ - 除夕
+ - 0101春节
+ - 0115元宵
+ - 0505端午
+ - 0707七夕
+ - 0815中秋
+ - 0909重阳
+
+
+
+ - 初一
+ - 初二
+ - 初三
+ - 初四
+ - 初五
+ - 初六
+ - 初七
+ - 初八
+ - 初九
+ - 初十
+ - 十一
+ - 十二
+ - 十三
+ - 十四
+ - 十五
+ - 十六
+ - 十七
+ - 十八
+ - 十九
+ - 二十
+ - 廿一
+ - 廿二
+ - 廿三
+ - 廿四
+ - 廿五
+ - 廿六
+ - 廿七
+ - 廿八
+ - 廿九
+ - 三十
+
+
+
+ - 0101元旦
+ - 0214情人节
+ - 0308妇女节
+ - 0312植树节
+ - 0315消权日
+ - 0401愚人节
+ - 0422地球日
+ - 0501劳动节
+ - 0504青年节
+ - 0601儿童节
+ - 0701建党节
+ - 0801建军节
+ - 0910教师节
+ - 1001国庆节
+ - 1031万圣节
+ - 1111光棍节
+ - 1224平安夜
+ - 1225圣诞节
+
+
+
+ - 春分
+ - 清明
+ - 谷雨
+ - 立夏
+ - 小满
+ - 芒种
+ - 夏至
+ - 小暑
+ - 大暑
+ - 立秋
+ - 处暑
+ - 白露
+ - 秋分
+ - 寒露
+ - 霜降
+ - 立冬
+ - 小雪
+ - 大雪
+ - 冬至
+ - 小寒
+ - 大寒
+ - 立春
+ - 雨水
+ - 惊蛰
+
+
+
+ - 母亲节
+ - 父亲节
+ - 感恩节
+
+
+ 日
+ 一
+ 二
+ 三
+ 四
+ 五
+ 六
+
+
+
+ - 日
+ - 一
+ - 二
+ - 三
+ - 四
+ - 五
+ - 六
+
+
+
+ - 日
+ - 一
+ - 二
+ - 三
+ - 四
+ - 五
+ - 六
+
+
+
+ - 1月
+ - 2月
+ - 3月
+ - 4月
+ - 5月
+ - 6月
+ - 7月
+ - 8月
+ - 9月
+ - 10月
+ - 11月
+ - 12月
+
+
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ - 10
+
+
+
+
+ - 辛
+ - 壬
+ - 癸
+ - 甲
+ - 乙
+ - 丙
+ - 丁
+ - 戊
+ - 己
+ - 庚
+
+
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ - 10
+ - 11
+ - 12
+
+
+
+ - 酉
+ - 戌
+ - 亥
+ - 子
+ - 丑
+ - 寅
+ - 卯
+ - 辰
+ - 巳
+ - 午
+ - 未
+ - 申
+
+
+
diff --git a/CalendarView/calendarview/src/main/res/values/attrs.xml b/CalendarView/calendarview/src/main/res/values/attrs.xml
new file mode 100644
index 00000000..bce063b2
--- /dev/null
+++ b/CalendarView/calendarview/src/main/res/values/attrs.xml
@@ -0,0 +1,144 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CalendarView/calendarview/src/main/res/values/strings.xml b/CalendarView/calendarview/src/main/res/values/strings.xml
new file mode 100644
index 00000000..7da6f777
--- /dev/null
+++ b/CalendarView/calendarview/src/main/res/values/strings.xml
@@ -0,0 +1,215 @@
+
+ CalendarView
+
+ - 春节
+ - 二月
+ - 三月
+ - 四月
+ - 五月
+ - 六月
+ - 七月
+ - 八月
+ - 九月
+ - 十月
+ - 冬月
+ - 腊月
+
+
+
+ - 除夕
+ - 0101春节
+ - 0115元宵
+ - 0505端午
+ - 0707七夕
+ - 0815中秋
+ - 0909重阳
+
+
+
+ - 初一
+ - 初二
+ - 初三
+ - 初四
+ - 初五
+ - 初六
+ - 初七
+ - 初八
+ - 初九
+ - 初十
+ - 十一
+ - 十二
+ - 十三
+ - 十四
+ - 十五
+ - 十六
+ - 十七
+ - 十八
+ - 十九
+ - 二十
+ - 廿一
+ - 廿二
+ - 廿三
+ - 廿四
+ - 廿五
+ - 廿六
+ - 廿七
+ - 廿八
+ - 廿九
+ - 三十
+
+
+
+ - 0101元旦
+ - 0214情人节
+ - 0308妇女节
+ - 0312植树节
+ - 0315消权日
+ - 0401愚人节
+ - 0422地球日
+ - 0501劳动节
+ - 0504青年节
+ - 0601儿童节
+ - 0701建党节
+ - 0801建军节
+ - 0910教师节
+ - 1001国庆节
+ - 1031万圣节
+ - 1111光棍节
+ - 1224平安夜
+ - 1225圣诞节
+
+
+
+ - 春分
+ - 清明
+ - 谷雨
+ - 立夏
+ - 小满
+ - 芒种
+ - 夏至
+ - 小暑
+ - 大暑
+ - 立秋
+ - 处暑
+ - 白露
+ - 秋分
+ - 寒露
+ - 霜降
+ - 立冬
+ - 小雪
+ - 大雪
+ - 冬至
+ - 小寒
+ - 大寒
+ - 立春
+ - 雨水
+ - 惊蛰
+
+
+
+ - 母亲节
+ - 父亲节
+ - 感恩节
+
+
+ Sun
+ Mon
+ Tue
+ Wed
+ Thu
+ Fri
+ Sat
+
+
+ - Sun
+ - Mon
+ - Tue
+ - Wed
+ - Thu
+ - Fri
+ - Sat
+
+
+
+ - S
+ - M
+ - T
+ - W
+ - T
+ - F
+ - S
+
+
+
+ - JAN
+ - FEB
+ - MAR
+ - APR
+ - MAY
+ - JUN
+ - JUL
+ - AUG
+ - SEP
+ - OCT
+ - NOV
+ - DEC
+
+
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ - 10
+
+
+
+
+ - 辛
+ - 壬
+ - 癸
+ - 甲
+ - 乙
+ - 丙
+ - 丁
+ - 戊
+ - 己
+ - 庚
+
+
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ - 10
+ - 11
+ - 12
+
+
+
+ - 酉
+ - 戌
+ - 亥
+ - 子
+ - 丑
+ - 寅
+ - 卯
+ - 辰
+ - 巳
+ - 午
+ - 未
+ - 申
+
+
+
diff --git a/CalendarView/calendarview/src/test/java/com/haibin/calendarview/ExampleUnitTest.java b/CalendarView/calendarview/src/test/java/com/haibin/calendarview/ExampleUnitTest.java
new file mode 100644
index 00000000..64891846
--- /dev/null
+++ b/CalendarView/calendarview/src/test/java/com/haibin/calendarview/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.haibin.calendarview;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/CalendarView/gradle.properties b/CalendarView/gradle.properties
new file mode 100644
index 00000000..4dcc7a43
--- /dev/null
+++ b/CalendarView/gradle.properties
@@ -0,0 +1,45 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+POM_BINTRAY_NAME=com.haibin:calendarview
+POM_DESCRIPTION=A very good CalendarView for Android.
+POM_ARTIFACT_ID=calendarview
+VERSION_NAME=3.7.1
+VERSION_CODE=3.7.1
+POM_BINTRAY_PRPO=maven
+POM_PACKAGING=aar
+POM_NAME=calendarview
+POM_GROUP_ID=com.haibin
+POM_URL=https://github.com/huanghaibin-dev/calendarview
+POM_GIT_URL=https://github.com/huanghaibin-dev/calendarview.git
+POM_SCM_URL=https://github.com/huanghaibin-dev/calendarview
+POM_SCM_CONNECTION=scm:git@github.com:huanghaibin-dev/calendarview.git
+POM_SCM_DEV_CONNECTION=scm:git@github.com:huanghaibin-dev/calendarview.git
+POM_LICENCE_COMMENTS=A business-friendly OSS license
+POM_LICENCE_NAME=The Apache Software License, Version 2.0
+POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
+POM_LICENCE_DIST=repo
+POM_LICENCE_ALL=["Apache-2.0"]
+POM_DEVELOPER_ID=huanghaibin-dev
+POM_DEVELOPER_NAME=huanghaibin
+POM_DEVELOPER_EMAIL=huanghaibin_dev@163.com
+POM_DEVELOPER_URL=https://github.com/MiracleTimes-Dev
+POM_ISSUE_MANAGEMENT_SYSTEM=Github
+POM_ISSUE_MANAGEMENT_URL=https://github.com/huanghaibin-dev/calendarview/issues
+POM_INCEPTION_YEAR=2016
+android.useAndroidX=true
+android.enableJetifier=true
\ No newline at end of file
diff --git a/CalendarView/gradle/wrapper/gradle-wrapper.properties b/CalendarView/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..9b6616cb
--- /dev/null
+++ b/CalendarView/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/CalendarView/gradlew b/CalendarView/gradlew
new file mode 100644
index 00000000..9d82f789
--- /dev/null
+++ b/CalendarView/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/CalendarView/gradlew.bat b/CalendarView/gradlew.bat
new file mode 100644
index 00000000..8a0b282a
--- /dev/null
+++ b/CalendarView/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/CalendarView/script/gradle-jcenter-push.gradle b/CalendarView/script/gradle-jcenter-push.gradle
new file mode 100644
index 00000000..ff1d2337
--- /dev/null
+++ b/CalendarView/script/gradle-jcenter-push.gradle
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2016 huanghaibin_dev
+ * WebSite https://github.com/MiracleTimes-Dev
+ * Version 1.0.2
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+apply plugin: 'com.jfrog.bintray'
+apply plugin: 'com.github.dcendents.android-maven'
+
+afterEvaluate { project ->
+ install {
+ repositories.mavenInstaller {
+ configurePOM(pom);
+ }
+ }
+
+ task sourcesJar(type: Jar) {
+ classifier = 'sources'
+ from android.sourceSets.main.java.srcDirs
+ }
+
+ task javadoc(type: Javadoc) {
+ failOnError false
+ source = android.sourceSets.main.java.srcDirs
+ options {
+ links "http://docs.oracle.com/javase/7/docs/api/"
+ linksOffline "http://d.android.com/reference", "${android.sdkDirectory}/docs/reference"
+ }
+ classpath += project.android.libraryVariants.toList().first().javaCompile.classpath
+ classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
+ }
+
+ task javadocJar(type: Jar, dependsOn: javadoc) {
+ classifier = 'javadoc'
+ from javadoc.destinationDir
+ }
+
+ artifacts {
+ archives javadocJar
+ archives sourcesJar
+ }
+}
+
+private configurePOM(def pom) {
+ pom.project {
+ packaging POM_PACKAGING
+ groupId POM_GROUP_ID
+ artifactId POM_ARTIFACT_ID
+
+ name POM_NAME
+ description POM_DESCRIPTION
+ url POM_URL
+
+ version VERSION_NAME
+ inceptionYear POM_INCEPTION_YEAR
+
+ licenses {
+ license {
+ name POM_LICENCE_NAME
+ url POM_LICENCE_URL
+ distribution POM_LICENCE_DIST
+ comments POM_LICENCE_COMMENTS
+ }
+ }
+
+ developers {
+ developer {
+ id POM_DEVELOPER_ID
+ name POM_DEVELOPER_NAME
+ email POM_DEVELOPER_EMAIL
+ url POM_DEVELOPER_URL
+ }
+ }
+
+ scm {
+ url POM_SCM_URL
+ connection POM_SCM_CONNECTION
+ developerConnection POM_SCM_DEV_CONNECTION
+ }
+
+ issueManagement {
+ system POM_ISSUE_MANAGEMENT_SYSTEM
+ url POM_ISSUE_MANAGEMENT_URL
+ }
+ }
+}
+
+Properties properties = new Properties()
+properties.load(project.rootProject.file('local.properties').newDataInputStream())
+
+
+bintray {
+ user = properties.getProperty("BINTRAY_USER")
+ key = properties.getProperty("BINTRAY_KEY")
+
+ group = POM_GROUP_ID
+ version = VERSION_NAME
+
+ configurations = ['archives']
+
+ pkg {
+ repo = POM_BINTRAY_PRPO
+ name = POM_BINTRAY_NAME
+ desc = POM_DESCRIPTION
+ websiteUrl = POM_URL
+ vcsUrl = POM_GIT_URL
+ licenses = POM_LICENCE_ALL
+ publish = true
+ publicDownloadNumbers = true
+ version {
+ desc = POM_DESCRIPTION
+ gpg {
+ sign = false //Determines whether to GPG sign the files. The default is false
+ passphrase = properties.getProperty("BINTRAY_GPG_PASSWORD")
+ //Optional. The passphrase for GPG signing'
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CalendarView/settings.gradle b/CalendarView/settings.gradle
new file mode 100644
index 00000000..c4fc0f49
--- /dev/null
+++ b/CalendarView/settings.gradle
@@ -0,0 +1 @@
+include ':app', ':calendarview'