├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── othershe │ │ └── calendarviewtest │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── othershe │ │ │ └── calendarviewtest │ │ │ ├── MainActivity.java │ │ │ └── MultiChooseActivity.java │ └── res │ │ ├── drawable │ │ └── circle_bg.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_multi_choose.xml │ │ ├── input_layout.xml │ │ └── item_layout.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── last.png │ │ └── next.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── othershe │ └── calendarviewtest │ └── ExampleUnitTest.java ├── build.gradle ├── calendarview ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── othershe │ │ └── calendarview │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── othershe │ │ │ └── calendarview │ │ │ ├── bean │ │ │ ├── AttrsBean.java │ │ │ └── DateBean.java │ │ │ ├── listener │ │ │ ├── CalendarViewAdapter.java │ │ │ ├── OnMultiChooseListener.java │ │ │ ├── OnPagerChangeListener.java │ │ │ └── OnSingleChooseListener.java │ │ │ ├── utils │ │ │ ├── CalendarUtil.java │ │ │ ├── LunarUtil.java │ │ │ └── SolarUtil.java │ │ │ └── weiget │ │ │ ├── CalendarPagerAdapter.java │ │ │ ├── CalendarView.java │ │ │ ├── MonthView.java │ │ │ └── WeekView.java │ └── res │ │ ├── drawable │ │ └── blue_circle.png │ │ ├── layout │ │ └── item_month_layout.xml │ │ └── values │ │ ├── attrs.xml │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── othershe │ └── calendarview │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshot ├── 1.gif ├── 2.gif └── 3.gif └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files // 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/workspace.xml 38 | *.iws 39 | .idea/ 40 | 41 | # Keystore files 42 | *.jks 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CalendarView 2 | 3 | ### 功能 4 | * 1、支持农历、节气、常用节假日 5 | * 2、日期范围设置,默认支持的最大日期范围[1900.1~2049.12] 6 | * 3、禁用日期范围设置 7 | * 4、初始化选中单个或多个日期 8 | * 5、单选、多选操作 9 | * 6、跳转到指定日期 10 | * 7、替换农历为指定文字 11 | * 8、通过自定义属性定制日期外观,以及简单的日期item布局配置 12 | * 9、...... 13 | 14 | #### [基本原理](http://www.jianshu.com/p/304c8e70d0bd) 15 | #### [demo体验](https://fir.im/vehj?release_id=5a3c76cc959d695b3800035b) 16 | 17 | ### 效果图: 18 | |![](screenshot/1.gif)|![](screenshot/2.gif)|![](screenshot/3.gif)| 19 | |---|---|---| 20 | 21 | ### 基本用法: 22 | **Step 1. 添加JitPack仓库** 23 | 在当前项目等根目录下的 `build.gradle` 文件中添加如下内容: 24 | ``` gradle 25 | allprojects { 26 | repositories { 27 | ... 28 | maven { url "https://jitpack.io" } 29 | } 30 | } 31 | ``` 32 | **Step 2. 添加项目依赖** 33 | ``` gradle 34 | dependencies { 35 | compile 'com.github.Othershe:CalendarView:1.2.1' 36 | } 37 | ``` 38 | **Step 3. 在布局文件中添加WeekView、CalendarView** 39 | ```java 40 | 43 | 44 | 48 | ``` 49 | **Step 4. 相关初始化** 50 | ```java 51 | CalendarView calendarView = (CalendarView) findViewById(R.id.calendar); 52 | //日历init,年月日之间用点号隔开 53 | calendarView 54 | .setStartEndDate("2010.7", "2018.12") 55 | .setInitDate("2017.11") 56 | .setSingleDate("2017.12.12") 57 | .init(); 58 | 59 | //月份切换回调 60 | calendarView.setOnPagerChangeListener(new OnPagerChangeListener() { 61 | @Override 62 | public void onPagerChanged(int[] date) { 63 | 64 | } 65 |        }); 66 | 67 | //单选回调 68 | calendarView.setOnItemClickListener(new OnMonthItemClickListener() { 69 | @Override 70 | public void onMonthItemClick(View view, DateBean date) { 71 | 72 | } 73 |        }); 74 | 75 | ``` 76 | ### CalendarView相关方法 77 | |方法名|描述 78 | |---|---| 79 | |setInitDate(String date)|设置日历的初始显示年月 80 | |setStartEndDate(String startDate, String endDate)|设置日历开始、结束年月 81 | |setDisableStartEndDate(String startDate, String endDate)|设置日历的禁用日期范围(小于startDate、大于endDate禁用) 82 | |setSpecifyMap(HashMap map)|将显示农历的区域替换成指定文字 83 | |setSingleDate(String date)|设置单选时初始选中的日期(不设置则不默认选中) 84 | |getSingleDate()|得到单选时选中的日期 85 | |setMultiDate(List dates)|设置多选时默认选中的日期集合 86 | |getMultiDate()|得到多选时选中的全部日期 87 | |toSpecifyDate(int year, int month, int day)|单选时跳转到指定年月日 88 | |setOnCalendarViewAdapter(int layoutId, CalendarViewAdapter adapter)|设置自定义日期item样式 89 | |init()|日期初始化(以上属性配置完后调用) 90 | |setOnPagerChangeListener(OnPagerChangeListener listener)|设置月份切换回调 91 | |setOnSingleChooseListener(OnSingleChooseListener listener)|设置单选回调 92 | |setOnMultiChooseListener(OnMultiChooseListener listener)|设置多选回调 93 | |today()| 单选时跳转到今天 94 | |nextMonth()|跳转到下个月 95 | |lastMonth()|跳转到上个月 96 | |nextYear()|跳转到下一年的当前月 97 | |lastYear()|跳转到上一年的当前月 98 | |toStart()|跳转到日历的开始年月 99 | |toEnd()|跳转到日历的结束年月 100 | |CalendarUtil.getCurrentDate()|获得当前日期(今天) 101 | 102 | ### CalendarView的自定义属性 103 | namespace:xmlns:calendarview="http://schemas.android.com/apk/res-auto" 104 | 105 | |属性名|格式|描述|默认值 106 | |---|---|---|---| 107 | |choose_type|enum|设置单选(single)、多选(multi)|single 108 | |show_lunar|boolean|是否显示农历|true 109 | |show_last_next|boolean|是否在MonthView显示上月和下月日期|true 110 | |show_holiday|boolean|是否显示节假日|true 111 | |show_term|boolean|是否显示节气|true 112 | |switch_choose|boolean|单选时切换月份,是否选中上次的日期|true 113 | |solar_color|color|阳历日期的颜色 114 | |solar_size|integer|阳历的日期尺寸|14 115 | |lunar_color|color|农历的日期颜色 116 | |lunar_size|integer|农历的日期尺寸|8 117 | |holiday_color|color|节假日、节气的颜色 118 | |choose_color|color|选中的日期颜色 119 | |day_bg|reference|选中的日期背景(图片) 120 | 121 | ### WeekView的自定义属性 122 | namespace:xmlns:weekview="http://schemas.android.com/apk/res-auto" 123 | 124 | |属性名|格式|描述|默认值 125 | |---|---|---|---| 126 | |week_str|string|周的表示形式,用点隔开(例如:日.一.二.三.四.五.六) 127 | |week_color|color|周的颜色 128 | |week_size|integer|周的尺寸|12 129 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion "26.0.2" 6 | defaultConfig { 7 | applicationId "com.othershe.calendarview" 8 | minSdkVersion 14 9 | targetSdkVersion 26 10 | versionCode 2 11 | versionName "2.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:26.1.0' 25 | compile project(':calendarview') 26 | } 27 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/othershe/calendarviewtest/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarviewtest; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.othershe.calendarview", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/othershe/calendarviewtest/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarviewtest; 2 | 3 | import android.content.DialogInterface; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AlertDialog; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.text.TextUtils; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.widget.EditText; 12 | import android.widget.TextView; 13 | import android.widget.Toast; 14 | 15 | import com.othershe.calendarview.bean.DateBean; 16 | import com.othershe.calendarview.listener.OnSingleChooseListener; 17 | import com.othershe.calendarview.listener.OnPagerChangeListener; 18 | import com.othershe.calendarview.utils.CalendarUtil; 19 | import com.othershe.calendarview.weiget.CalendarView; 20 | 21 | import java.util.HashMap; 22 | 23 | public class MainActivity extends AppCompatActivity { 24 | 25 | private CalendarView calendarView; 26 | private TextView chooseDate; 27 | 28 | private int[] cDate = CalendarUtil.getCurrentDate(); 29 | 30 | @Override 31 | protected void onCreate(Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | setContentView(R.layout.activity_main); 34 | final TextView title = (TextView) findViewById(R.id.title); 35 | //当前选中的日期 36 | chooseDate = findViewById(R.id.choose_date); 37 | 38 | calendarView = (CalendarView) findViewById(R.id.calendar); 39 | HashMap map = new HashMap<>(); 40 | map.put("2017.10.30", "qaz"); 41 | map.put("2017.10.1", "wsx"); 42 | map.put("2017.11.12", "yhn"); 43 | map.put("2017.9.15", "edc"); 44 | map.put("2017.11.6", "rfv"); 45 | map.put("2017.11.11", "tgb"); 46 | calendarView 47 | // .setSpecifyMap(map) 48 | .setStartEndDate("2016.1", "2028.12") 49 | .setDisableStartEndDate("2016.10.10", "2028.10.10") 50 | .setInitDate(cDate[0] + "." + cDate[1]) 51 | .setSingleDate(cDate[0] + "." + cDate[1] + "." + cDate[2]) 52 | .init(); 53 | 54 | // .setOnCalendarViewAdapter(R.layout.item_layout, new CalendarViewAdapter() { 55 | // @Override 56 | // public TextView[] convertView(View view, DateBean date) { 57 | // TextView solarDay = (TextView) view.findViewById(R.id.solar_day); 58 | // TextView lunarDay = (TextView) view.findViewById(R.id.lunar_day); 59 | // return new TextView[]{solarDay, lunarDay}; 60 | // } 61 | // }).init(); 62 | 63 | title.setText(cDate[0] + "年" + cDate[1] + "月"); 64 | chooseDate.setText("当前选中的日期:" + cDate[0] + "年" + cDate[1] + "月" + cDate[2] + "日"); 65 | 66 | calendarView.setOnPagerChangeListener(new OnPagerChangeListener() { 67 | @Override 68 | public void onPagerChanged(int[] date) { 69 | title.setText(date[0] + "年" + date[1] + "月"); 70 | } 71 | }); 72 | 73 | calendarView.setOnSingleChooseListener(new OnSingleChooseListener() { 74 | @Override 75 | public void onSingleChoose(View view, DateBean date) { 76 | title.setText(date.getSolar()[0] + "年" + date.getSolar()[1] + "月"); 77 | if (date.getType() == 1) { 78 | chooseDate.setText("当前选中的日期:" + date.getSolar()[0] + "年" + date.getSolar()[1] + "月" + date.getSolar()[2] + "日"); 79 | } 80 | } 81 | }); 82 | } 83 | 84 | public void someday(View v) { 85 | View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.input_layout, null); 86 | final EditText year = (EditText) view.findViewById(R.id.year); 87 | final EditText month = (EditText) view.findViewById(R.id.month); 88 | final EditText day = (EditText) view.findViewById(R.id.day); 89 | 90 | new AlertDialog.Builder(this) 91 | .setView(view) 92 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 93 | @Override 94 | public void onClick(DialogInterface dialog, int which) { 95 | if (TextUtils.isEmpty(year.getText()) 96 | || TextUtils.isEmpty(month.getText()) 97 | || TextUtils.isEmpty(day.getText())) { 98 | Toast.makeText(MainActivity.this, "请完善日期!", Toast.LENGTH_SHORT).show(); 99 | return; 100 | } 101 | boolean result = calendarView.toSpecifyDate(Integer.valueOf(year.getText().toString()), 102 | Integer.valueOf(month.getText().toString()), 103 | Integer.valueOf(day.getText().toString())); 104 | if (!result) { 105 | Toast.makeText(MainActivity.this, "日期越界!", Toast.LENGTH_SHORT).show(); 106 | } else { 107 | chooseDate.setText("当前选中的日期:" + year.getText() + "年" + month.getText() + "月" + day.getText() + "日"); 108 | } 109 | dialog.dismiss(); 110 | } 111 | }) 112 | .setNegativeButton("取消", null).show(); 113 | } 114 | 115 | public void today(View view) { 116 | calendarView.today(); 117 | chooseDate.setText("当前选中的日期:" + cDate[0] + "年" + cDate[1] + "月" + cDate[2] + "日"); 118 | } 119 | 120 | public void lastMonth(View view) { 121 | calendarView.lastMonth(); 122 | } 123 | 124 | public void nextMonth(View view) { 125 | calendarView.nextMonth(); 126 | } 127 | 128 | public void start(View view) { 129 | calendarView.toStart(); 130 | } 131 | 132 | public void end(View view) { 133 | calendarView.toEnd(); 134 | } 135 | 136 | public void lastYear(View view) { 137 | calendarView.lastYear(); 138 | } 139 | 140 | public void nextYear(View view) { 141 | calendarView.nextYear(); 142 | } 143 | 144 | public void multiChoose(View view) { 145 | startActivity(new Intent(MainActivity.this, MultiChooseActivity.class)); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /app/src/main/java/com/othershe/calendarviewtest/MultiChooseActivity.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarviewtest; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.widget.TextView; 8 | 9 | import com.othershe.calendarview.weiget.CalendarView; 10 | import com.othershe.calendarview.bean.DateBean; 11 | import com.othershe.calendarview.listener.OnMultiChooseListener; 12 | import com.othershe.calendarview.listener.OnPagerChangeListener; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | public class MultiChooseActivity extends AppCompatActivity { 18 | 19 | private CalendarView calendarView; 20 | private TextView chooseDate; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_multi_choose); 26 | 27 | final TextView title = (TextView) findViewById(R.id.title); 28 | 29 | final StringBuilder sb = new StringBuilder(); 30 | 31 | chooseDate = (TextView) findViewById(R.id.choose_date); 32 | 33 | List list = new ArrayList<>(); 34 | list.add("2017.11.11"); 35 | list.add("2017.11.12"); 36 | list.add("2017.12.22"); 37 | list.add("2017.12.25"); 38 | 39 | calendarView = (CalendarView) findViewById(R.id.calendar); 40 | calendarView 41 | .setStartEndDate("2017.1", "2019.12") 42 | .setDisableStartEndDate("2017.10.7", "2019.10.7") 43 | .setInitDate("2017.11") 44 | .setMultiDate(list) 45 | .init(); 46 | 47 | title.setText(2017 + "年" + 11 + "月"); 48 | 49 | for (String d : list) { 50 | sb.append("选中:" + d + "\n"); 51 | } 52 | chooseDate.setText(sb.toString()); 53 | 54 | calendarView.setOnMultiChooseListener(new OnMultiChooseListener() { 55 | @Override 56 | public void onMultiChoose(View view, DateBean date, boolean flag) { 57 | String d = date.getSolar()[0] + "." + date.getSolar()[1] + "." + date.getSolar()[2] + "."; 58 | if (flag) {//选中 59 | sb.append("选中:" + d + "\n"); 60 | } else {//取消选中 61 | sb.append("取消:" + d + "\n"); 62 | } 63 | chooseDate.setText(sb.toString()); 64 | 65 | //test 66 | if (flag) { 67 | for (DateBean db : calendarView.getMultiDate()) { 68 | Log.e("date:", "" + db.getSolar()[0] + db.getSolar()[1] + db.getSolar()[2]); 69 | } 70 | } 71 | } 72 | }); 73 | 74 | calendarView.setOnPagerChangeListener(new OnPagerChangeListener() { 75 | @Override 76 | public void onPagerChanged(int[] date) { 77 | title.setText(date[0] + "年" + date[1] + "月"); 78 | } 79 | }); 80 | } 81 | 82 | public void lastMonth(View view) { 83 | calendarView.lastMonth(); 84 | } 85 | 86 | public void nextMonth(View view) { 87 | calendarView.nextMonth(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/circle_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 | 17 | 23 | 24 | 31 | 32 | 39 | 40 | 41 | 42 | 45 | 46 | 55 | 56 | 60 | 61 | 71 | 72 | 82 | 83 | 93 | 94 | 104 | 105 | 115 | 116 | 126 | 127 | 137 | 138 | 139 | 140 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_multi_choose.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 21 | 22 | 29 | 30 | 37 | 38 | 39 | 40 | 43 | 44 | 49 | 50 | 54 | 55 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/res/layout/input_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 20 | 21 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shehuan/CalendarView/ff07156464db5eea70c82ef11b1598951fd0eb2d/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/last.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shehuan/CalendarView/ff07156464db5eea70c82ef11b1598951fd0eb2d/app/src/main/res/mipmap-hdpi/last.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shehuan/CalendarView/ff07156464db5eea70c82ef11b1598951fd0eb2d/app/src/main/res/mipmap-hdpi/next.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shehuan/CalendarView/ff07156464db5eea70c82ef11b1598951fd0eb2d/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shehuan/CalendarView/ff07156464db5eea70c82ef11b1598951fd0eb2d/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shehuan/CalendarView/ff07156464db5eea70c82ef11b1598951fd0eb2d/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shehuan/CalendarView/ff07156464db5eea70c82ef11b1598951fd0eb2d/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | CalendarView 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/othershe/calendarviewtest/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarviewtest; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.0.0' 10 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | jcenter() 20 | google() 21 | } 22 | } 23 | 24 | task clean(type: Delete) { 25 | delete rootProject.buildDir 26 | } 27 | -------------------------------------------------------------------------------- /calendarview/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /calendarview/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | // JitPack Maven 3 | apply plugin: 'com.github.dcendents.android-maven' 4 | // Your Group 5 | group='com.github.Othershe' 6 | 7 | android { 8 | compileSdkVersion 26 9 | buildToolsVersion "26.0.2" 10 | 11 | defaultConfig { 12 | minSdkVersion 14 13 | targetSdkVersion 26 14 | versionCode 2 15 | versionName "2.0" 16 | 17 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 18 | 19 | } 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | } 27 | 28 | dependencies { 29 | compile fileTree(dir: 'libs', include: ['*.jar']) 30 | compile 'com.android.support:appcompat-v7:26.1.0' 31 | } 32 | -------------------------------------------------------------------------------- /calendarview/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /calendarview/src/androidTest/java/com/othershe/calendarview/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.othershe.calendarview.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /calendarview/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /calendarview/src/main/java/com/othershe/calendarview/bean/AttrsBean.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview.bean; 2 | 3 | import android.graphics.Color; 4 | 5 | import com.othershe.calendarview.R; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public class AttrsBean { 11 | 12 | private int[] startDate;//日历的开始年、月 13 | private int[] endDate;//日历的结束年、月 14 | private int[] singleDate;//单选是默认选中的日期(年、月、日) 15 | private List multiDates;//多选时默认选中的日期集合 16 | private int[] disableStartDate;//单选时默认选中的年、月、日disableStar 17 | private int[] disableEndDate;//单选时默认选中的年、月、日 18 | private boolean showLastNext = true;//是否显示上个月、下个月 19 | private boolean showLunar = true;//是否显示农历 20 | private boolean showHoliday = true;//是否显示节假日(不显示农历则节假日无法显示,节假日会覆盖农历显示) 21 | private boolean showTerm = true;//是否显示节气 22 | private boolean switchChoose = true;//单选时切换月份,是否选中上次的日期 23 | private int colorSolar = Color.BLACK;//阳历的日期颜色 24 | private int colorLunar = Color.parseColor("#999999");//阴历的日期颜色 25 | private int colorHoliday = Color.parseColor("#EC9729");//节假日的颜色 26 | private int colorChoose = Color.WHITE;//选中的日期文字颜色 27 | private int sizeSolar = 14;//阳历日期文字尺寸 28 | private int sizeLunar = 8;//阴历日期文字尺寸 29 | private int dayBg = R.drawable.blue_circle;//选中的背景 30 | private Map specifyMap;//指定日期对应的文字map 31 | private int chooseType = 0;//表示日历是单选还是多选 32 | 33 | public int[] getStartDate() { 34 | return startDate; 35 | } 36 | 37 | public void setStartDate(int[] startDate) { 38 | this.startDate = startDate; 39 | } 40 | 41 | public int[] getEndDate() { 42 | return endDate; 43 | } 44 | 45 | public void setEndDate(int[] endDate) { 46 | this.endDate = endDate; 47 | } 48 | 49 | public int[] getSingleDate() { 50 | return singleDate; 51 | } 52 | 53 | public void setSingleDate(int[] singleDate) { 54 | this.singleDate = singleDate; 55 | } 56 | 57 | public List getMultiDates() { 58 | return multiDates; 59 | } 60 | 61 | public int[] getDisableStartDate() { 62 | return disableStartDate; 63 | } 64 | 65 | public void setDisableStartDate(int[] disableStartDate) { 66 | this.disableStartDate = disableStartDate; 67 | } 68 | 69 | public int[] getDisableEndDate() { 70 | return disableEndDate; 71 | } 72 | 73 | public void setDisableEndDate(int[] disableEndDate) { 74 | this.disableEndDate = disableEndDate; 75 | } 76 | 77 | public void setMultiDates(List multiDates) { 78 | this.multiDates = multiDates; 79 | } 80 | 81 | public boolean isShowLastNext() { 82 | return showLastNext; 83 | } 84 | 85 | public void setShowLastNext(boolean showLastNext) { 86 | this.showLastNext = showLastNext; 87 | } 88 | 89 | public boolean isShowLunar() { 90 | return showLunar; 91 | } 92 | 93 | public void setShowLunar(boolean showLunar) { 94 | this.showLunar = showLunar; 95 | } 96 | 97 | public boolean isShowHoliday() { 98 | return showHoliday; 99 | } 100 | 101 | public void setShowHoliday(boolean showHoliday) { 102 | this.showHoliday = showHoliday; 103 | } 104 | 105 | public boolean isShowTerm() { 106 | return showTerm; 107 | } 108 | 109 | public void setShowTerm(boolean showTerm) { 110 | this.showTerm = showTerm; 111 | } 112 | 113 | public boolean isSwitchChoose() { 114 | return switchChoose; 115 | } 116 | 117 | public void setSwitchChoose(boolean switchChoose) { 118 | this.switchChoose = switchChoose; 119 | } 120 | 121 | public int getColorSolar() { 122 | return colorSolar; 123 | } 124 | 125 | public void setColorSolar(int colorSolar) { 126 | this.colorSolar = colorSolar; 127 | } 128 | 129 | public int getColorLunar() { 130 | return colorLunar; 131 | } 132 | 133 | public void setColorLunar(int colorLunar) { 134 | this.colorLunar = colorLunar; 135 | } 136 | 137 | public int getColorHoliday() { 138 | return colorHoliday; 139 | } 140 | 141 | public void setColorHoliday(int colorHoliday) { 142 | this.colorHoliday = colorHoliday; 143 | } 144 | 145 | public int getColorChoose() { 146 | return colorChoose; 147 | } 148 | 149 | public void setColorChoose(int colorChoose) { 150 | this.colorChoose = colorChoose; 151 | } 152 | 153 | public int getSizeSolar() { 154 | return sizeSolar; 155 | } 156 | 157 | public void setSizeSolar(int sizeSolar) { 158 | this.sizeSolar = sizeSolar; 159 | } 160 | 161 | public int getSizeLunar() { 162 | return sizeLunar; 163 | } 164 | 165 | public void setSizeLunar(int sizeLunar) { 166 | this.sizeLunar = sizeLunar; 167 | } 168 | 169 | public int getDayBg() { 170 | return dayBg; 171 | } 172 | 173 | public void setDayBg(int dayBg) { 174 | this.dayBg = dayBg; 175 | } 176 | 177 | public Map getSpecifyMap() { 178 | return specifyMap; 179 | } 180 | 181 | public void setSpecifyMap(Map specifyMap) { 182 | this.specifyMap = specifyMap; 183 | } 184 | 185 | public int getChooseType() { 186 | return chooseType; 187 | } 188 | 189 | public void setChooseType(int chooseType) { 190 | this.chooseType = chooseType; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /calendarview/src/main/java/com/othershe/calendarview/bean/DateBean.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview.bean; 2 | 3 | public class DateBean { 4 | private int[] solar;//阳历年、月、日 5 | private String[] lunar;//农历月、日 6 | private String solarHoliday;//阳历节假日 7 | private String lunarHoliday;//阳历节假日 8 | private int type;//0:上月,1:当月,2:下月 9 | private String term;//节气 10 | 11 | public int[] getSolar() { 12 | return solar; 13 | } 14 | 15 | public void setSolar(int year, int month, int day) { 16 | this.solar = new int[]{year, month, day}; 17 | } 18 | 19 | public String[] getLunar() { 20 | return lunar; 21 | } 22 | 23 | public void setLunar(String[] lunar) { 24 | this.lunar = lunar; 25 | } 26 | 27 | public String getSolarHoliday() { 28 | return solarHoliday; 29 | } 30 | 31 | public void setSolarHoliday(String solarHoliday) { 32 | this.solarHoliday = solarHoliday; 33 | } 34 | 35 | public String getLunarHoliday() { 36 | return lunarHoliday; 37 | } 38 | 39 | public void setLunarHoliday(String lunarHoliday) { 40 | this.lunarHoliday = lunarHoliday; 41 | } 42 | 43 | public int getType() { 44 | return type; 45 | } 46 | 47 | public void setType(int type) { 48 | this.type = type; 49 | } 50 | 51 | public String getTerm() { 52 | return term; 53 | } 54 | 55 | public void setTerm(String term) { 56 | this.term = term; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /calendarview/src/main/java/com/othershe/calendarview/listener/CalendarViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview.listener; 2 | 3 | import android.view.View; 4 | import android.widget.TextView; 5 | 6 | import com.othershe.calendarview.bean.DateBean; 7 | 8 | public interface CalendarViewAdapter { 9 | /** 10 | * 返回阳历、阴历两个TextView 11 | * 12 | * @param view 13 | * @param date 14 | * @return 15 | */ 16 | TextView[] convertView(View view, DateBean date); 17 | } 18 | -------------------------------------------------------------------------------- /calendarview/src/main/java/com/othershe/calendarview/listener/OnMultiChooseListener.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview.listener; 2 | 3 | import android.view.View; 4 | 5 | import com.othershe.calendarview.bean.DateBean; 6 | 7 | /** 8 | * 多选接口 9 | */ 10 | public interface OnMultiChooseListener { 11 | /** 12 | * @param view 13 | * @param date 14 | * @param flag 多选时flag=true代表选中数据,flag=false代表取消选中 15 | */ 16 | void onMultiChoose(View view, DateBean date, boolean flag); 17 | } 18 | -------------------------------------------------------------------------------- /calendarview/src/main/java/com/othershe/calendarview/listener/OnPagerChangeListener.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview.listener; 2 | 3 | /** 4 | * 页面切换接口 5 | */ 6 | public interface OnPagerChangeListener { 7 | /** 8 | * @param date date[0]年份 date[1]月份 date[2]日 9 | */ 10 | void onPagerChanged(int[] date); 11 | } 12 | -------------------------------------------------------------------------------- /calendarview/src/main/java/com/othershe/calendarview/listener/OnSingleChooseListener.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview.listener; 2 | 3 | import android.view.View; 4 | 5 | import com.othershe.calendarview.bean.DateBean; 6 | 7 | /** 8 | * 日期点击接口 9 | */ 10 | public interface OnSingleChooseListener { 11 | /** 12 | * @param view 13 | * @param date 14 | */ 15 | void onSingleChoose(View view, DateBean date); 16 | } 17 | -------------------------------------------------------------------------------- /calendarview/src/main/java/com/othershe/calendarview/utils/CalendarUtil.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview.utils; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | import android.util.TypedValue; 6 | 7 | import com.othershe.calendarview.bean.DateBean; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Calendar; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | public class CalendarUtil { 15 | /** 16 | * 获得当月显示的日期(上月 + 当月 + 下月) 17 | * 18 | * @param year 当前年份 19 | * @param month 当前月份 20 | * @return 21 | */ 22 | public static List getMonthDate(int year, int month, Map map) { 23 | List datas = new ArrayList<>(); 24 | int week = SolarUtil.getFirstWeekOfMonth(year, month - 1); 25 | 26 | int lastYear; 27 | int lastMonth; 28 | if (month == 1) { 29 | lastMonth = 12; 30 | lastYear = year - 1; 31 | } else { 32 | lastMonth = month - 1; 33 | lastYear = year; 34 | } 35 | int lastMonthDays = SolarUtil.getMonthDays(lastYear, lastMonth);//上个月总天数 36 | 37 | int currentMonthDays = SolarUtil.getMonthDays(year, month);//当前月总天数 38 | 39 | int nextYear; 40 | int nextMonth; 41 | if (month == 12) { 42 | nextMonth = 1; 43 | nextYear = year + 1; 44 | } else { 45 | nextMonth = month + 1; 46 | nextYear = year; 47 | } 48 | 49 | for (int i = 0; i < week; i++) { 50 | datas.add(initDateBean(lastYear, lastMonth, lastMonthDays - week + 1 + i, 0, map)); 51 | } 52 | 53 | for (int i = 0; i < currentMonthDays; i++) { 54 | datas.add(initDateBean(year, month, i + 1, 1, map)); 55 | } 56 | 57 | for (int i = 0; i < 7 * getMonthRows(year, month) - currentMonthDays - week; i++) { 58 | datas.add(initDateBean(nextYear, nextMonth, i + 1, 2, map)); 59 | } 60 | 61 | return datas; 62 | } 63 | 64 | private static DateBean initDateBean(int year, int month, int day, int type, Map map) { 65 | DateBean dateBean = new DateBean(); 66 | dateBean.setSolar(year, month, day); 67 | 68 | if (map == null) { 69 | String[] temp = LunarUtil.solarToLunar(year, month, day); 70 | dateBean.setLunar(new String[]{temp[0], temp[1]}); 71 | dateBean.setLunarHoliday(temp[2]); 72 | } else { 73 | if (map.containsKey(year + "." + month + "." + day)) { 74 | dateBean.setLunar(new String[]{"", map.get(year + "." + month + "." + day), ""}); 75 | } else { 76 | dateBean.setLunar(new String[]{"", "", ""}); 77 | } 78 | } 79 | 80 | dateBean.setType(type); 81 | dateBean.setTerm(LunarUtil.getTermString(year, month - 1, day)); 82 | 83 | if (type == 0) { 84 | dateBean.setSolarHoliday(SolarUtil.getSolarHoliday(year, month, day - 1)); 85 | } else { 86 | dateBean.setSolarHoliday(SolarUtil.getSolarHoliday(year, month, day)); 87 | } 88 | return dateBean; 89 | } 90 | 91 | public static DateBean getDateBean(int year, int month, int day) { 92 | return initDateBean(year, month, day, 1, null); 93 | } 94 | 95 | /** 96 | * 计算当前月需要显示几行 97 | * 98 | * @param year 99 | * @param month 100 | * @return 101 | */ 102 | public static int getMonthRows(int year, int month) { 103 | int items = SolarUtil.getFirstWeekOfMonth(year, month - 1) + SolarUtil.getMonthDays(year, month); 104 | int rows = items % 7 == 0 ? items / 7 : (items / 7) + 1; 105 | if (rows == 4) { 106 | rows = 5; 107 | } 108 | return rows; 109 | } 110 | 111 | /** 112 | * 根据ViewPager position 得到对应年月 113 | * 114 | * @param position 115 | * @return 116 | */ 117 | public static int[] positionToDate(int position, int startY, int startM) { 118 | int year = position / 12 + startY; 119 | int month = position % 12 + startM; 120 | 121 | if (month > 12) { 122 | month = month % 12; 123 | year = year + 1; 124 | } 125 | 126 | return new int[]{year, month}; 127 | } 128 | 129 | /** 130 | * 根据年月得到ViewPager position 131 | * 132 | * @param year 133 | * @param month 134 | * @return 135 | */ 136 | public static int dateToPosition(int year, int month, int startY, int startM) { 137 | return (year - startY) * 12 + month - startM; 138 | } 139 | 140 | /** 141 | * 计算当前日期 142 | * 143 | * @return 144 | */ 145 | public static int[] getCurrentDate() { 146 | Calendar calendar = Calendar.getInstance(); 147 | return new int[]{calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH)}; 148 | } 149 | 150 | public static int[] strToArray(String str) { 151 | if (!TextUtils.isEmpty(str)) { 152 | String[] strArray = str.split("\\."); 153 | int[] result = new int[strArray.length]; 154 | for (int i = 0; i < strArray.length; i++) { 155 | result[i] = Integer.valueOf(strArray[i]); 156 | } 157 | return result; 158 | } 159 | return null; 160 | } 161 | 162 | public static long dateToMillis(int[] date) { 163 | int day = date.length == 2 ? 1 : date[2]; 164 | Calendar calendar = Calendar.getInstance(); 165 | calendar.set(date[0], date[1], day); 166 | return calendar.getTimeInMillis(); 167 | } 168 | 169 | public static int getPxSize(Context context, int size) { 170 | return size * context.getResources().getDisplayMetrics().densityDpi; 171 | } 172 | 173 | public static int getTextSize1(Context context, int size) { 174 | return (int) (size * context.getResources().getDisplayMetrics().scaledDensity); 175 | } 176 | 177 | public static int getTextSize(Context context, int size) { 178 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, size, context.getResources().getDisplayMetrics()); 179 | 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /calendarview/src/main/java/com/othershe/calendarview/utils/LunarUtil.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview.utils; 2 | 3 | import java.text.ParseException; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | import java.util.GregorianCalendar; 8 | import java.util.TimeZone; 9 | 10 | public class LunarUtil { 11 | //1900-2049 12 | private static final int[] LUNAR_INFO = { 13 | 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, 14 | 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, 15 | 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, 16 | 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, 17 | 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, 18 | 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5d0, 0x14573, 0x052d0, 0x0a9a8, 0x0e950, 0x06aa0, 19 | 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, 20 | 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6, 21 | 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, 22 | 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0, 23 | 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, 24 | 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, 25 | 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, 26 | 0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 27 | 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0 28 | }; 29 | 30 | private static final int[] solarTermInfo = { 31 | 0, 21208, 42467, 63836, 85337, 107014, 128867, 150921, 32 | 173149, 195551, 218072, 240693, 263343, 285989, 308563, 331033, 33 | 353350, 375494, 397447, 419210, 440795, 462224, 483532, 504758 34 | }; 35 | 36 | private static final String[] solarTerm = { 37 | "小寒", "大寒", "立春", "雨水", "惊蛰", "春分", "清明", "谷雨", "立夏", "小满", "芒种", "夏至", 38 | "小暑", "大暑", "立秋", "处暑", "白露", "秋分", "寒露", "霜降", "立冬", "小雪", "大雪", "冬至"}; 39 | 40 | private static final String[] monthInfo = new String[]{"", "正月", "二月", "三月", "四月", "五月", 41 | "六月", "七月", "八月", "九月", "十月", "冬月", "腊月"}; 42 | 43 | private static final String[] dayInfo = new String[]{"", "一", "二", "三", "四", "五", "六", "七", "八", "九"}; 44 | 45 | // 允许输入的最小年份 46 | private final static int MIN_YEAR = 1900; 47 | // 允许输入的最大年份 48 | private final static int MAX_YEAR = 2049; 49 | // 阳历日期计算起点 50 | private final static String START_DATE = "19000130"; 51 | 52 | /** 53 | * 阳历转阴历 54 | * 55 | * @param year 56 | * @param month 57 | * @param day 58 | * @return 59 | */ 60 | public static String[] solarToLunar(int year, int month, int day) { 61 | int i; 62 | int temp = 0; 63 | int lunarYear; 64 | int lunarMonth; //农历月份 65 | int lunarDay; //农历当月第几天 66 | boolean leapMonthFlag = false; 67 | 68 | String solarDate = "" + year; 69 | 70 | if (month < 10) { 71 | solarDate = solarDate + "0" + month; 72 | } else { 73 | solarDate = solarDate + month; 74 | } 75 | 76 | if (day < 10) { 77 | solarDate = solarDate + "0" + day; 78 | } else { 79 | solarDate = solarDate + day; 80 | } 81 | 82 | SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd"); 83 | Date myDate = null; 84 | Date startDate = null; 85 | try { 86 | myDate = formatter.parse(solarDate); 87 | startDate = formatter.parse(START_DATE); 88 | } catch (ParseException e) { 89 | e.printStackTrace(); 90 | } 91 | 92 | int offset = daysBetween(startDate, myDate); 93 | 94 | for (i = MIN_YEAR; i <= MAX_YEAR; i++) { 95 | temp = getYearDays(i); //求当年农历年天数 96 | if (offset - temp < 1) { 97 | break; 98 | } else { 99 | offset -= temp; 100 | } 101 | } 102 | lunarYear = i; 103 | 104 | int leapMonth = getLeapMonth(lunarYear);//计算该年闰哪个月 105 | //设定当年是否有闰月 106 | boolean isLeapYear; 107 | if (leapMonth > 0) { 108 | isLeapYear = true; 109 | } else { 110 | isLeapYear = false; 111 | } 112 | 113 | for (i = 1; i <= 12; i++) { 114 | if (i == leapMonth + 1 && isLeapYear) { 115 | temp = getLeapMonthDays(lunarYear); 116 | isLeapYear = false; 117 | leapMonthFlag = true; 118 | i--; 119 | } else { 120 | try { 121 | temp = getMonthDays(lunarYear, i); 122 | } catch (Exception e) { 123 | e.printStackTrace(); 124 | } 125 | } 126 | offset -= temp; 127 | if (offset <= 0) { 128 | break; 129 | } 130 | } 131 | 132 | offset += temp; 133 | lunarMonth = i; 134 | lunarDay = offset; 135 | return new String[]{(leapMonthFlag & (lunarMonth == leapMonth) ? "闰" : "") + getLunarMonth(lunarMonth), 136 | getLunarDay(lunarDay), getLunarHoliday(lunarYear, lunarMonth, lunarDay)}; 137 | } 138 | 139 | /** 140 | * 计算阴历年 月的天数 141 | * 142 | * @param lunarYear 阴历年 143 | * @param month 阴历月 144 | * @return 该月天数 145 | */ 146 | private static int getMonthDays(int lunarYear, int month) throws Exception { 147 | if ((month > 31) || (month < 0)) { 148 | throw (new Exception("月份有错!")); 149 | } 150 | int bit = 1 << (16 - month); 151 | if (((LUNAR_INFO[lunarYear - 1900] & 0x0FFFF) & bit) == 0) { 152 | return 29; 153 | } else { 154 | return 30; 155 | } 156 | } 157 | 158 | /** 159 | * 计算两个阳历日期相差的天数。 160 | * 161 | * @param startDate 开始时间 162 | * @param endDate 截至时间 163 | * @return 天数 164 | */ 165 | private static int daysBetween(Date startDate, Date endDate) { 166 | int days = 0; 167 | //将转换的两个时间对象转换成Calendar对象 168 | Calendar can1 = Calendar.getInstance(); 169 | can1.setTime(startDate); 170 | Calendar can2 = Calendar.getInstance(); 171 | can2.setTime(endDate); 172 | //拿出两个年份 173 | int year1 = can1.get(Calendar.YEAR); 174 | int year2 = can2.get(Calendar.YEAR); 175 | //天数 176 | 177 | Calendar can = null; 178 | //如果can1 < can2 179 | //减去小的时间在这一年已经过了的天数 180 | //加上大的时间已过的天数 181 | if (can1.before(can2)) { 182 | days -= can1.get(Calendar.DAY_OF_YEAR); 183 | days += can2.get(Calendar.DAY_OF_YEAR); 184 | can = can1; 185 | } else { 186 | days -= can2.get(Calendar.DAY_OF_YEAR); 187 | days += can1.get(Calendar.DAY_OF_YEAR); 188 | can = can2; 189 | } 190 | for (int i = 0; i < Math.abs(year2 - year1); i++) { 191 | //获取小的时间当前年的总天数 192 | days += can.getActualMaximum(Calendar.DAY_OF_YEAR); 193 | //再计算下一年。 194 | can.add(Calendar.YEAR, 1); 195 | } 196 | return days; 197 | } 198 | 199 | /** 200 | * 计算阴历年的总天数 201 | * 202 | * @param year 阴历年 203 | * @return 总天数 204 | */ 205 | private static int getYearDays(int year) { 206 | int sum = 29 * 12; 207 | for (int i = 0x8000; i >= 0x8; i >>= 1) { 208 | if ((LUNAR_INFO[year - 1900] & 0xfff0 & i) != 0) { 209 | sum++; 210 | } 211 | } 212 | return sum + getLeapMonthDays(year); 213 | } 214 | 215 | /** 216 | * 计算阴历年闰月多少天 217 | * 218 | * @param year 阴历年 219 | * @return 天数 220 | */ 221 | private static int getLeapMonthDays(int year) { 222 | if (getLeapMonth(year) != 0) { 223 | if ((LUNAR_INFO[year - MIN_YEAR] & 0xf0000) == 0) { 224 | return 29; 225 | } else { 226 | return 30; 227 | } 228 | } else { 229 | return 0; 230 | } 231 | } 232 | 233 | /** 234 | * 计算阴历年闰哪个月 1-12 , 没闰传回 0 235 | * 236 | * @param year 阴历年 237 | * @return 月份 238 | */ 239 | private static int getLeapMonth(int year) { 240 | return LUNAR_INFO[year - 1900] & 0xf; 241 | } 242 | 243 | private static String getLunarMonth(int month) { 244 | return monthInfo[month]; 245 | } 246 | 247 | private static String getLunarDay(int day) { 248 | if (day == 10) { 249 | return "初十"; 250 | } else if (day == 20) { 251 | return "二十"; 252 | } else if (day == 30) { 253 | return "三十"; 254 | } 255 | 256 | String str1 = ""; 257 | int d1 = day / 10; 258 | if (d1 == 0) { 259 | str1 = "初"; 260 | } else if (d1 == 1) { 261 | str1 = "十"; 262 | } else if (d1 == 2) { 263 | str1 = "廿"; 264 | } else if (d1 == 3) { 265 | str1 = "卅"; 266 | } 267 | return str1 + dayInfo[day % 10]; 268 | } 269 | 270 | /** 271 | * 计算农历节日 272 | * 273 | * @param year 274 | * @param month 275 | * @param day 276 | * @return 277 | */ 278 | private static String getLunarHoliday(int year, int month, int day) { 279 | 280 | 281 | String holiday = ""; 282 | if (month == 1 && day == 1) { 283 | holiday = "春节"; 284 | } else if (month == 1 && day == 15) { 285 | holiday = "元宵节"; 286 | } else if (month == 2 && day == 2) { 287 | holiday = "龙抬头"; 288 | } else if (month == 5 && day == 5) { 289 | holiday = "端午节"; 290 | } else if (month == 7 && day == 7) { 291 | holiday = "七夕"; 292 | } else if (month == 8 && day == 15) { 293 | holiday = "中秋节"; 294 | } else if (month == 9 && day == 9) { 295 | holiday = "重阳节"; 296 | } else if (month == 12 && day == 8) { 297 | holiday = "腊八"; 298 | } else if (month == 12 && day == 23) { 299 | holiday = "小年"; 300 | } else { 301 | if (month == 12) { 302 | if ((((daysInLunarMonth(year, month) == 29) && day == 29)) 303 | || ((((daysInLunarMonth(year, month) == 30) && day == 30)))) { 304 | holiday = "除夕"; 305 | } 306 | } 307 | } 308 | return holiday; 309 | } 310 | 311 | public static int daysInLunarMonth(int year, int month) { 312 | if ((LUNAR_INFO[year - MIN_YEAR] & (0x10000 >> month)) == 0) 313 | return 29; 314 | else 315 | return 30; 316 | } 317 | 318 | private static GregorianCalendar utcCal = null; 319 | 320 | /** 321 | * 根据阳历日期计算24节气 322 | */ 323 | public static String getTermString(int solarYear, int solarMonth, int solarDay) { 324 | String termString = ""; 325 | if (getSolarTermDay(solarYear, solarMonth * 2) == solarDay) { 326 | termString = solarTerm[solarMonth * 2]; 327 | } else if (getSolarTermDay(solarYear, solarMonth * 2 + 1) == solarDay) { 328 | termString = solarTerm[solarMonth * 2 + 1]; 329 | } 330 | return termString; 331 | } 332 | 333 | private static int getSolarTermDay(int solarYear, int index) { 334 | return getUTCDay(getSolarTermCalendar(solarYear, index)); 335 | } 336 | 337 | private static Date getSolarTermCalendar(int solarYear, int index) { 338 | long l = (long) 31556925974.7 * (solarYear - 1900) 339 | + solarTermInfo[index] * 60000L; 340 | l = l + UTC(1900, 0, 6, 2, 5, 0); 341 | return new Date(l); 342 | } 343 | 344 | private static synchronized int getUTCDay(Date date) { 345 | makeUTCCalendar(); 346 | synchronized (utcCal) { 347 | utcCal.clear(); 348 | utcCal.setTimeInMillis(date.getTime()); 349 | return utcCal.get(java.util.Calendar.DAY_OF_MONTH); 350 | } 351 | } 352 | 353 | private static void makeUTCCalendar() { 354 | if (utcCal == null) { 355 | utcCal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); 356 | } 357 | } 358 | 359 | private static synchronized long UTC(int y, int m, int d, int h, int min, int sec) { 360 | makeUTCCalendar(); 361 | synchronized (utcCal) { 362 | utcCal.clear(); 363 | utcCal.set(y, m, d, h, min, sec); 364 | return utcCal.getTimeInMillis(); 365 | } 366 | } 367 | /**计算24节气结束*/ 368 | } 369 | -------------------------------------------------------------------------------- /calendarview/src/main/java/com/othershe/calendarview/utils/SolarUtil.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview.utils; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.util.Calendar; 6 | 7 | public class SolarUtil { 8 | /** 9 | * 计算阳历节日 10 | * 11 | * @param year 12 | * @param month 13 | * @param day 14 | * @return 15 | */ 16 | public static String getSolarHoliday(int year, int month, int day) { 17 | int md = (int) (month * Math.pow(10, day >= 10 ? 2 : 1) + day); 18 | String holiday = ""; 19 | switch (md) { 20 | case 11: 21 | holiday = "元旦"; 22 | break; 23 | case 214: 24 | holiday = "情人节"; 25 | break; 26 | case 38: 27 | holiday = "妇女节"; 28 | break; 29 | case 312: 30 | holiday = "植树节"; 31 | break; 32 | case 41: 33 | holiday = "愚人节"; 34 | break; 35 | case 51: 36 | holiday = "劳动节"; 37 | break; 38 | case 54: 39 | holiday = "青年节"; 40 | break; 41 | case 512: 42 | holiday = "护士节"; 43 | break; 44 | case 61: 45 | holiday = "儿童节"; 46 | break; 47 | case 71: 48 | holiday = "建党节"; 49 | break; 50 | case 81: 51 | holiday = "建军节"; 52 | break; 53 | case 910: 54 | holiday = "教师节"; 55 | break; 56 | case 101: 57 | holiday = "国庆节"; 58 | break; 59 | case 1111: 60 | holiday = "光棍节"; 61 | break; 62 | case 1224: 63 | holiday = "平安夜"; 64 | break; 65 | case 1225: 66 | holiday = "圣诞节"; 67 | break; 68 | } 69 | 70 | if (!TextUtils.isEmpty(holiday)) { 71 | return holiday; 72 | } 73 | 74 | if (month == 4) { 75 | holiday = chingMingDay(year, day); 76 | } else if (month == 5) { 77 | if (day == motherFatherDay(year, month, 1)) { 78 | holiday = "母亲节"; 79 | } 80 | } else if (month == 6) { 81 | if (day == motherFatherDay(year, month, 2)) { 82 | holiday = "父亲节"; 83 | } 84 | } 85 | 86 | return holiday; 87 | } 88 | 89 | /** 90 | * 计算母亲节、父亲节是几号 91 | * 92 | * @param year 93 | * @param month 94 | * @param delta 95 | * @return 96 | */ 97 | private static int motherFatherDay(int year, int month, int delta) { 98 | int f = getFirstWeekOfMonth(year, month - 1); 99 | f = f == 0 ? 7 : f; 100 | return 7 - f + 1 + 7 * delta; 101 | } 102 | 103 | public static String chingMingDay(int year, int day) { 104 | String holiday = ""; 105 | if (day >= 4 && day <= 6) { 106 | if (year <= 1999) { 107 | int compare = (int) (((year - 1900) * 0.2422 + 5.59) - ((year - 1900) / 4)); 108 | if (compare == day) { 109 | holiday = "清明节"; 110 | } 111 | } else { 112 | int compare = (int) (((year - 2000) * 0.2422 + 4.81) - ((year - 2000) / 4)); 113 | if (compare == day) { 114 | holiday = "清明节"; 115 | } 116 | } 117 | } 118 | return holiday; 119 | } 120 | 121 | /** 122 | * 计算指定月份的天数 123 | * 124 | * @param year 125 | * @param month 126 | * @return 127 | */ 128 | public static int getMonthDays(int year, int month) { 129 | switch (month) { 130 | case 1: 131 | case 3: 132 | case 5: 133 | case 7: 134 | case 8: 135 | case 10: 136 | case 12: 137 | return 31; 138 | case 4: 139 | case 6: 140 | case 9: 141 | case 11: 142 | return 30; 143 | case 2: 144 | if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) { 145 | return 29; 146 | } else { 147 | return 28; 148 | } 149 | default: 150 | return -1; 151 | } 152 | } 153 | 154 | /** 155 | * 计算当月1号是周几 156 | * 157 | * @param year 158 | * @param month 159 | * @return 160 | */ 161 | public static int getFirstWeekOfMonth(int year, int month) { 162 | Calendar calendar = Calendar.getInstance(); 163 | calendar.set(year, month, 1); 164 | return calendar.get(Calendar.DAY_OF_WEEK) - 1; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /calendarview/src/main/java/com/othershe/calendarview/weiget/CalendarPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview.weiget; 2 | 3 | import android.support.v4.view.PagerAdapter; 4 | import android.util.SparseArray; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import com.othershe.calendarview.bean.AttrsBean; 9 | import com.othershe.calendarview.listener.CalendarViewAdapter; 10 | import com.othershe.calendarview.utils.CalendarUtil; 11 | import com.othershe.calendarview.utils.SolarUtil; 12 | 13 | import java.util.LinkedList; 14 | 15 | public class CalendarPagerAdapter extends PagerAdapter { 16 | 17 | //缓存上一次回收的MonthView 18 | private LinkedList cache = new LinkedList<>(); 19 | private SparseArray mViews = new SparseArray<>(); 20 | 21 | private int count; 22 | 23 | private int item_layout; 24 | private CalendarViewAdapter calendarViewAdapter; 25 | 26 | private AttrsBean mAttrsBean; 27 | 28 | public CalendarPagerAdapter(int count) { 29 | this.count = count; 30 | } 31 | 32 | @Override 33 | public int getCount() { 34 | return count; 35 | } 36 | 37 | @Override 38 | public boolean isViewFromObject(View view, Object object) { 39 | return view == object; 40 | } 41 | 42 | @Override 43 | public Object instantiateItem(ViewGroup container, int position) { 44 | MonthView view; 45 | if (!cache.isEmpty()) { 46 | view = cache.removeFirst(); 47 | } else { 48 | view = new MonthView(container.getContext()); 49 | } 50 | //根据position计算对应年、月 51 | int[] date = CalendarUtil.positionToDate(position, mAttrsBean.getStartDate()[0], mAttrsBean.getStartDate()[1]); 52 | view.setAttrsBean(mAttrsBean); 53 | view.setOnCalendarViewAdapter(item_layout, calendarViewAdapter); 54 | view.setDateList(CalendarUtil.getMonthDate(date[0], date[1], mAttrsBean.getSpecifyMap()), SolarUtil.getMonthDays(date[0], date[1])); 55 | mViews.put(position, view); 56 | container.addView(view); 57 | 58 | return view; 59 | } 60 | 61 | @Override 62 | public void destroyItem(ViewGroup container, int position, Object object) { 63 | container.removeView((MonthView) object); 64 | cache.addLast((MonthView) object); 65 | mViews.remove(position); 66 | } 67 | 68 | /** 69 | * 获得ViewPager缓存的View 70 | * 71 | * @return 72 | */ 73 | public SparseArray getViews() { 74 | return mViews; 75 | } 76 | 77 | 78 | public void setAttrsBean(AttrsBean attrsBean) { 79 | mAttrsBean = attrsBean; 80 | } 81 | 82 | public void setOnCalendarViewAdapter(int item_layout, CalendarViewAdapter calendarViewAdapter) { 83 | this.item_layout = item_layout; 84 | this.calendarViewAdapter = calendarViewAdapter; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /calendarview/src/main/java/com/othershe/calendarview/weiget/CalendarView.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview.weiget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.support.v4.view.ViewPager; 6 | import android.util.AttributeSet; 7 | import android.util.SparseArray; 8 | 9 | import com.othershe.calendarview.R; 10 | import com.othershe.calendarview.bean.AttrsBean; 11 | import com.othershe.calendarview.bean.DateBean; 12 | import com.othershe.calendarview.listener.CalendarViewAdapter; 13 | import com.othershe.calendarview.listener.OnMultiChooseListener; 14 | import com.othershe.calendarview.listener.OnSingleChooseListener; 15 | import com.othershe.calendarview.listener.OnPagerChangeListener; 16 | import com.othershe.calendarview.utils.CalendarUtil; 17 | import com.othershe.calendarview.utils.SolarUtil; 18 | 19 | import java.util.ArrayList; 20 | import java.util.HashMap; 21 | import java.util.HashSet; 22 | import java.util.List; 23 | import java.util.Set; 24 | 25 | public class CalendarView extends ViewPager { 26 | //记录当前PagerAdapter的position 27 | private int currentPosition; 28 | 29 | private OnPagerChangeListener pagerChangeListener; 30 | private OnSingleChooseListener singleChooseListener; 31 | private OnMultiChooseListener multiChooseListener; 32 | private CalendarViewAdapter calendarViewAdapter; 33 | private int item_layout; 34 | 35 | private int[] initDate;//日历初始显示的年月 36 | private int[] startDate;//日历的开始年、月 37 | private int[] endDate;//日历的结束年、月 38 | 39 | private int count;//ViewPager的页数 40 | private int[] lastClickDate = new int[2];//记录单选的ViewPager position以及选中的日期 41 | private SparseArray> chooseDate;//记录多选时全部选中的日期 42 | private Set positions;//多选时记录选中日期对应的ViewPager position 43 | 44 | private CalendarPagerAdapter calendarPagerAdapter; 45 | 46 | private AttrsBean mAttrsBean; 47 | 48 | public CalendarView(Context context) { 49 | this(context, null); 50 | } 51 | 52 | public CalendarView(Context context, AttributeSet attrs) { 53 | super(context, attrs); 54 | mAttrsBean = new AttrsBean(); 55 | initAttr(context, attrs); 56 | } 57 | 58 | private void initAttr(Context context, AttributeSet attrs) { 59 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CalendarView); 60 | for (int i = 0; i < ta.getIndexCount(); i++) { 61 | int attr = ta.getIndex(i); 62 | if (attr == R.styleable.CalendarView_show_last_next) { 63 | mAttrsBean.setShowLastNext(ta.getBoolean(attr, true)); 64 | } else if (attr == R.styleable.CalendarView_show_lunar) { 65 | mAttrsBean.setShowLunar(ta.getBoolean(attr, true)); 66 | } else if (attr == R.styleable.CalendarView_show_holiday) { 67 | mAttrsBean.setShowHoliday(ta.getBoolean(attr, true)); 68 | } else if (attr == R.styleable.CalendarView_show_term) { 69 | mAttrsBean.setShowTerm(ta.getBoolean(attr, true)); 70 | } else if (attr == R.styleable.CalendarView_switch_choose) { 71 | mAttrsBean.setSwitchChoose(ta.getBoolean(attr, true)); 72 | } else if (attr == R.styleable.CalendarView_solar_color) { 73 | mAttrsBean.setColorSolar(ta.getColor(attr, mAttrsBean.getColorSolar())); 74 | } else if (attr == R.styleable.CalendarView_solar_size) { 75 | mAttrsBean.setSizeSolar(CalendarUtil.getTextSize(context, ta.getInteger(attr, mAttrsBean.getSizeSolar()))); 76 | } else if (attr == R.styleable.CalendarView_lunar_color) { 77 | mAttrsBean.setColorLunar(ta.getColor(attr, mAttrsBean.getColorLunar())); 78 | } else if (attr == R.styleable.CalendarView_lunar_size) { 79 | mAttrsBean.setSizeLunar(CalendarUtil.getTextSize(context, ta.getInt(attr, mAttrsBean.getSizeLunar()))); 80 | } else if (attr == R.styleable.CalendarView_holiday_color) { 81 | mAttrsBean.setColorHoliday(ta.getColor(attr, mAttrsBean.getColorHoliday())); 82 | } else if (attr == R.styleable.CalendarView_choose_color) { 83 | mAttrsBean.setColorChoose(ta.getColor(attr, mAttrsBean.getColorChoose())); 84 | } else if (attr == R.styleable.CalendarView_day_bg) { 85 | mAttrsBean.setDayBg(ta.getResourceId(attr, mAttrsBean.getDayBg())); 86 | } else if (attr == R.styleable.CalendarView_choose_type) { 87 | mAttrsBean.setChooseType(ta.getInt(attr, 0)); 88 | } 89 | } 90 | 91 | ta.recycle(); 92 | 93 | startDate = new int[]{1900, 1}; 94 | endDate = new int[]{2049, 12}; 95 | mAttrsBean.setStartDate(startDate); 96 | mAttrsBean.setEndDate(endDate); 97 | } 98 | 99 | public void init() { 100 | //根据设定的日期范围计算日历的页数 101 | count = (endDate[0] - startDate[0]) * 12 + endDate[1] - startDate[1] + 1; 102 | calendarPagerAdapter = new CalendarPagerAdapter(count); 103 | calendarPagerAdapter.setAttrsBean(mAttrsBean); 104 | calendarPagerAdapter.setOnCalendarViewAdapter(item_layout, calendarViewAdapter); 105 | setAdapter(calendarPagerAdapter); 106 | 107 | currentPosition = CalendarUtil.dateToPosition(initDate[0], initDate[1], startDate[0], startDate[1]); 108 | 109 | //单选 110 | if (mAttrsBean.getChooseType() == 0) { 111 | int[] singleDate = mAttrsBean.getSingleDate(); 112 | if (singleDate != null) { 113 | lastClickDate[0] = CalendarUtil.dateToPosition(singleDate[0], singleDate[1], startDate[0], startDate[1]); 114 | lastClickDate[1] = singleDate[2]; 115 | } 116 | } 117 | 118 | //多选 119 | if (mAttrsBean.getChooseType() == 1) { 120 | positions = new HashSet<>(); 121 | chooseDate = new SparseArray<>(); 122 | if (mAttrsBean.getMultiDates() != null) { 123 | for (int[] date : mAttrsBean.getMultiDates()) { 124 | if (isIllegal(date)) { 125 | int datePosition = CalendarUtil.dateToPosition(date[0], date[1], startDate[0], startDate[1]); 126 | positions.add(datePosition); 127 | setChooseDate(date[2], true, datePosition); 128 | } 129 | } 130 | } 131 | } 132 | 133 | setCurrentItem(currentPosition, false); 134 | 135 | addOnPageChangeListener(new SimpleOnPageChangeListener() { 136 | @Override 137 | public void onPageSelected(int position) { 138 | refreshMonthView(position); 139 | currentPosition = position; 140 | if (pagerChangeListener != null) { 141 | int[] date = CalendarUtil.positionToDate(position, startDate[0], startDate[1]); 142 | pagerChangeListener.onPagerChanged(new int[]{date[0], date[1], lastClickDate[1]}); 143 | } 144 | } 145 | }); 146 | } 147 | 148 | /** 149 | * 计算 ViewPager 高度 150 | * 151 | * @param widthMeasureSpec 152 | * @param heightMeasureSpec 153 | */ 154 | @Override 155 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 156 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 157 | 158 | int calendarHeight; 159 | if (getAdapter() != null) { 160 | MonthView view = (MonthView) getChildAt(0); 161 | if (view != null) { 162 | calendarHeight = view.getMeasuredHeight(); 163 | setMeasuredDimension(widthMeasureSpec, MeasureSpec.makeMeasureSpec(calendarHeight, MeasureSpec.EXACTLY)); 164 | } 165 | } 166 | } 167 | 168 | /** 169 | * 刷新MonthView 170 | * 171 | * @param position 172 | */ 173 | private void refreshMonthView(int position) { 174 | MonthView monthView = calendarPagerAdapter.getViews().get(position); 175 | if (mAttrsBean.getChooseType() == 1) {//多选 176 | if (chooseDate.get(position) != null) 177 | monthView.multiChooseRefresh(chooseDate.get(position)); 178 | } else { 179 | //单选时,如果设置切换月份不选中上次选中的日期但如果切换回有选中日期的页则需要刷新选中,或者切换选中开启则需要刷新选中 180 | boolean flag = (!mAttrsBean.isSwitchChoose() && lastClickDate[0] == position) 181 | || mAttrsBean.isSwitchChoose(); 182 | monthView.refresh(lastClickDate[1], flag); 183 | } 184 | } 185 | 186 | /** 187 | * 设置单选时选中的日期 188 | * 189 | * @param day 190 | */ 191 | public void setLastClickDay(int day) { 192 | lastClickDate[0] = currentPosition; 193 | lastClickDate[1] = day; 194 | } 195 | 196 | /** 197 | * 设置多选时选中的日期 198 | * 199 | * @param day 200 | * @param flag 多选时flag=true代表选中数据,flag=false代表取消选中 201 | * @param position 代表记录viewpager哪一页的数据 202 | */ 203 | public void setChooseDate(int day, boolean flag, int position) { 204 | if (position == -1) { 205 | position = currentPosition; 206 | } 207 | HashSet days = chooseDate.get(position); 208 | if (flag) { 209 | if (days == null) { 210 | days = new HashSet<>(); 211 | chooseDate.put(position, days); 212 | } 213 | days.add(day); 214 | positions.add(position); 215 | } else { 216 | days.remove(day); 217 | } 218 | } 219 | 220 | /** 221 | * 检查初始化选中的日期,或者要跳转的日期是否合法 222 | * 223 | * @param destDate 224 | * @return 225 | */ 226 | private boolean isIllegal(int[] destDate) { 227 | 228 | if (destDate[1] > 12 || destDate[1] < 1) { 229 | return false; 230 | } 231 | 232 | if (CalendarUtil.dateToMillis(destDate) < CalendarUtil.dateToMillis(startDate)) { 233 | return false; 234 | } 235 | 236 | if (CalendarUtil.dateToMillis(destDate) > CalendarUtil.dateToMillis(endDate)) { 237 | return false; 238 | } 239 | 240 | if (destDate[2] > SolarUtil.getMonthDays(destDate[0], destDate[1]) || destDate[2] < 1) { 241 | return false; 242 | } 243 | 244 | 245 | if (mAttrsBean.getDisableStartDate() != null) { 246 | if (CalendarUtil.dateToMillis(destDate) < CalendarUtil.dateToMillis(mAttrsBean.getDisableStartDate())) { 247 | return false; 248 | } 249 | } 250 | 251 | if (mAttrsBean.getDisableEndDate() != null) { 252 | if (CalendarUtil.dateToMillis(destDate) > CalendarUtil.dateToMillis(mAttrsBean.getDisableEndDate())) { 253 | return false; 254 | } 255 | } 256 | 257 | return true; 258 | } 259 | 260 | /** 261 | * 设置日期单选回调 262 | * 263 | * @param singleChooseListener 264 | */ 265 | public void setOnSingleChooseListener(OnSingleChooseListener singleChooseListener) { 266 | this.singleChooseListener = singleChooseListener; 267 | } 268 | 269 | public OnMultiChooseListener getMultiChooseListener() { 270 | return multiChooseListener; 271 | } 272 | 273 | /** 274 | * 设置日期多选回调 275 | * 276 | * @param multiChooseListener 277 | */ 278 | public void setOnMultiChooseListener(OnMultiChooseListener multiChooseListener) { 279 | this.multiChooseListener = multiChooseListener; 280 | } 281 | 282 | public OnSingleChooseListener getSingleChooseListener() { 283 | return singleChooseListener; 284 | } 285 | 286 | /** 287 | * 设置月份切换回调 288 | * 289 | * @param pagerChangeListener 290 | */ 291 | public void setOnPagerChangeListener(OnPagerChangeListener pagerChangeListener) { 292 | this.pagerChangeListener = pagerChangeListener; 293 | } 294 | 295 | /** 296 | * 设置自定义日期样式 297 | * 298 | * @param item_layout 自定义的日期item布局 299 | * @param calendarViewAdapter 解析item的接口 300 | */ 301 | public CalendarView setOnCalendarViewAdapter(int item_layout, CalendarViewAdapter calendarViewAdapter) { 302 | this.item_layout = item_layout; 303 | this.calendarViewAdapter = calendarViewAdapter; 304 | return this; 305 | } 306 | 307 | /** 308 | * 单选时跳转到今天 309 | */ 310 | public void today() { 311 | int destPosition = CalendarUtil.dateToPosition(CalendarUtil.getCurrentDate()[0], CalendarUtil.getCurrentDate()[1], startDate[0], startDate[1]); 312 | lastClickDate[0] = destPosition; 313 | lastClickDate[1] = CalendarUtil.getCurrentDate()[2]; 314 | if (destPosition == currentPosition) { 315 | refreshMonthView(destPosition); 316 | } else { 317 | setCurrentItem(destPosition, false); 318 | } 319 | } 320 | 321 | /** 322 | * 单选时跳转到指定日期 323 | * 324 | * @param year 325 | * @param month 326 | * @param day 327 | */ 328 | public boolean toSpecifyDate(int year, int month, int day) { 329 | if (!isIllegal(new int[]{year, month, day})) { 330 | return false; 331 | } 332 | toDestDate(year, month, day); 333 | return true; 334 | } 335 | 336 | private void toDestDate(int year, int month, int day) { 337 | int destPosition = CalendarUtil.dateToPosition(year, month, startDate[0], startDate[1]); 338 | if (!mAttrsBean.isSwitchChoose() && day != 0) { 339 | lastClickDate[0] = destPosition; 340 | } 341 | lastClickDate[1] = day != 0 ? day : lastClickDate[1]; 342 | if (destPosition == currentPosition) { 343 | //在当月进行日期跳转 344 | refreshMonthView(destPosition); 345 | } else { 346 | setCurrentItem(destPosition, false); 347 | } 348 | } 349 | 350 | 351 | /** 352 | * 跳转到下个月 353 | */ 354 | public void nextMonth() { 355 | if (currentPosition < count - 1) 356 | setCurrentItem(++currentPosition, false); 357 | } 358 | 359 | /** 360 | * 跳转到上个月 361 | */ 362 | public void lastMonth() { 363 | if (currentPosition > 0) 364 | setCurrentItem(--currentPosition, false); 365 | } 366 | 367 | /** 368 | * 跳转到上一年的当前月 369 | */ 370 | public void lastYear() { 371 | if (currentPosition - 12 >= 0) { 372 | setCurrentItem(currentPosition -= 12, false); 373 | } 374 | } 375 | 376 | /** 377 | * 跳转到下一年的当前月 378 | */ 379 | public void nextYear() { 380 | if (currentPosition + 12 <= count) { 381 | setCurrentItem(currentPosition += 12, false); 382 | } 383 | } 384 | 385 | /** 386 | * 跳转到日历的开始年月 387 | */ 388 | public void toStart() { 389 | toDestDate(startDate[0], startDate[1], 0); 390 | } 391 | 392 | /** 393 | * 跳转到日历的结束年月 394 | */ 395 | public void toEnd() { 396 | toDestDate(endDate[0], endDate[1], 0); 397 | } 398 | 399 | /** 400 | * 将指定日期的农历替换成对应文字 401 | */ 402 | public CalendarView setSpecifyMap(HashMap map) { 403 | mAttrsBean.setSpecifyMap(map); 404 | return this; 405 | } 406 | 407 | /** 408 | * 设置日历初始显示的年月 409 | * 410 | * @param date 411 | * @return 412 | */ 413 | public CalendarView setInitDate(String date) { 414 | initDate = CalendarUtil.strToArray(date); 415 | return this; 416 | } 417 | 418 | /** 419 | * 设置日历的开始年月、结束年月 420 | * 421 | * @param startDate 422 | * @param endDate 423 | * @return 424 | */ 425 | public CalendarView setStartEndDate(String startDate, String endDate) { 426 | this.startDate = CalendarUtil.strToArray(startDate); 427 | if (startDate == null) { 428 | this.startDate = new int[]{1900, 1}; 429 | } 430 | this.endDate = CalendarUtil.strToArray(endDate); 431 | if (endDate == null) { 432 | this.endDate = new int[]{2049, 12}; 433 | } 434 | mAttrsBean.setStartDate(this.startDate); 435 | mAttrsBean.setEndDate(this.endDate); 436 | return this; 437 | } 438 | 439 | /** 440 | * 设置多选时默认选中的日期集合 441 | * 442 | * @param dates 443 | * @return 444 | */ 445 | public CalendarView setMultiDate(List dates) { 446 | List multiDates = new ArrayList<>(); 447 | for (String date : dates) { 448 | int[] d = CalendarUtil.strToArray(date); 449 | if (isIllegal(d)) { 450 | multiDates.add(d); 451 | } 452 | } 453 | mAttrsBean.setMultiDates(multiDates); 454 | return this; 455 | } 456 | 457 | 458 | /** 459 | * 设置单选时默认选中的日期 460 | * 461 | * @param date 462 | * @return 463 | */ 464 | public CalendarView setSingleDate(String date) { 465 | int[] singleDate = CalendarUtil.strToArray(date); 466 | if (!isIllegal(singleDate)) { 467 | singleDate = null; 468 | } 469 | mAttrsBean.setSingleDate(singleDate); 470 | return this; 471 | } 472 | 473 | /** 474 | * 设置日历禁用范围 475 | * 476 | * @param startDate 禁用startDate之前的日期 477 | * @param endDate 禁用endDate之后的日期 478 | * @return 479 | */ 480 | public CalendarView setDisableStartEndDate(String startDate, String endDate) { 481 | mAttrsBean.setDisableStartDate(CalendarUtil.strToArray(startDate)); 482 | mAttrsBean.setDisableEndDate(CalendarUtil.strToArray(endDate)); 483 | return this; 484 | } 485 | 486 | /** 487 | * 得到单选时当前选中的日期 488 | * 489 | * @return 490 | */ 491 | public DateBean getSingleDate() { 492 | int[] date = CalendarUtil.positionToDate(lastClickDate[0], startDate[0], startDate[1]); 493 | return CalendarUtil.getDateBean(date[0], date[1], lastClickDate[1]); 494 | } 495 | 496 | /** 497 | * 得到多选时选中的日期 498 | * 499 | * @return 500 | */ 501 | public List getMultiDate() { 502 | List list = new ArrayList<>(); 503 | for (Integer position : positions) { 504 | HashSet days = chooseDate.get(position); 505 | if (days.size() > 0) { 506 | int[] date = CalendarUtil.positionToDate(position, startDate[0], startDate[1]); 507 | for (Integer day : days) { 508 | list.add(CalendarUtil.getDateBean(date[0], date[1], day)); 509 | } 510 | } 511 | } 512 | return list; 513 | } 514 | } 515 | -------------------------------------------------------------------------------- /calendarview/src/main/java/com/othershe/calendarview/weiget/MonthView.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview.weiget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.text.TextUtils; 6 | import android.util.AttributeSet; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.TextView; 11 | 12 | import com.othershe.calendarview.R; 13 | import com.othershe.calendarview.bean.AttrsBean; 14 | import com.othershe.calendarview.bean.DateBean; 15 | import com.othershe.calendarview.listener.CalendarViewAdapter; 16 | import com.othershe.calendarview.listener.OnMultiChooseListener; 17 | import com.othershe.calendarview.listener.OnSingleChooseListener; 18 | import com.othershe.calendarview.utils.CalendarUtil; 19 | 20 | import java.util.HashSet; 21 | import java.util.List; 22 | import java.util.Set; 23 | 24 | public class MonthView extends ViewGroup { 25 | 26 | private static final int ROW = 6; 27 | private static final int COLUMN = 7; 28 | 29 | private static final int COLOR_RESET = 0;//重置文字颜色 30 | private static final int COLOR_SET = 1;//设置文字颜色 31 | 32 | private Context mContext; 33 | 34 | private View lastClickedView;//记录上次点击的Item 35 | private int currentMonthDays;//记录当月天数 36 | private int lastMonthDays;//记录当月显示的上个月天数 37 | private int nextMonthDays;//记录当月显示的下个月天数 38 | 39 | private int item_layout; 40 | private CalendarViewAdapter calendarViewAdapter; 41 | private Set chooseDays = new HashSet<>();//记录多选时当前页选中的日期 42 | private AttrsBean mAttrsBean; 43 | 44 | public MonthView(Context context) { 45 | this(context, null); 46 | } 47 | 48 | public MonthView(Context context, AttributeSet attrs) { 49 | super(context, attrs, 0); 50 | 51 | mContext = context; 52 | setBackgroundColor(Color.WHITE); 53 | } 54 | 55 | /** 56 | * @param dates 需要展示的日期数据 57 | * @param currentMonthDays 当月天数 58 | */ 59 | public void setDateList(List dates, int currentMonthDays) { 60 | if (getChildCount() > 0) { 61 | removeAllViews(); 62 | } 63 | lastMonthDays = 0; 64 | nextMonthDays = 0; 65 | boolean findSingleDate = false;//是否找到单选时默认选中的日期 66 | chooseDays.clear(); 67 | 68 | this.currentMonthDays = currentMonthDays; 69 | for (int i = 0; i < dates.size(); i++) { 70 | final DateBean date = dates.get(i); 71 | 72 | if (date.getType() == 0) { 73 | lastMonthDays++; 74 | if (!mAttrsBean.isShowLastNext()) { 75 | addView(new View(mContext), i); 76 | continue; 77 | } 78 | } 79 | 80 | if (date.getType() == 2) { 81 | nextMonthDays++; 82 | if (!mAttrsBean.isShowLastNext()) { 83 | addView(new View(mContext), i); 84 | continue; 85 | } 86 | } 87 | 88 | View view; 89 | TextView solarDay;//阳历TextView 90 | TextView lunarDay;//阴历TextView(节假日、节气同样使用阴历TextView来显示) 91 | if (item_layout != 0 && calendarViewAdapter != null) { 92 | view = LayoutInflater.from(mContext).inflate(item_layout, null); 93 | TextView[] views = calendarViewAdapter.convertView(view, date); 94 | solarDay = views[0]; 95 | lunarDay = views[1]; 96 | } else { 97 | view = LayoutInflater.from(mContext).inflate(R.layout.item_month_layout, null); 98 | solarDay = (TextView) view.findViewById(R.id.solar_day); 99 | lunarDay = (TextView) view.findViewById(R.id.lunar_day); 100 | } 101 | 102 | solarDay.setTextColor(mAttrsBean.getColorSolar()); 103 | solarDay.setTextSize(mAttrsBean.getSizeSolar()); 104 | lunarDay.setTextColor(mAttrsBean.getColorLunar()); 105 | lunarDay.setTextSize(mAttrsBean.getSizeLunar()); 106 | 107 | //设置上个月和下个月的阳历颜色 108 | if (date.getType() == 0 || date.getType() == 2) { 109 | solarDay.setTextColor(mAttrsBean.getColorLunar()); 110 | } 111 | solarDay.setText(String.valueOf(date.getSolar()[2])); 112 | 113 | //设置农历(节假日显示) 114 | if (mAttrsBean.isShowLunar()) { 115 | if ("初一".equals(date.getLunar()[1])) { 116 | lunarDay.setText(date.getLunar()[0]); 117 | if ("正月".equals(date.getLunar()[0]) && mAttrsBean.isShowHoliday()) { 118 | lunarDay.setTextColor(mAttrsBean.getColorHoliday()); 119 | lunarDay.setText("春节"); 120 | } 121 | } else { 122 | if (!TextUtils.isEmpty(date.getSolarHoliday()) && mAttrsBean.isShowHoliday()) {//阳历节日 123 | setLunarText(date.getSolarHoliday(), lunarDay, date.getType()); 124 | } else if (!TextUtils.isEmpty(date.getLunarHoliday()) && mAttrsBean.isShowHoliday()) {//农历节日 125 | setLunarText(date.getLunarHoliday(), lunarDay, date.getType()); 126 | } else if (!TextUtils.isEmpty(date.getTerm()) && mAttrsBean.isShowTerm()) {//节气 127 | setLunarText(date.getTerm(), lunarDay, date.getType()); 128 | } else { 129 | if (TextUtils.isEmpty(date.getLunar()[1])) { 130 | lunarDay.setVisibility(GONE); 131 | } else { 132 | lunarDay.setText(date.getLunar()[1]);//农历日期 133 | } 134 | } 135 | } 136 | 137 | } else { 138 | lunarDay.setVisibility(GONE); 139 | } 140 | 141 | //找到单选时默认选中的日期,并选中(如果有) 142 | if (mAttrsBean.getChooseType() == 0 143 | && mAttrsBean.getSingleDate() != null 144 | && !findSingleDate 145 | && date.getType() == 1 146 | && mAttrsBean.getSingleDate()[0] == date.getSolar()[0] 147 | && mAttrsBean.getSingleDate()[1] == date.getSolar()[1] 148 | && mAttrsBean.getSingleDate()[2] == date.getSolar()[2]) { 149 | lastClickedView = view; 150 | setDayColor(view, COLOR_SET); 151 | findSingleDate = true; 152 | } 153 | 154 | //找到多选时默认选中的多个日期,并选中(如果有) 155 | if (mAttrsBean.getChooseType() == 1 && mAttrsBean.getMultiDates() != null) { 156 | for (int[] d : mAttrsBean.getMultiDates()) { 157 | if (date.getType() == 1 158 | && d[0] == date.getSolar()[0] 159 | && d[1] == date.getSolar()[1] 160 | && d[2] == date.getSolar()[2]) { 161 | setDayColor(view, COLOR_SET); 162 | chooseDays.add(d[2]); 163 | break; 164 | } 165 | } 166 | } 167 | 168 | //设置禁用日期 169 | if (date.getType() == 1) { 170 | view.setTag(date.getSolar()[2]); 171 | if (mAttrsBean.getDisableStartDate() != null 172 | && (CalendarUtil.dateToMillis(mAttrsBean.getDisableStartDate()) > CalendarUtil.dateToMillis(date.getSolar()))) { 173 | solarDay.setTextColor(mAttrsBean.getColorLunar()); 174 | lunarDay.setTextColor(mAttrsBean.getColorLunar()); 175 | view.setTag(-1); 176 | addView(view, i); 177 | continue; 178 | } 179 | 180 | if (mAttrsBean.getDisableEndDate() != null 181 | && (CalendarUtil.dateToMillis(mAttrsBean.getDisableEndDate()) < CalendarUtil.dateToMillis(date.getSolar()))) { 182 | solarDay.setTextColor(mAttrsBean.getColorLunar()); 183 | lunarDay.setTextColor(mAttrsBean.getColorLunar()); 184 | view.setTag(-1); 185 | addView(view, i); 186 | continue; 187 | } 188 | } 189 | 190 | view.setOnClickListener(new OnClickListener() { 191 | @Override 192 | public void onClick(View v) { 193 | int day = date.getSolar()[2]; 194 | CalendarView calendarView = (CalendarView) getParent(); 195 | OnSingleChooseListener clickListener = calendarView.getSingleChooseListener(); 196 | OnMultiChooseListener chooseListener = calendarView.getMultiChooseListener(); 197 | if (date.getType() == 1) {//点击当月 198 | if (mAttrsBean.getChooseType() == 1 && chooseListener != null) {//多选的情况 199 | boolean flag; 200 | if (chooseDays.contains(day)) { 201 | setDayColor(v, COLOR_RESET); 202 | chooseDays.remove(day); 203 | flag = false; 204 | } else { 205 | setDayColor(v, COLOR_SET); 206 | chooseDays.add(day); 207 | flag = true; 208 | } 209 | calendarView.setChooseDate(day, flag, -1); 210 | chooseListener.onMultiChoose(v, date, flag); 211 | } else { 212 | calendarView.setLastClickDay(day); 213 | if (lastClickedView != null) { 214 | setDayColor(lastClickedView, COLOR_RESET); 215 | } 216 | setDayColor(v, COLOR_SET); 217 | lastClickedView = v; 218 | 219 | if (clickListener != null) { 220 | clickListener.onSingleChoose(v, date); 221 | } 222 | } 223 | } else if (date.getType() == 0) {//点击上月 224 | if (mAttrsBean.isSwitchChoose()) { 225 | calendarView.setLastClickDay(day); 226 | } 227 | calendarView.lastMonth(); 228 | if (clickListener != null) { 229 | clickListener.onSingleChoose(v, date); 230 | } 231 | } else if (date.getType() == 2) {//点击下月 232 | if (mAttrsBean.isSwitchChoose()) { 233 | calendarView.setLastClickDay(day); 234 | } 235 | calendarView.nextMonth(); 236 | if (clickListener != null) { 237 | clickListener.onSingleChoose(v, date); 238 | } 239 | } 240 | } 241 | }); 242 | addView(view, i); 243 | } 244 | requestLayout(); 245 | } 246 | 247 | private void setLunarText(String str, TextView text, int type) { 248 | text.setText(str); 249 | if (type == 1) { 250 | text.setTextColor(mAttrsBean.getColorHoliday()); 251 | } 252 | text.setTag("holiday"); 253 | } 254 | 255 | private void setDayColor(View v, int type) { 256 | TextView solarDay = (TextView) v.findViewById(R.id.solar_day); 257 | TextView lunarDay = (TextView) v.findViewById(R.id.lunar_day); 258 | solarDay.setTextSize(mAttrsBean.getSizeSolar()); 259 | lunarDay.setTextSize(mAttrsBean.getSizeLunar()); 260 | 261 | if (type == 0) { 262 | v.setBackgroundResource(0); 263 | solarDay.setTextColor(mAttrsBean.getColorSolar()); 264 | if ("holiday".equals(lunarDay.getTag())) { 265 | lunarDay.setTextColor(mAttrsBean.getColorHoliday()); 266 | } else { 267 | lunarDay.setTextColor(mAttrsBean.getColorLunar()); 268 | } 269 | } else if (type == 1) { 270 | v.setBackgroundResource(mAttrsBean.getDayBg()); 271 | solarDay.setTextColor(mAttrsBean.getColorChoose()); 272 | lunarDay.setTextColor(mAttrsBean.getColorChoose()); 273 | } 274 | } 275 | 276 | @Override 277 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 278 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 279 | 280 | int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); 281 | int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); 282 | 283 | int itemWidth = widthSpecSize / COLUMN; 284 | 285 | //计算日历的最大高度 286 | if (heightSpecSize > itemWidth * ROW) { 287 | heightSpecSize = itemWidth * ROW; 288 | } 289 | 290 | setMeasuredDimension(widthSpecSize, heightSpecSize); 291 | 292 | int itemHeight = heightSpecSize / ROW; 293 | 294 | int itemSize = Math.min(itemWidth, itemHeight); 295 | for (int i = 0; i < getChildCount(); i++) { 296 | View childView = getChildAt(i); 297 | childView.measure(MeasureSpec.makeMeasureSpec(itemSize, MeasureSpec.EXACTLY), 298 | MeasureSpec.makeMeasureSpec(itemSize, MeasureSpec.EXACTLY)); 299 | } 300 | } 301 | 302 | @Override 303 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 304 | if (getChildCount() == 0) { 305 | return; 306 | } 307 | 308 | View childView = getChildAt(0); 309 | int itemWidth = childView.getMeasuredWidth(); 310 | int itemHeight = childView.getMeasuredHeight(); 311 | //计算列间距 312 | int dx = (getMeasuredWidth() - itemWidth * COLUMN) / (COLUMN * 2); 313 | 314 | //当显示五行时扩大行间距 315 | int dy = 0; 316 | if (getChildCount() == 35) { 317 | dy = itemHeight / 5; 318 | } 319 | 320 | for (int i = 0; i < getChildCount(); i++) { 321 | View view = getChildAt(i); 322 | 323 | int left = i % COLUMN * itemWidth + ((2 * (i % COLUMN) + 1)) * dx; 324 | int top = i / COLUMN * (itemHeight + dy); 325 | int right = left + itemWidth; 326 | int bottom = top + itemHeight; 327 | view.layout(left, top, right, bottom); 328 | } 329 | } 330 | 331 | public void refresh(int day, boolean flag) { 332 | if (lastClickedView != null) { 333 | setDayColor(lastClickedView, COLOR_RESET); 334 | } 335 | if (!flag) { 336 | return; 337 | } 338 | View destView = findDestView(day); 339 | if (destView == null) { 340 | return; 341 | } 342 | setDayColor(destView, COLOR_SET); 343 | lastClickedView = destView; 344 | invalidate(); 345 | } 346 | 347 | /** 348 | * 多选时刷新日期,以恢复之前选中的日期 349 | * 350 | * @param set 351 | */ 352 | public void multiChooseRefresh(HashSet set) { 353 | for (Integer day : set) { 354 | setDayColor(findDestView(day), COLOR_SET); 355 | chooseDays.add(day); 356 | } 357 | invalidate(); 358 | } 359 | 360 | /** 361 | * 查找要跳转到的页面需要展示的日期View 362 | * 363 | * @param day 364 | * @return 365 | */ 366 | private View findDestView(int day) { 367 | View view = null; 368 | for (int i = lastMonthDays; i < getChildCount() - nextMonthDays; i++) { 369 | if ((Integer) getChildAt(i).getTag() == day) { 370 | view = getChildAt(i); 371 | break; 372 | } 373 | } 374 | 375 | if (view == null) { 376 | view = getChildAt(currentMonthDays + lastMonthDays - 1); 377 | } 378 | 379 | if ((Integer) view.getTag() == -1) { 380 | view = null; 381 | } 382 | return view; 383 | } 384 | 385 | public void setAttrsBean(AttrsBean attrsBean) { 386 | mAttrsBean = attrsBean; 387 | } 388 | 389 | public void setOnCalendarViewAdapter(int item_layout, CalendarViewAdapter calendarViewAdapter) { 390 | this.item_layout = item_layout; 391 | this.calendarViewAdapter = calendarViewAdapter; 392 | } 393 | } 394 | -------------------------------------------------------------------------------- /calendarview/src/main/java/com/othershe/calendarview/weiget/WeekView.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview.weiget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.Paint; 8 | import android.text.TextUtils; 9 | import android.util.AttributeSet; 10 | import android.view.View; 11 | 12 | import com.othershe.calendarview.R; 13 | import com.othershe.calendarview.utils.CalendarUtil; 14 | 15 | public class WeekView extends View { 16 | 17 | private String[] weekArray = {"日", "一", "二", "三", "四", "五", "六"}; 18 | private int weekSize = 12;//文字尺寸 19 | private int weekColor = Color.BLACK;//文字颜色 20 | 21 | private Paint mPaint; 22 | private Context context; 23 | 24 | public WeekView(Context context) { 25 | this(context, null); 26 | } 27 | 28 | public WeekView(Context context, AttributeSet attrs) { 29 | this(context, attrs, 0); 30 | } 31 | 32 | public WeekView(Context context, AttributeSet attrs, int defStyleAttr) { 33 | super(context, attrs, defStyleAttr); 34 | this.context = context; 35 | initAttrs(attrs); 36 | initPaint(); 37 | setBackgroundColor(Color.WHITE); 38 | } 39 | 40 | private void initAttrs(AttributeSet attrs) { 41 | String weekStr = null; 42 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.WeekView); 43 | for (int i = 0; i < ta.getIndexCount(); i++) { 44 | int attr = ta.getIndex(i); 45 | if (attr == R.styleable.WeekView_week_color) { 46 | weekColor = ta.getColor(R.styleable.WeekView_week_color, weekColor); 47 | } else if (attr == R.styleable.WeekView_week_size) { 48 | weekSize = ta.getInteger(R.styleable.WeekView_week_size, weekSize); 49 | } else if (attr == R.styleable.WeekView_week_str) { 50 | weekStr = ta.getString(R.styleable.WeekView_week_str); 51 | } 52 | } 53 | ta.recycle(); 54 | 55 | if (!TextUtils.isEmpty(weekStr)) { 56 | String[] weeks = weekStr.split("\\."); 57 | if (weeks.length != 7) { 58 | return; 59 | } 60 | System.arraycopy(weeks, 0, weekArray, 0, 7); 61 | } 62 | 63 | weekSize = CalendarUtil.getTextSize1(context, weekSize); 64 | } 65 | 66 | 67 | private void initPaint() { 68 | mPaint = new Paint(); 69 | mPaint.setColor(weekColor); 70 | mPaint.setAntiAlias(true); 71 | mPaint.setTextSize(weekSize); 72 | } 73 | 74 | @Override 75 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 76 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 77 | 78 | int widthSize = MeasureSpec.getSize(widthMeasureSpec); 79 | int widthMode = MeasureSpec.getMode(widthMeasureSpec); 80 | int heightSize = MeasureSpec.getSize(heightMeasureSpec); 81 | int heightMode = MeasureSpec.getMode(heightMeasureSpec); 82 | if (heightMode == MeasureSpec.AT_MOST) { 83 | heightSize = CalendarUtil.getPxSize(context, 35); 84 | } 85 | if (widthMode == MeasureSpec.AT_MOST) { 86 | widthSize = CalendarUtil.getPxSize(context, 300); 87 | } 88 | setMeasuredDimension(widthSize, heightSize); 89 | } 90 | 91 | @Override 92 | protected void onDraw(Canvas canvas) { 93 | super.onDraw(canvas); 94 | int width = getWidth(); 95 | int height = getHeight(); 96 | int itemWidth = width / 7; 97 | 98 | for (int i = 0; i < weekArray.length; i++) { 99 | String text = weekArray[i]; 100 | int textWidth = (int) mPaint.measureText(text); 101 | int startX = itemWidth * i + (itemWidth - textWidth) / 2; 102 | int startY = (int) (height / 2 - (mPaint.ascent() + mPaint.descent()) / 2); 103 | canvas.drawText(text, startX, startY, mPaint); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /calendarview/src/main/res/drawable/blue_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shehuan/CalendarView/ff07156464db5eea70c82ef11b1598951fd0eb2d/calendarview/src/main/res/drawable/blue_circle.png -------------------------------------------------------------------------------- /calendarview/src/main/res/layout/item_month_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /calendarview/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /calendarview/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | CalendarView 3 | 4 | -------------------------------------------------------------------------------- /calendarview/src/test/java/com/othershe/calendarview/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.othershe.calendarview; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shehuan/CalendarView/ff07156464db5eea70c82ef11b1598951fd0eb2d/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Oct 30 10:06:38 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /screenshot/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shehuan/CalendarView/ff07156464db5eea70c82ef11b1598951fd0eb2d/screenshot/1.gif -------------------------------------------------------------------------------- /screenshot/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shehuan/CalendarView/ff07156464db5eea70c82ef11b1598951fd0eb2d/screenshot/2.gif -------------------------------------------------------------------------------- /screenshot/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shehuan/CalendarView/ff07156464db5eea70c82ef11b1598951fd0eb2d/screenshot/3.gif -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':calendarview' 2 | --------------------------------------------------------------------------------