├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── release
│ ├── app-release.apk
│ └── output.json
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── tuanhuo
│ │ └── android
│ │ └── myapplication
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── android
│ │ │ └── myapplication
│ │ │ └── MainActivity.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── tuanhuo
│ └── android
│ └── myapplication
│ └── ExampleUnitTest.java
├── build.gradle
├── calendarlibrary
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── tuanhuo
│ │ └── android
│ │ └── calendarlibrary
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── android
│ │ │ └── calendarlibrary
│ │ │ ├── CollapseCalendarView.java
│ │ │ ├── listener
│ │ │ ├── OnDateSelect.java
│ │ │ ├── OnMonthChangeListener.java
│ │ │ └── OnTitleClickListener.java
│ │ │ ├── manager
│ │ │ ├── CalendarManager.java
│ │ │ ├── CalendarUnit.java
│ │ │ ├── Day.java
│ │ │ ├── DefaultFormatter.java
│ │ │ ├── Formatter.java
│ │ │ ├── Month.java
│ │ │ ├── ProgressManager.java
│ │ │ ├── ProgressManagerImpl.java
│ │ │ ├── RangeUnit.java
│ │ │ ├── ResizeManager.java
│ │ │ └── Week.java
│ │ │ ├── models
│ │ │ ├── AbstractViewHolder.java
│ │ │ ├── SizeViewHolder.java
│ │ │ └── StubViewHolder.java
│ │ │ ├── utils
│ │ │ └── ChinaDate.java
│ │ │ └── widget
│ │ │ ├── DayView.java
│ │ │ └── WeekView.java
│ └── res
│ │ ├── color
│ │ ├── text_calendar.xml
│ │ ├── text_calendar_out_month.xml
│ │ ├── text_calendar_week.xml
│ │ └── text_calendar_week_out_month.xml
│ │ ├── drawable-xxhdpi
│ │ ├── ic_arrow_left_normal.png
│ │ └── ic_arrow_right_normal.png
│ │ ├── drawable
│ │ ├── bg_btn_calendar.xml
│ │ ├── bg_btn_calendar_today.xml
│ │ ├── bg_btn_calendar_today_selected.xml
│ │ ├── bg_calendar_point_blue.xml
│ │ ├── bg_calendar_point_white.xml
│ │ ├── bg_day_type_blue.xml
│ │ ├── bg_day_type_gray.xml
│ │ ├── bg_day_type_green.xml
│ │ ├── bg_day_type_red.xml
│ │ ├── ic_arrow_left.xml
│ │ └── ic_arrow_right.xml
│ │ ├── layout
│ │ ├── calendar_layout.xml
│ │ ├── day_layout.xml
│ │ └── week_layout.xml
│ │ └── values
│ │ ├── attrs.xml
│ │ ├── color.xml
│ │ └── strings.xml
│ └── test
│ └── java
│ └── com
│ └── tuanhuo
│ └── android
│ └── calendarlibrary
│ └── ExampleUnitTest.java
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── screenshot
├── BMmh.png
├── preview.gif
└── shot.jpg
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | /.idea
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 主要特性
2 |
3 | * 设置自定义日期角标
4 | * 农历切换
5 | * 左右滑动切换上下周月,上下滑动切换周月模式
6 | * 抽屉式周月切换效果
7 | * 标记指定日期(marker)
8 | * 跳转到指定日期
9 |
10 |
11 |

12 |
13 | 
14 |
15 | ## 使用方法
16 |
17 | #### 在layout中使用:
18 |
19 | ```xml
20 |
24 | ```
25 |
26 | #### 使用此方法初始化日历标记数据
27 |
28 | ```java
29 | mManager = new CalendarManager(LocalDate.now(),
30 | CalendarManager.State.MONTH,
31 | LocalDate.now().withYear(100),
32 | LocalDate.now().plusYears(100));
33 | calendarView.init(mManager);
34 | ```
35 |
36 | #### 使用方法设置日历数据
37 |
38 | 是否显示农历
39 | ```java
40 | calendarView.showChinaDay(true);
41 | ```
42 | 回到今天
43 | ```java
44 | calendarView.changeDate(LocalDate.now().toString());
45 | ```
46 | 周月切换
47 | ```java
48 | // TODO Auto-generated method stub
49 | mManager.toggleView();
50 | calendarView.populateLayout();
51 | ```
52 | 班排
53 | ```java
54 | calendarView.setArrayData(json);
55 | calendarView.populateLayout();
56 | ```
57 |
58 | #### 使用方法添加上相关监听
59 |
60 | 月份切换监听器
61 | ```java
62 | mManager.setMonthChangeListener(new OnMonthChangeListener() {
63 | @Override
64 | public void monthChange(String month, LocalDate mSelected) {
65 | // TODO Auto-generated method stub
66 | //Toast.makeText(MainActivity.this, month, Toast.LENGTH_SHORT).show();
67 | }
68 | });
69 | ```
70 | 日期选中监听器
71 | ```java
72 | calendarView.setDateSelectListener(new OnDateSelect() {
73 | @Override
74 | public void onDateSelected(LocalDate date) {
75 | // Toast.makeText(MainActivity.this, date.toString(), Toast.LENGTH_SHORT).show();
76 | }
77 | });
78 | ```
79 | ## APK
80 | 安装 [apk](https://www.pgyer.com/BMmh) 文件预览效果,或者通过下面二维码去下载安装:
81 |
82 | 
83 |
84 | ## 感谢
85 | [android-collapse-calendar-view](https://github.com/blazsolar/android-collapse-calendar-view)
86 |
87 | ## License
88 |
89 |
90 | Copyright 2018 chenyongci
91 |
92 | Licensed under the Apache License, Version 2.0 (the "License");
93 | you may not use this file except in compliance with the License.
94 | You may obtain a copy of the License at
95 |
96 | http://www.apache.org/licenses/LICENSE-2.0
97 |
98 | Unless required by applicable law or agreed to in writing, software
99 | distributed under the License is distributed on an "AS IS" BASIS,
100 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
101 | See the License for the specific language governing permissions and
102 | limitations under the License.
103 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion '25.0.2'
6 | defaultConfig {
7 | applicationId "com.tuanhuo.android.myapplication"
8 | minSdkVersion 15
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.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 | implementation fileTree(include: ['*.jar'], dir: 'libs')
24 | implementation 'com.android.support:appcompat-v7:25.1.1'
25 | implementation 'com.android.support.constraint:constraint-layout:1.0.2'
26 | testImplementation 'junit:junit:4.12'
27 | androidTestImplementation 'com.android.support.test:runner:1.0.1'
28 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
29 | implementation project(':calendarlibrary')
30 | }
31 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/release/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenyongci/Android-Week-Calendar/7d8d27c67237158dbd04e36117240f2d31497101/app/release/app-release.apk
--------------------------------------------------------------------------------
/app/release/output.json:
--------------------------------------------------------------------------------
1 | [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1},"path":"app-release.apk","properties":{"packageId":"com.tuanhuo.android.myapplication","split":"","minSdkVersion":"15"}}]
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/tuanhuo/android/myapplication/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.tuanhuo.android.myapplication;
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 | * Instrumented 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.tuanhuo.android.myapplication", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/myapplication/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.android.myapplication;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.view.View;
6 | import android.widget.CompoundButton;
7 | import android.widget.Switch;
8 |
9 | import com.android.calendarlibrary.CollapseCalendarView;
10 | import com.android.calendarlibrary.listener.OnDateSelect;
11 | import com.android.calendarlibrary.listener.OnMonthChangeListener;
12 | import com.android.calendarlibrary.listener.OnTitleClickListener;
13 | import com.android.calendarlibrary.manager.CalendarManager;
14 |
15 | import org.joda.time.LocalDate;
16 | import org.json.JSONArray;
17 | import org.json.JSONObject;
18 |
19 | import java.text.SimpleDateFormat;
20 | import java.util.Calendar;
21 |
22 | /**
23 | * @Author android
24 | * @date 2018/1/22
25 | * @return
26 | */
27 | public class MainActivity extends AppCompatActivity {
28 |
29 | private CollapseCalendarView calendarView;
30 | private Switch switchChinaDay;
31 |
32 | private CalendarManager mManager;
33 |
34 | private JSONObject json;
35 |
36 | private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
37 |
38 | @Override
39 | protected void onCreate(Bundle savedInstanceState) {
40 | super.onCreate(savedInstanceState);
41 | setContentView(R.layout.activity_main);
42 |
43 | calendarView = (CollapseCalendarView) findViewById(R.id.calendar);
44 | switchChinaDay = (Switch) findViewById(R.id.switch_china_day);
45 | initView();
46 | initListener();
47 | initData();
48 | }
49 |
50 |
51 | private void initView() {
52 | mManager = new CalendarManager(LocalDate.now(),
53 | CalendarManager.State.MONTH,
54 | LocalDate.now(),//默认这个月,如果LocalDate.now().minusYears(1));
55 | LocalDate.now().plusYears(1));//可以划之后一年
56 | calendarView.init(mManager);
57 |
58 | calendarView.showChinaDay(true);//显示农历
59 |
60 | switchChinaDay.setChecked(true);
61 | }
62 |
63 | private void initListener(){
64 | switchChinaDay.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
65 | @Override
66 | public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
67 | calendarView.showChinaDay(b);
68 | if(b){
69 | switchChinaDay.setText("显示农历");
70 | }else{
71 | switchChinaDay.setText("隐藏农历");
72 | }
73 | }
74 | });
75 |
76 | /**
77 | *月份切换监听器
78 | */
79 | mManager.setMonthChangeListener(new OnMonthChangeListener() {
80 |
81 | @Override
82 | public void monthChange(String month, LocalDate mSelected) {
83 | // TODO Auto-generated method stub
84 | //Toast.makeText(MainActivity.this, month, Toast.LENGTH_SHORT).show();
85 | }
86 |
87 | });
88 | /**
89 | * 日期选中监听器
90 | */
91 | calendarView.setDateSelectListener(new OnDateSelect() {
92 |
93 | @Override
94 | public void onDateSelected(LocalDate date) {
95 | // Toast.makeText(MainActivity.this, date.toString(), Toast.LENGTH_SHORT).show();
96 | }
97 | });
98 |
99 | calendarView.setTitleClickListener(new OnTitleClickListener() {
100 |
101 | @Override
102 | public void onTitleClick() {
103 | // Toast.makeText(MainActivity.this, "点击标题", Toast.LENGTH_SHORT).show();
104 | }
105 | });
106 | /**
107 | * 回到今天
108 | */
109 | findViewById(R.id.btn_today).setOnClickListener(new View.OnClickListener() {
110 |
111 | @Override
112 | public void onClick(View v) {
113 | // TODO Auto-generated method stub
114 | calendarView.changeDate(LocalDate.now().toString());
115 | }
116 | });
117 | /**
118 | * 周月切换
119 | */
120 | findViewById(R.id.btn_changemode).setOnClickListener(new View.OnClickListener() {
121 |
122 | @Override
123 | public void onClick(View v) {
124 | // TODO Auto-generated method stub
125 | mManager.toggleView();
126 | calendarView.populateLayout();
127 | }
128 | });
129 | }
130 |
131 |
132 | private void initData() {
133 | Calendar cal = Calendar.getInstance();
134 | // cal.set(Calendar.MONTH, 9);
135 | // cal.set(Calendar.DAY_OF_MONTH, 0);
136 | json = new JSONObject();
137 | try {
138 | for (int i = 0; i < 30; i++) {
139 | JSONObject jsonObject2 = new JSONObject();
140 | if (i <= 6) {
141 | jsonObject2.put("type", "休");
142 | } else if ( i > 6 && i< 11) {
143 | jsonObject2.put("type", "班");
144 | }
145 | if (i%3 == 0) {
146 | jsonObject2.put("list", new JSONArray());
147 | }
148 |
149 | json.put(sdf.format(cal.getTime()), jsonObject2);
150 |
151 | cal.add(Calendar.DAY_OF_MONTH, 1);
152 | }
153 | } catch (Exception e) {
154 | // TODO: handle exception
155 | e.printStackTrace();
156 | }
157 | //设置数据显示
158 | calendarView.setArrayData(json);
159 | //刷新日期
160 | calendarView.populateLayout();
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
22 |
23 |
32 |
33 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenyongci/Android-Week-Calendar/7d8d27c67237158dbd04e36117240f2d31497101/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenyongci/Android-Week-Calendar/7d8d27c67237158dbd04e36117240f2d31497101/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenyongci/Android-Week-Calendar/7d8d27c67237158dbd04e36117240f2d31497101/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenyongci/Android-Week-Calendar/7d8d27c67237158dbd04e36117240f2d31497101/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ff3f74
4 | #fc5c99
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 日历
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/tuanhuo/android/myapplication/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.tuanhuo.android.myapplication;
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 |
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.0.1'
11 |
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | }
20 | }
21 |
22 | task clean(type: Delete) {
23 | delete rootProject.buildDir
24 | }
25 |
--------------------------------------------------------------------------------
/calendarlibrary/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/calendarlibrary/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion '25.0.2'
6 |
7 |
8 | defaultConfig {
9 | minSdkVersion 15
10 | targetSdkVersion 25
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
15 |
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 |
25 | }
26 |
27 | dependencies {
28 | implementation fileTree(dir: 'libs', include: ['*.jar'])
29 |
30 | implementation 'com.android.support:appcompat-v7:25.1.1'
31 | compile 'joda-time:joda-time:2.9.9'
32 | }
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/calendarlibrary/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/calendarlibrary/src/androidTest/java/com/tuanhuo/android/calendarlibrary/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.tuanhuo.android.calendarlibrary;
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 | * Instrumented 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.tuanhuo.android.calendarlibrary.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/CollapseCalendarView.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.support.annotation.NonNull;
6 | import android.support.annotation.Nullable;
7 | import android.text.TextUtils;
8 | import android.util.AttributeSet;
9 | import android.util.Log;
10 | import android.view.LayoutInflater;
11 | import android.view.MotionEvent;
12 | import android.view.View;
13 | import android.view.animation.Animation;
14 | import android.view.animation.AnimationUtils;
15 | import android.widget.ImageButton;
16 | import android.widget.ImageView;
17 | import android.widget.LinearLayout;
18 | import android.widget.RelativeLayout;
19 | import android.widget.TextView;
20 |
21 | import com.android.calendarlibrary.listener.OnDateSelect;
22 | import com.android.calendarlibrary.listener.OnTitleClickListener;
23 | import com.android.calendarlibrary.manager.CalendarManager;
24 | import com.android.calendarlibrary.manager.Day;
25 | import com.android.calendarlibrary.manager.Formatter;
26 | import com.android.calendarlibrary.manager.Month;
27 | import com.android.calendarlibrary.manager.ResizeManager;
28 | import com.android.calendarlibrary.manager.Week;
29 | import com.android.calendarlibrary.widget.DayView;
30 | import com.android.calendarlibrary.widget.WeekView;
31 |
32 | import org.joda.time.DateTimeConstants;
33 | import org.joda.time.LocalDate;
34 | import org.json.JSONObject;
35 |
36 | import java.text.ParseException;
37 | import java.text.SimpleDateFormat;
38 | import java.util.LinkedList;
39 | import java.util.List;
40 | import java.util.Queue;
41 |
42 |
43 |
44 | public class CollapseCalendarView extends LinearLayout implements View.OnClickListener {
45 |
46 | public static final String TAG = "CalendarView";
47 |
48 | private CalendarManager mManager;
49 |
50 | private TextView mTitleView;
51 | private ImageButton mPrev;
52 | private ImageButton mNext;
53 | private LinearLayout mWeeksView;
54 |
55 | private final LayoutInflater mInflater;
56 | private final RecycleBin mRecycleBin = new RecycleBin();
57 |
58 | private OnDateSelect mListener;
59 | private TextView mSelectionText;
60 | private LinearLayout mHeader;
61 | private ResizeManager mResizeManager;
62 |
63 | private boolean initialized;
64 |
65 |
66 | private ImageView mIvPre;
67 | private ImageView mIvNext;
68 | private Animation left_in;
69 | private Animation right_in;
70 | private String[] weeks;
71 | private OnTitleClickListener titleClickListener;
72 |
73 | private JSONObject dataObject; //日历数据源
74 | private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
75 | private boolean showChinaDay = true; //是否显示农历日期
76 |
77 | public CollapseCalendarView(Context context) {
78 | this(context, null);
79 | //onFinishInflate();
80 | }
81 |
82 |
83 | public CollapseCalendarView(Context context, AttributeSet attrs) {
84 | this(context, attrs, R.attr.calendarViewStyle);
85 | }
86 |
87 |
88 |
89 | public CollapseCalendarView(Context context, AttributeSet attrs, int defStyle) {
90 | super(context, attrs, defStyle);
91 |
92 | mInflater = LayoutInflater.from(context);
93 | mResizeManager = new ResizeManager(this);
94 | inflate(context, R.layout.calendar_layout, this);
95 | setOrientation(VERTICAL);
96 |
97 |
98 | weeks = getResources().getStringArray(R.array.weeks);
99 | setBackgroundColor(getResources().getColor(R.color.cal_color_white));
100 | mIvPre = new ImageView(getContext());
101 | mIvNext = new ImageView(getContext());
102 | RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
103 | mIvPre.setLayoutParams(param);
104 | mIvNext.setLayoutParams(param);
105 | initAnim();
106 | }
107 |
108 |
109 | /**
110 | * 初始化日历管理器
111 | * @param manager 日历管理器对象
112 | */
113 | public void init(@NonNull CalendarManager manager) {
114 | if (manager != null) {
115 |
116 | mManager = manager;
117 |
118 | populateLayout();
119 |
120 | if (mListener != null) {
121 | mListener.onDateSelected(mManager.getSelectedDay());
122 | }
123 |
124 | }
125 | }
126 |
127 | public CalendarManager getManager() {
128 | return mManager;
129 | }
130 |
131 | @Override
132 | public void onClick(View v) {
133 | Log.d(TAG, "On click");
134 | if (mManager != null) {
135 | int id = v.getId();
136 | if (id == R.id.prev) {
137 | prev();
138 | } else if (id == R.id.next) {
139 | Log.d(TAG, "next");
140 | next();
141 | } else if (id == R.id.title) {
142 | if (titleClickListener != null) {
143 | titleClickListener.onTitleClick();
144 | }
145 | }
146 | }
147 | }
148 |
149 | @Override
150 | protected void dispatchDraw( Canvas canvas) {
151 | mResizeManager.onDraw();
152 | super.dispatchDraw(canvas);
153 | }
154 |
155 | public CalendarManager.State getState() {
156 | if (mManager != null) {
157 | return mManager.getState();
158 | } else {
159 | return null;
160 | }
161 | }
162 |
163 | public void setListener(@Nullable OnDateSelect listener) {
164 | mListener = listener;
165 | }
166 |
167 | /**
168 | * 设置日历标题
169 | * @param text
170 | */
171 | public void setTitle(String text) {
172 | if (TextUtils.isEmpty(text)) {
173 | mHeader.setVisibility(View.VISIBLE);
174 | mSelectionText.setVisibility(View.GONE);
175 | } else {
176 | mHeader.setVisibility(View.GONE);
177 | mSelectionText.setVisibility(View.VISIBLE);
178 | mSelectionText.setText(text);
179 | }
180 | }
181 |
182 | @Override
183 | public boolean onInterceptTouchEvent(MotionEvent ev) {
184 | return mResizeManager.onInterceptTouchEvent(ev);
185 | }
186 |
187 | @Override
188 | public boolean onTouchEvent( MotionEvent event) {
189 | super.onTouchEvent(event);
190 | return mResizeManager.onTouchEvent(event);
191 | }
192 |
193 | @Override
194 | protected void onFinishInflate() {
195 | super.onFinishInflate();
196 |
197 | mTitleView = (TextView) findViewById(R.id.title);
198 | mPrev = (ImageButton) findViewById(R.id.prev);
199 | mNext = (ImageButton) findViewById(R.id.next);
200 | mWeeksView = (LinearLayout) findViewById(R.id.weeks);
201 |
202 | mHeader = (LinearLayout) findViewById(R.id.header);
203 | mSelectionText = (TextView) findViewById(R.id.selection_title);
204 |
205 | mTitleView.setOnClickListener(this);
206 |
207 | mPrev.setOnClickListener(this);
208 | mNext.setOnClickListener(this);
209 |
210 | populateLayout();
211 | }
212 |
213 | /**
214 | * 绘制日历周标题
215 | */
216 | private void populateDays() {
217 |
218 | if (!initialized) {
219 | CalendarManager manager = getManager();
220 |
221 | if (manager != null) {
222 | Formatter formatter = manager.getFormatter();
223 |
224 | LinearLayout layout = (LinearLayout) findViewById(R.id.days);
225 |
226 | LocalDate date = LocalDate.now().withDayOfWeek(DateTimeConstants.MONDAY);
227 | for (int i = 0; i < 7; i++) {
228 | TextView textView = (TextView) layout.getChildAt(i);
229 |
230 | /*textView.setText(weeks[i]);
231 | if (i > 4) {
232 | textView.setTextColor(getResources().getColor(R.color.cal_blue_dark));
233 | }*/
234 | textView.setText(formatter.getDayName(date));
235 |
236 | date = date.plusDays(1);
237 | }
238 |
239 | initialized = true;
240 | }
241 | }
242 | }
243 |
244 |
245 | public void setArrayData(JSONObject jsonObject){
246 | this.dataObject = jsonObject;
247 | }
248 |
249 |
250 | public void showChinaDay(boolean showChinaDay){
251 | this.showChinaDay = showChinaDay;
252 | populateLayout();
253 | }
254 |
255 | /**
256 | * 设置标题点击监听器
257 | * @param titleClickListener
258 | */
259 | public void setTitleClickListener(OnTitleClickListener titleClickListener){
260 | this.titleClickListener = titleClickListener;
261 | }
262 |
263 | /**
264 | * 初始化左右滑动动画
265 | */
266 | private void initAnim(){
267 | left_in = AnimationUtils.makeInAnimation(getContext(), true);
268 | right_in = AnimationUtils.makeInAnimation(getContext(), false);
269 | }
270 |
271 |
272 | /**
273 | * 切换到指定某天界面
274 | * @param date yyyy-MM-dd
275 | */
276 | public void changeDate(String date){
277 | if (date.compareTo(mManager.getSelectedDay().toString()) > 0) {
278 | this.setAnimation(right_in);
279 | right_in.start();
280 | } else if (date.compareTo(mManager.getSelectedDay().toString()) < 0) {
281 | this.setAnimation(left_in);
282 | left_in.start();
283 | }
284 | try {
285 | mManager.init(LocalDate.fromDateFields(sdf.parse(date)));
286 | } catch (ParseException e) {
287 | // TODO Auto-generated catch block
288 | e.printStackTrace();
289 | }
290 | init(mManager);
291 | }
292 |
293 |
294 |
295 | public void setDateSelectListener(OnDateSelect listener) {
296 | mListener = listener;
297 | }
298 |
299 |
300 | /**
301 | * 显示日历自带标题
302 | */
303 | public void showHeader(){
304 | mHeader.setVisibility(View.VISIBLE);
305 | }
306 | /**
307 | * 隐藏日历自带标题
308 | */
309 | public void hideHeader(){
310 | mHeader.setVisibility(View.GONE);
311 | }
312 |
313 |
314 | /**
315 | * 周-月
316 | */
317 | public void weekToMonth(){
318 | if (mManager.getState() == CalendarManager.State.WEEK) {
319 | mManager.toggleView();
320 | }
321 | }
322 |
323 | /**
324 | * 月-周
325 | */
326 | public void monthToWeek(){
327 | if (mManager.getState() == CalendarManager.State.MONTH) {
328 | mManager.toggleView();
329 | }
330 | }
331 |
332 |
333 |
334 | /**
335 | * 刷新日历View
336 | */
337 | public synchronized void populateLayout() {
338 | if (mManager != null) {
339 | populateDays();
340 | if (mPrev != null) {
341 | mPrev.setEnabled(mManager.hasPrev());
342 | mNext.setEnabled(mManager.hasNext());
343 | mTitleView.setText(mManager.getHeaderText());
344 | if (mManager.getState() == CalendarManager.State.MONTH) {
345 | populateMonthLayout((Month) mManager.getUnits());
346 | } else {
347 | populateWeekLayout((Week) mManager.getUnits());
348 | }
349 | }
350 | }
351 | }
352 |
353 | /**
354 | * 刷新日历Month View
355 | * @param month 月份
356 | */
357 | private void populateMonthLayout(Month month) {
358 | List
weeks = month.getWeeks();
359 | int cnt = weeks.size();
360 | for (int i = 0; i < cnt; i++) {
361 | WeekView weekView = getWeekView(i);
362 | populateWeekLayout(weeks.get(i), weekView);
363 | }
364 | int childCnt = mWeeksView.getChildCount();
365 | if (cnt < childCnt) {
366 | for (int i = cnt; i < childCnt; i++) {
367 | cacheView(i);
368 | }
369 | }
370 | }
371 |
372 | /**
373 | * 刷新日历Week View
374 | * @param week 周
375 | */
376 | private void populateWeekLayout(Week week) {
377 | WeekView weekView = getWeekView(0);
378 | populateWeekLayout(week, weekView);
379 |
380 | int cnt = mWeeksView.getChildCount();
381 | if (cnt > 1) {
382 | for (int i = cnt - 1; i > 0; i--) {
383 | cacheView(i);
384 | }
385 | }
386 | }
387 |
388 | private void populateWeekLayout(@NonNull Week week, @NonNull WeekView weekView) {
389 |
390 | List days = week.getDays();
391 | for (int i = 0; i < 7; i++) {
392 | final Day day = days.get(i);
393 | LinearLayout layout = (LinearLayout) weekView.getChildAt(i);
394 |
395 |
396 | /*DayView dayView = (DayView) weekView.getChildAt(i);
397 | dayView.setText(day.getText());
398 | dayView.setSelected(day.isSelected());
399 | dayView.setCurrent(day.isCurrent());
400 |
401 | boolean enables = day.isEnabled();
402 | dayView.setEnabled(enables);
403 |
404 | if (enables) {
405 | dayView.setOnClickListener(new OnClickListener() {
406 | @Override
407 | public void onClick(View v) {
408 | LocalDate date = day.getDate();
409 | if (mManager.selectDay(date)) {
410 | populateLayout();
411 | if (mListener != null) {
412 | mListener.onDateSelected(date);
413 | }
414 | }
415 | }
416 | });
417 | } else {
418 | dayView.setOnClickListener(null);
419 | }*/
420 |
421 |
422 | DayView dayView = (DayView) layout.findViewById(R.id.tvDayView);
423 | DayView chinaDay = (DayView) layout.findViewById(R.id.tvChina);
424 | if (showChinaDay) {
425 | chinaDay.setVisibility(View.VISIBLE);
426 | } else {
427 | chinaDay.setVisibility(View.GONE);
428 | }
429 | View viewPoint = layout.findViewById(R.id.view_point);
430 | TextView tvDayTypeLeft = (TextView) layout.findViewById(R.id.tv_day_type_left);
431 | TextView tvDayTypeRight = (TextView) layout.findViewById(R.id.tv_day_type_right);
432 | //渲染日期上班休假类型
433 | if (dataObject != null && dataObject.optJSONObject(sdf.format(day.getDate().toDate())) != null) {
434 | JSONObject jsonObject = dataObject.optJSONObject(sdf.format(day.getDate().toDate()));
435 | if (!TextUtils.isEmpty(jsonObject.optString("type"))) {
436 | tvDayTypeRight.setText(jsonObject.optString("type"));
437 | tvDayTypeRight.setVisibility(View.VISIBLE);
438 | if (jsonObject.optString("type").equals("假")) {
439 | tvDayTypeRight.setBackgroundDrawable(getResources().getDrawable(R.drawable.bg_day_type_red));
440 | } else if (jsonObject.optString("type").equals("班")) {
441 | tvDayTypeRight.setBackgroundDrawable(getResources().getDrawable(R.drawable.bg_day_type_blue));
442 | }
443 | } else {
444 | tvDayTypeRight.setVisibility(View.GONE);
445 | }
446 | if (jsonObject.optJSONArray("list") != null && jsonObject.optJSONArray("list").length() >= 0) {
447 | viewPoint.setVisibility(View.VISIBLE);
448 | } else {
449 | viewPoint.setVisibility(View.INVISIBLE);
450 | }
451 | } else {
452 | tvDayTypeRight.setVisibility(View.INVISIBLE);
453 | viewPoint.setVisibility(View.INVISIBLE);
454 | }
455 |
456 |
457 | dayView.setText(day.getText());
458 | chinaDay.setText(day.getChinaDate());
459 | if (day.getDate().getYear() == mManager.getCurrentMonthDate().getYear()
460 | && day.getDate().getMonthOfYear() == mManager.getCurrentMonthDate().getMonthOfYear()
461 | || mManager.getState() == CalendarManager.State.WEEK) {
462 | //如果日期是当前月份
463 | if (i > 4) {
464 | //周末
465 | dayView.setTextColor(getResources().getColor(R.color.cal_blue_dark));
466 | chinaDay.setTextColor(getResources().getColor(R.color.cal_blue_dark));
467 | } else {
468 | //非周末
469 | dayView.setTextColor(getResources().getColor(R.color.cal_text_normal));
470 | chinaDay.setTextColor(getResources().getColor(R.color.cal_text_normal));
471 | }
472 | } else {
473 | //日期不是当前月份
474 | if (i > 4) {
475 | //周末
476 | dayView.setTextColor(getResources().getColor(R.color.cal_blue_light));
477 | chinaDay.setTextColor(getResources().getColor(R.color.cal_blue_light));
478 | } else {
479 | //非周末
480 | dayView.setTextColor(getResources().getColor(R.color.cal_line_grey));
481 | chinaDay.setTextColor(getResources().getColor(R.color.cal_line_grey));
482 | }
483 | }
484 | layout.setSelected(day.isSelected());
485 | if (day.getDate().equals(mManager.getToday()) && day.isSelected()) {
486 | //日期对象是今天,并且被选中
487 | if (day.getDate().getYear() == mManager.getCurrentMonthDate().getYear()
488 | && day.getDate().getMonthOfYear() == mManager.getCurrentMonthDate().getMonthOfYear()) {
489 | //如果日期是当前月份
490 | if (i > 4) {
491 | //周末
492 | dayView.setTextColor(getResources().getColorStateList(R.color.text_calendar_week));
493 | chinaDay.setTextColor(getResources().getColorStateList(R.color.text_calendar_week));
494 | } else {
495 | //非周末
496 | dayView.setTextColor(getResources().getColorStateList(R.color.text_calendar));
497 | chinaDay.setTextColor(getResources().getColorStateList(R.color.text_calendar));
498 | }
499 | } else {
500 | //日期不是当前月份
501 | if (i > 4) {
502 | //周末
503 | dayView.setTextColor(getResources().getColorStateList(R.color.text_calendar_week_out_month));
504 | chinaDay.setTextColor(getResources().getColorStateList(R.color.text_calendar_week_out_month));
505 | } else {
506 | //非周末
507 | dayView.setTextColor(getResources().getColorStateList(R.color.text_calendar_out_month));
508 | chinaDay.setTextColor(getResources().getColorStateList(R.color.text_calendar_out_month));
509 | }
510 | }
511 | layout.setBackgroundDrawable(getResources().getDrawable(R.drawable.bg_btn_calendar_today_selected));
512 | viewPoint.setBackgroundDrawable(getResources().getDrawable(R.drawable.bg_calendar_point_white));
513 | layout.setSelected(true);
514 | } else if (day.getDate().equals(mManager.getToday())) {
515 | //日期对象今天
516 | viewPoint.setBackgroundDrawable(getResources().getDrawable(R.drawable.bg_calendar_point_blue));
517 | layout.setBackgroundDrawable(getResources().getDrawable(R.drawable.bg_btn_calendar_today));
518 | layout.setSelected(true);
519 | } else {
520 | //日期对象被选中
521 | viewPoint.setBackgroundDrawable(getResources().getDrawable(R.drawable.bg_calendar_point_blue));
522 | layout.setBackgroundDrawable(getResources().getDrawable(R.drawable.bg_btn_calendar));
523 | }
524 | dayView.setCurrent(day.isCurrent());
525 | boolean enables = day.isEnabled();
526 | dayView.setEnabled(enables);
527 | if (enables) { // 解除点击限制,所有的都可以点击
528 | layout.setOnClickListener(new OnClickListener() {
529 | @Override
530 | public void onClick(View v) {
531 | LocalDate date = day.getDate();
532 | if (mManager.getState() == CalendarManager.State.MONTH) {
533 | //判断当前视图状态为月份视图
534 | if (date.getYear() > mManager.getCurrentMonthDate().getYear()) {
535 | //选中日期年份大于当前显示年份
536 | next();
537 | } else if (date.getYear() < mManager.getCurrentMonthDate().getYear()) {
538 | //选中日期年份小于当前显示年份
539 | prev();
540 | } else {
541 | //选中日期年份等于当前显示年份
542 | if (date.getMonthOfYear() > mManager.getCurrentMonthDate().getMonthOfYear()) {
543 | //选中月份大于当前月份
544 | next();
545 | } else if (date.getMonthOfYear() < mManager.getCurrentMonthDate().getMonthOfYear()) {
546 | //选中月份小于当前月份
547 | prev();
548 | }
549 | }
550 | }
551 |
552 | if (mManager.selectDay(date)) {
553 | populateLayout();
554 | if (mListener != null) {
555 | //执行选中回调
556 | mListener.onDateSelected(date);
557 | }
558 | }
559 | }
560 | });
561 | } else {
562 | dayView.setOnClickListener(null);
563 | }
564 | }
565 |
566 | }
567 |
568 | /**
569 | * 翻转到前一页
570 | */
571 | public void prev(){
572 | if (mManager.prev()) {
573 | populateLayout();
574 | }
575 | if (mListener != null) {
576 | //执行选中回调
577 | mListener.onDateSelected(mManager.getSelectedDay());
578 | }
579 | this.setAnimation(left_in);
580 | left_in.start();
581 | }
582 |
583 | /**
584 | * 翻转到下一页
585 | */
586 | public void next(){
587 | if (mManager.next()) {
588 | populateLayout();
589 | }
590 | if (mListener != null) {
591 | //执行选中回调
592 | mListener.onDateSelected(mManager.getSelectedDay());
593 | }
594 | this.setAnimation(right_in);
595 | right_in.start();
596 | }
597 |
598 |
599 | public LinearLayout getWeeksView() {
600 | return mWeeksView;
601 | }
602 |
603 |
604 | private WeekView getWeekView(int index) {
605 | int cnt = mWeeksView.getChildCount();
606 |
607 | if(cnt < index + 1) {
608 | for (int i = cnt; i < index + 1; i++) {
609 | View view = getView();
610 | mWeeksView.addView(view);
611 | }
612 | }
613 |
614 | return (WeekView) mWeeksView.getChildAt(index);
615 | }
616 |
617 | private View getView() {
618 | View view = mRecycleBin.recycleView();
619 | if (view == null) {
620 | view = mInflater.inflate(R.layout.week_layout, this, false);
621 | } else {
622 | view.setVisibility(View.VISIBLE);
623 | }
624 | return view;
625 | }
626 |
627 | private void cacheView(int index) {
628 | View view = mWeeksView.getChildAt(index);
629 | if(view != null) {
630 | mWeeksView.removeViewAt(index);
631 | mRecycleBin.addView(view);
632 | }
633 | }
634 |
635 | public LocalDate getSelectedDate() {
636 | return mManager.getSelectedDay();
637 | }
638 |
639 | @Override
640 | protected void onDetachedFromWindow() {
641 | super.onDetachedFromWindow();
642 |
643 | mResizeManager.recycle();
644 | }
645 |
646 | private class RecycleBin {
647 | private final Queue mViews = new LinkedList();
648 | public View recycleView() {
649 | return mViews.poll();
650 | }
651 | public void addView(View view) {
652 | mViews.add(view);
653 | }
654 | }
655 |
656 |
657 | }
658 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/listener/OnDateSelect.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.listener;
2 |
3 | import org.joda.time.LocalDate;
4 |
5 | /**
6 | * @Author : chenyongci
7 | * @date : 2018/7/25
8 | * @desc :
9 | */
10 |
11 | public interface OnDateSelect {
12 | void onDateSelected(LocalDate date);
13 | }
14 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/listener/OnMonthChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.listener;
2 |
3 | import org.joda.time.LocalDate;
4 |
5 | /**
6 | * @Author : chenyongci
7 | * @date : 2018/7/25
8 | * @desc :
9 | */
10 |
11 | public interface OnMonthChangeListener {
12 | /**
13 | * 月份切换监听器
14 | * @param month 当前月份
15 | * @param mSelected 选中日期
16 | */
17 | void monthChange(String month, LocalDate mSelected);
18 | }
19 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/listener/OnTitleClickListener.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.listener;
2 |
3 | /**
4 | * @Author : chenyongci
5 | * @date : 2018/7/25
6 | * @desc :
7 | */
8 |
9 | public interface OnTitleClickListener {
10 | void onTitleClick();
11 | }
12 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/manager/CalendarManager.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.manager;
2 |
3 | import com.android.calendarlibrary.listener.OnMonthChangeListener;
4 |
5 | import org.joda.time.LocalDate;
6 |
7 | import java.util.Calendar;
8 |
9 | /**
10 | * 日历管理
11 | * @author MaJian
12 | *
13 | */
14 | public class CalendarManager {
15 |
16 | private State mState;
17 | private RangeUnit mUnit;
18 | private LocalDate mSelected;
19 | private LocalDate mToday;
20 | private LocalDate mMinDate;
21 | private LocalDate mMaxDate;
22 | private Formatter formatter;
23 | private LocalDate mActiveMonth;
24 | private String monthStr = ""; //月份字符串
25 | private OnMonthChangeListener monthChangeListener;
26 |
27 |
28 | /**
29 | * 设置月份切换监听器
30 | * @param monthChangeListener
31 | */
32 | public void setMonthChangeListener(OnMonthChangeListener monthChangeListener){
33 | this.monthChangeListener = monthChangeListener;
34 | }
35 |
36 | /**
37 | * 构造器
38 | * @param selected 选中的日期
39 | * @param state 状态:月份、周
40 | * @param minDate 最小日期
41 | * @param maxDate 最大日期
42 | */
43 | public CalendarManager( LocalDate selected, State state, LocalDate minDate,
44 | LocalDate maxDate) {
45 | this(selected, state, minDate, maxDate, null);
46 | }
47 | /**
48 | * 设置今天
49 | */
50 | public void setToday(){
51 | mToday = LocalDate.now();
52 | }
53 |
54 | public CalendarManager( LocalDate selected, State state, LocalDate minDate,
55 | LocalDate maxDate, Formatter formatter) {
56 | mToday = LocalDate.now();
57 | mState = state;
58 | if (formatter == null) {
59 | this.formatter = new DefaultFormatter();
60 | } else {
61 | this.formatter = formatter;
62 | }
63 | init(selected, minDate, maxDate);
64 | }
65 |
66 | /**
67 | * 返回今天日期对象
68 | * @return
69 | */
70 | public LocalDate getToday(){
71 | if (mToday == null) {
72 | return LocalDate.now();
73 | }
74 | return mToday;
75 | }
76 |
77 | public boolean selectDay( LocalDate date) {
78 | if (!mSelected.isEqual(date)) {
79 | mUnit.deselect(mSelected);
80 | mSelected = date;
81 | mUnit.select(mSelected);
82 |
83 | if (mState == State.WEEK) {
84 | setActiveMonth(date);
85 | }
86 | return true;
87 | } else {
88 | return false;
89 | }
90 | }
91 |
92 |
93 | public LocalDate getSelectedDay() {
94 | return mSelected;
95 | }
96 |
97 | /**
98 | * 获取头部日期显示文本
99 | * @return 根据日期单位返回月份或者周数
100 | */
101 |
102 | public String getHeaderText() {
103 | if (!monthStr.equals(formatter.getHeaderText(mUnit.getType(), mUnit.getFrom(), mUnit.getTo() , mSelected))) {
104 | monthStr = formatter.getHeaderText(mUnit.getType(), mUnit.getFrom(), mUnit.getTo() , mSelected);
105 | if (monthChangeListener != null) {
106 | monthChangeListener.monthChange(monthStr,mSelected);
107 | }
108 | }
109 | return monthStr;
110 | }
111 |
112 | /**
113 | * 得到日历月份的一个日期
114 | * @return
115 | */
116 | public LocalDate getCurrentMonthDate(){
117 | if (mUnit == null || mUnit.getFrom() == null) {
118 | return LocalDate.now();
119 | }
120 | return mUnit.getFrom();
121 | }
122 |
123 | /**
124 | * 是否有下一项
125 | * @return
126 | */
127 | public boolean hasNext() {
128 | return mUnit.hasNext();
129 | }
130 |
131 | /**
132 | * 是否有前一项
133 | * @return
134 | */
135 | public boolean hasPrev() {
136 | return mUnit.hasPrev();
137 | }
138 |
139 | /**
140 | * 下一项
141 | * @return
142 | */
143 | public boolean next() {
144 | boolean next = mUnit.next();
145 | Calendar cal = Calendar.getInstance();
146 | cal.setTime(mSelected.toDate());
147 | if (mState == State.MONTH) {
148 | cal.add(Calendar.MONTH, 1);
149 | mSelected = LocalDate.fromCalendarFields(cal);
150 | selectDay(mSelected);
151 | } else if (mState == State.WEEK) {
152 | cal.add(Calendar.WEEK_OF_MONTH, 1);
153 | mSelected = LocalDate.fromCalendarFields(cal);
154 | selectDay(mSelected);
155 | }
156 | mUnit.select(mSelected);
157 | setActiveMonth(mUnit.getFrom());
158 | return next;
159 | }
160 |
161 | /**
162 | * 前一项
163 | * @return
164 | */
165 | public boolean prev() {
166 | boolean prev = mUnit.prev();
167 | Calendar cal = Calendar.getInstance();
168 | cal.setTime(mSelected.toDate());
169 | if (mState == State.MONTH) {
170 | cal.add(Calendar.MONTH, -1);
171 | mSelected = LocalDate.fromCalendarFields(cal);
172 | selectDay(mSelected);
173 | } else if (mState == State.WEEK) {
174 | cal.add(Calendar.WEEK_OF_MONTH, -1);
175 | mSelected = LocalDate.fromCalendarFields(cal);
176 | selectDay(mSelected);
177 | }
178 | mUnit.select(mSelected);
179 | setActiveMonth(mUnit.getTo());
180 | return prev;
181 | }
182 |
183 | /**
184 | *
185 | * @return index of month to focus to
186 | */
187 | public synchronized void toggleView() {
188 | if(mState == State.MONTH) {
189 | toggleFromMonth();
190 | } else {
191 | toggleFromWeek();
192 | }
193 | }
194 |
195 | /**
196 | * 得到当前日历状态
197 | * @return
198 | */
199 | public State getState() {
200 | return mState;
201 | }
202 |
203 | public CalendarUnit getUnits() {
204 | return mUnit;
205 | }
206 |
207 | public LocalDate getActiveMonth() {
208 | return mActiveMonth;
209 | }
210 |
211 | private void setActiveMonth(LocalDate activeMonth) {
212 | mActiveMonth = activeMonth.withDayOfMonth(1);
213 | }
214 |
215 | private synchronized void toggleFromMonth() {
216 |
217 | // if same month as selected
218 | if (mUnit.isInView(mSelected)) {
219 | toggleFromMonth(mSelected);
220 |
221 | setActiveMonth(mSelected);
222 | } else {
223 | setActiveMonth(mUnit.getFrom());
224 | toggleFromMonth(mUnit.getFirstDateOfCurrentMonth(mActiveMonth));
225 | }
226 | }
227 |
228 | void toggleToWeek(int weekInMonth) {
229 | LocalDate date = mUnit.getFrom().plusDays(weekInMonth * 7);
230 | toggleFromMonth(date);
231 | }
232 |
233 | private void toggleFromMonth(LocalDate date) {
234 | setUnit(new Week(date, mToday, mMinDate, mMaxDate));
235 | mUnit.select(mSelected);
236 | mState = State.WEEK;
237 | }
238 |
239 | private synchronized void toggleFromWeek() {
240 | setUnit(new Month(mActiveMonth, mToday, mMinDate, mMaxDate));
241 | mUnit.select(mSelected);
242 | mState = State.MONTH;
243 | }
244 |
245 | private void init() {
246 | if (mState == State.MONTH) {
247 | setUnit(new Month(mSelected, mToday, mMinDate, mMaxDate));
248 | } else {
249 | setUnit(new Week(mSelected, mToday, mMinDate, mMaxDate));
250 | }
251 | mUnit.select(mSelected);
252 | }
253 |
254 | synchronized void setUnit( RangeUnit unit) {
255 | if (unit != null) {
256 | mUnit = unit;
257 | }
258 | }
259 |
260 | public int getWeekOfMonth() {
261 | if(mUnit.isInView(mSelected)) {
262 | if (mUnit.isIn(mSelected)) { // TODO not pretty
263 | return mUnit.getWeekInMonth(mSelected);
264 | } else if (mUnit.getFrom().isAfter(mSelected)) {
265 | return mUnit.getWeekInMonth(mUnit.getFrom());
266 | } else {
267 | return mUnit.getWeekInMonth(mUnit.getTo());
268 | }
269 | } else {
270 | return mUnit.getFirstWeek(mUnit.getFirstDateOfCurrentMonth(mActiveMonth)); // if not in this month first week should be selected
271 | }
272 | }
273 |
274 | public void init( LocalDate date){
275 | this.init(date, mMinDate, mMaxDate);
276 | }
277 |
278 | public void init( LocalDate date, LocalDate minDate, LocalDate maxDate) {
279 | mSelected = date;
280 | setActiveMonth(date);
281 | mMinDate = minDate;
282 | mMaxDate = maxDate;
283 | init();
284 | }
285 |
286 | public LocalDate getMinDate() {
287 | return mMinDate;
288 | }
289 |
290 | public void setMinDate( LocalDate minDate) {
291 | mMinDate = minDate;
292 | }
293 |
294 | public LocalDate getMaxDate() {
295 | return mMaxDate;
296 | }
297 |
298 | public void setMaxDate( LocalDate maxDate) {
299 | mMaxDate = maxDate;
300 | }
301 |
302 | public Formatter getFormatter() {
303 | return formatter;
304 | }
305 |
306 | /**
307 | * CalendarManager State
308 | * @author MaJian
309 | *
310 | */
311 | public enum State {
312 | MONTH,
313 | WEEK
314 | }
315 |
316 | }
317 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/manager/CalendarUnit.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.manager;
2 |
3 | import android.support.annotation.IntDef;
4 |
5 | import org.joda.time.DateTimeConstants;
6 | import org.joda.time.LocalDate;
7 |
8 | import java.lang.annotation.Retention;
9 | import java.lang.annotation.RetentionPolicy;
10 |
11 |
12 | /**
13 | *
14 | * @author MaJian
15 | *
16 | */
17 | public abstract class CalendarUnit {
18 |
19 | @IntDef({ TYPE_WEEK, TYPE_MONTH })
20 | @Retention(RetentionPolicy.SOURCE)
21 | public @interface CalendarType { }
22 |
23 | public static final int TYPE_WEEK = 1;
24 | public static final int TYPE_MONTH = 2;
25 |
26 | private final LocalDate mToday;
27 | private LocalDate mFrom;
28 | private LocalDate mTo;
29 | private boolean mSelected;
30 |
31 | protected CalendarUnit( LocalDate from, LocalDate to, LocalDate today) {
32 | mToday = today;
33 | mFrom = from;
34 | mTo = to;
35 | }
36 |
37 | public LocalDate getToday() {
38 | return mToday;
39 | }
40 |
41 | public LocalDate getFrom() {
42 | return mFrom;
43 | }
44 |
45 | public LocalDate getTo() {
46 | return mTo;
47 | }
48 |
49 | protected void setFrom( LocalDate from) {
50 | mFrom = from;
51 | }
52 |
53 | protected void setTo( LocalDate to) {
54 | mTo = to;
55 | }
56 |
57 | protected void setSelected(boolean selected) {
58 | mSelected = selected;
59 | }
60 |
61 | public boolean isSelected() {
62 | return mSelected;
63 | }
64 |
65 | public abstract boolean hasNext();
66 |
67 | public abstract boolean hasPrev();
68 |
69 | public abstract boolean next();
70 |
71 | public abstract boolean prev();
72 |
73 | @CalendarType public abstract int getType();
74 |
75 | public boolean isIn( LocalDate date) {
76 | return !mFrom.isAfter(date) && !mTo.isBefore(date);
77 | }
78 |
79 | public boolean isInView( LocalDate date) {
80 | return !mFrom.withDayOfWeek(DateTimeConstants.MONDAY).isAfter(date)
81 | && !mTo.withDayOfWeek(DateTimeConstants.SUNDAY).isBefore(date);
82 | }
83 |
84 | public abstract void deselect( LocalDate date);
85 |
86 | public abstract boolean select( LocalDate date);
87 |
88 | public abstract void build();
89 |
90 | @Override
91 | public boolean equals(Object o) {
92 | if (this == o) return true;
93 | if (!(o instanceof CalendarUnit)) return false;
94 |
95 | CalendarUnit that = (CalendarUnit) o;
96 |
97 | if (mSelected != that.mSelected) return false;
98 | if (!mFrom.equals(that.mFrom)) return false;
99 | if (!mTo.equals(that.mTo)) return false;
100 | if (!mToday.equals(that.mToday)) return false;
101 |
102 | return true;
103 | }
104 |
105 | @Override
106 | public int hashCode() {
107 | int result = mToday.hashCode();
108 | result = 31 * result + mFrom.hashCode();
109 | result = 31 * result + mTo.hashCode();
110 | result = 31 * result + (mSelected ? 1 : 0);
111 | return result;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/manager/Day.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.manager;
2 |
3 |
4 | import com.android.calendarlibrary.utils.ChinaDate;
5 |
6 | import org.joda.time.LocalDate;
7 | import org.joda.time.format.DateTimeFormat;
8 | import org.joda.time.format.DateTimeFormatter;
9 |
10 |
11 | /**
12 | * Created by Blaz Solar on 24/02/14.
13 | */
14 | public class Day {
15 |
16 | private static final DateTimeFormatter mFormatter = DateTimeFormat.forPattern("d");
17 |
18 | private final LocalDate mDate;
19 | private boolean mToday;
20 | private boolean mSelected;
21 | private boolean mEnabled;
22 | private boolean mCurrent;
23 |
24 | public Day( LocalDate date, boolean today) {
25 | mDate = date;
26 | mToday = today;
27 | mEnabled = true;
28 | mCurrent = true;
29 | }
30 |
31 |
32 | public LocalDate getDate() {
33 | return mDate;
34 | }
35 |
36 | public boolean isEnabled() {
37 | return mEnabled;
38 | }
39 |
40 | public void setEnabled(boolean enabled) {
41 | mEnabled = enabled;
42 | }
43 |
44 | public boolean isSelected() {
45 | return mSelected;
46 | }
47 |
48 | public void setSelected(boolean selected) {
49 | mSelected = selected;
50 | }
51 |
52 | public boolean isCurrent() {
53 | return mCurrent;
54 | }
55 |
56 | public void setCurrent(boolean current) {
57 | mCurrent = current;
58 | }
59 |
60 | public boolean isToday() {
61 | return mToday;
62 | }
63 |
64 | @Override
65 | public boolean equals(Object o) {
66 | if (this == o) return true;
67 | if (o == null || getClass() != o.getClass()) return false;
68 |
69 | Day day = (Day) o;
70 |
71 | if (mEnabled != day.mEnabled) return false;
72 | if (mSelected != day.mSelected) return false;
73 | if (mToday != day.mToday) return false;
74 | if (!mDate.isEqual(day.mDate)) return false;
75 |
76 | return true;
77 | }
78 |
79 | @Override
80 | public int hashCode() {
81 | int result = mDate.hashCode();
82 | result = 31 * result + (mToday ? 1 : 0);
83 | result = 31 * result + (mSelected ? 1 : 0);
84 | result = 31 * result + (mEnabled ? 1 : 0);
85 | return result;
86 | }
87 |
88 |
89 | public String getText() {
90 | return mDate.toString(mFormatter);
91 | }
92 |
93 | public final static String[] nStr1 = new String[] { "", "正", "二", "三", "四",
94 | "五", "六", "七", "八", "九", "十", "十一", "十二" };
95 |
96 | /**
97 | * 获取农历时间
98 | */
99 | public String getChinaDate() {
100 | long[] l = ChinaDate.calElement(mDate.getYear(), mDate.getMonthOfYear(), mDate.getDayOfMonth());
101 | String chinadate= ChinaDate.getChinaDate((int) (l[2]));
102 | if(chinadate.equals("初一")){
103 | chinadate=nStr1[(int) l[1]]+"月";
104 | }
105 | return chinadate;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/manager/DefaultFormatter.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.manager;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import org.joda.time.LocalDate;
6 | import org.joda.time.format.DateTimeFormat;
7 | import org.joda.time.format.DateTimeFormatter;
8 |
9 | /**
10 | * 默认日期格式化
11 | * @author MaJian
12 | *
13 | */
14 | public class DefaultFormatter implements Formatter {
15 |
16 | private final DateTimeFormatter dayFormatter;
17 | @SuppressWarnings("unused")
18 | private final DateTimeFormatter weekHeaderFormatter;
19 | private final DateTimeFormatter monthHeaderFormatter;
20 |
21 | public DefaultFormatter() {
22 | this("E", "'第' w '周'", "yyyy '年' M '月'");
23 | }
24 |
25 | public DefaultFormatter(@NonNull String dayPattern, @NonNull String weekPattern, @NonNull String monthPattern) {
26 | dayFormatter = DateTimeFormat.forPattern(dayPattern);
27 | weekHeaderFormatter = DateTimeFormat.forPattern(weekPattern);
28 | monthHeaderFormatter = DateTimeFormat.forPattern(monthPattern);
29 | }
30 |
31 | @Override
32 | public String getDayName(@NonNull LocalDate date) {
33 | return date.toString(dayFormatter);
34 | }
35 |
36 | @Override
37 | public String getHeaderText(@CalendarUnit.CalendarType int type, @NonNull LocalDate from, @NonNull LocalDate to , @NonNull LocalDate selected) {
38 | if (from.isBefore(selected) && to.isAfter(selected) || from.isEqual(selected) || to.isEqual(selected)) {
39 | //如果选中日期在开始日期和结束日期之间,header返回选中日期月份
40 | return selected.toString(monthHeaderFormatter);
41 | }
42 | switch (type) {
43 | case CalendarUnit.TYPE_WEEK:
44 | //周视图默认显示结束日期月份
45 | return to.toString(monthHeaderFormatter);
46 | case CalendarUnit.TYPE_MONTH:
47 | //月视图默认显示开始日期月份
48 | return from.toString(monthHeaderFormatter);
49 | default:
50 | throw new IllegalStateException("Unknown calendar type");
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/manager/Formatter.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.manager;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import org.joda.time.LocalDate;
6 |
7 | /**
8 | * Created by Blaz Solar on 26/04/15.
9 | */
10 | public interface Formatter {
11 |
12 | String getDayName(@NonNull LocalDate date);
13 | String getHeaderText(int type, @NonNull LocalDate from, @NonNull LocalDate to, @NonNull LocalDate selected);
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/manager/Month.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.manager;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 |
6 | import org.joda.time.LocalDate;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /**
12 | * Created by Blaz Solar on 24/02/14.
13 | */
14 | public class Month extends RangeUnit {
15 |
16 | @NonNull
17 | private final List mWeeks = new ArrayList();
18 | private int mSelectedIndex = -1;
19 |
20 | protected Month(@NonNull LocalDate date, @NonNull LocalDate today, @Nullable LocalDate minDate,
21 | @Nullable LocalDate maxDate) {
22 | super(
23 | date.withDayOfMonth(1),
24 | date.withDayOfMonth(date.dayOfMonth().getMaximumValue()),
25 | today,
26 | minDate,
27 | maxDate
28 | );
29 |
30 | build();
31 | }
32 |
33 | @Override
34 | public boolean hasNext() {
35 |
36 | LocalDate maxDate = getMaxDate();
37 | if (maxDate == null) {
38 | return true;
39 | } else {
40 |
41 | LocalDate to = getTo();
42 | int year = maxDate.getYear();
43 | int yearTo = to.getYear();
44 |
45 | int month = maxDate.getMonthOfYear();
46 | int monthTo = to.getMonthOfYear();
47 |
48 | return year > yearTo ||
49 | (year == yearTo && month > monthTo);
50 |
51 | }
52 | }
53 |
54 | @Override
55 | public boolean hasPrev() {
56 |
57 | LocalDate minDate = getMinDate();
58 | if (minDate == null) {
59 | return true;
60 | } else {
61 |
62 | LocalDate from = getFrom();
63 | int year = minDate.getYear();
64 | int yearFrom = from.getYear();
65 |
66 | int month = minDate.getMonthOfYear();
67 | int monthFrom = from.getMonthOfYear();
68 |
69 | return year < yearFrom ||
70 | (year == yearFrom && month < monthFrom);
71 |
72 | }
73 | }
74 |
75 | @Override
76 | public boolean next() {
77 |
78 | if (hasNext()) {
79 | setFrom(getTo().plusDays(1));
80 | setTo(getFrom().withDayOfMonth(getFrom().dayOfMonth().getMaximumValue()));
81 |
82 | build();
83 | return true;
84 | } else {
85 | return false;
86 | }
87 | }
88 |
89 | @Override
90 | public boolean prev() {
91 |
92 | if (hasPrev()) {
93 | setFrom(getFrom().minusDays(1).withDayOfMonth(1));
94 | setTo(getFrom().withDayOfMonth(getFrom().dayOfMonth().getMaximumValue()));
95 |
96 | build();
97 | return true;
98 | } else {
99 | return false;
100 | }
101 |
102 | }
103 |
104 | @Override
105 | public int getType() {
106 | return TYPE_MONTH;
107 | }
108 |
109 | @Override
110 | public void deselect(@NonNull LocalDate date) {
111 | if (date != null && isSelected() && isInView(date)) {
112 | for (Week week : mWeeks) {
113 | if (week.isSelected() && week.isIn(date)) {
114 | mSelectedIndex = -1;
115 | setSelected(false);
116 | week.deselect(date);
117 | }
118 | }
119 | }
120 | }
121 |
122 | @Override
123 | public boolean select(@NonNull LocalDate date) {
124 | int cnt = mWeeks.size();
125 | for (int i = 0; i < cnt; i++) {
126 | Week week = mWeeks.get(i);
127 | if (week.select(date)) {
128 | mSelectedIndex = i;
129 | setSelected(true);
130 | return true;
131 | }
132 | }
133 | return false;
134 | }
135 |
136 | @NonNull
137 | public List getWeeks() {
138 | return mWeeks;
139 | }
140 |
141 | public int getSelectedIndex() {
142 | return mSelectedIndex;
143 | }
144 |
145 | @Override
146 | public void build() {
147 |
148 | setSelected(false);
149 | mWeeks.clear();
150 |
151 | LocalDate date = getFrom().withDayOfWeek(1);
152 | for (int i = 0; i == 0 || getTo().compareTo(date) >= 0; i++) {
153 | mWeeks.add(new Week(date, getToday(), getMinDate(), getMaxDate()));
154 | date = date.plusWeeks(1);
155 | }
156 |
157 | }
158 |
159 | @Nullable
160 | @Override
161 | LocalDate getFirstDateOfCurrentMonth(@NonNull LocalDate currentMonth) {
162 |
163 | if (currentMonth != null) {
164 | int year = currentMonth.getYear();
165 | int month = currentMonth.getMonthOfYear();
166 |
167 | LocalDate from = getFirstEnabled();
168 | int fromYear = from.getYear();
169 | int fromMonth = from.getMonthOfYear();
170 |
171 | if (year == fromYear && month == fromMonth) {
172 | return from;
173 | }
174 | }
175 |
176 | return null;
177 |
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/manager/ProgressManager.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.manager;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.widget.LinearLayout;
5 |
6 | import com.android.calendarlibrary.CollapseCalendarView;
7 | import com.android.calendarlibrary.models.AbstractViewHolder;
8 | import com.android.calendarlibrary.models.SizeViewHolder;
9 |
10 |
11 | /**
12 | * Created by Blaz Solar on 17/04/14.
13 | */
14 | public abstract class ProgressManager {
15 |
16 | public static final String TAG = "ProgressManager";
17 |
18 | @NonNull
19 | protected CollapseCalendarView mCalendarView;
20 |
21 | protected LinearLayout mWeeksView;
22 | protected AbstractViewHolder[] mViews;
23 |
24 | protected SizeViewHolder mCalendarHolder;
25 | protected SizeViewHolder mWeeksHolder;
26 |
27 | final int mActiveIndex;
28 |
29 | private boolean mInitialized = false;
30 |
31 | final boolean mFromMonth;
32 |
33 | protected ProgressManager(@NonNull CollapseCalendarView calendarView, int activeWeek, boolean fromMonth) {
34 | mCalendarView = calendarView;
35 | mWeeksView = calendarView.getWeeksView();
36 | mActiveIndex = activeWeek;
37 | mFromMonth = fromMonth;
38 | }
39 |
40 | public void applyDelta(float delta) {
41 | float progress = getProgress(getDeltaInBounds(delta));
42 | apply(progress);
43 | }
44 |
45 | public void apply(float progress) {
46 |
47 | //解决日历快速滑动消失问题
48 | if (Float.isNaN(progress)) {
49 | return;
50 | }
51 | mCalendarHolder.animate(progress);
52 | mWeeksHolder.animate(progress);
53 |
54 | // animate views if necessary
55 | if (mViews != null) {
56 | for (AbstractViewHolder view : mViews) {
57 | view.animate(progress);
58 | }
59 | }
60 |
61 | // request layout
62 | mCalendarView.requestLayout();
63 |
64 | }
65 |
66 | public boolean isInitialized() {
67 | return mInitialized;
68 | }
69 |
70 | void setInitialized(boolean initialized) {
71 | mInitialized = initialized;
72 | }
73 |
74 | public int getCurrentHeight() {
75 | return mCalendarView.getLayoutParams().height - mCalendarHolder.getMinHeight();
76 | }
77 |
78 | public int getStartSize() {
79 | return 0;
80 | }
81 |
82 | public int getEndSize() {
83 | return mCalendarHolder.getHeight();
84 | }
85 |
86 | public abstract void finish(boolean expanded);
87 |
88 | public float getProgress(int distance) {
89 | return Math.max(0, Math.min(distance * 1f / mCalendarHolder.getHeight(), 1));
90 | }
91 |
92 | protected int getActiveIndex() {
93 | return mActiveIndex;
94 | }
95 |
96 | private int getDeltaInBounds(float delta) {
97 |
98 | if (mFromMonth) {
99 | return (int) Math.max(-mCalendarHolder.getHeight(), Math.min(0, delta)) + mCalendarHolder.getHeight();
100 | } else {
101 | return (int) Math.max(0, Math.min(mCalendarHolder.getHeight(), delta));
102 | }
103 |
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/manager/ProgressManagerImpl.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.manager;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.view.ViewTreeObserver;
7 |
8 | import com.android.calendarlibrary.CollapseCalendarView;
9 | import com.android.calendarlibrary.models.AbstractViewHolder;
10 | import com.android.calendarlibrary.models.SizeViewHolder;
11 | import com.android.calendarlibrary.models.StubViewHolder;
12 |
13 |
14 | /**
15 | * Created by Blaz Solar on 17/04/14.
16 | */
17 | public class ProgressManagerImpl extends ProgressManager {
18 |
19 | public ProgressManagerImpl(@NonNull CollapseCalendarView calendarView, int activeWeek, boolean fromMonth) {
20 | super(calendarView, activeWeek, fromMonth);
21 |
22 | if (!fromMonth) {
23 | initMonthView();
24 | } else {
25 | initWeekView();
26 | }
27 |
28 | }
29 |
30 | @Override
31 | public void finish(final boolean expanded) {
32 |
33 | mCalendarView.post(new Runnable() { // to prevent flickering
34 | @Override
35 | public void run() {
36 | mCalendarView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
37 | mWeeksView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
38 |
39 | for (AbstractViewHolder view : mViews) {
40 | view.onFinish(true);
41 | }
42 |
43 | if (!expanded) {
44 | CalendarManager manager = mCalendarView.getManager();
45 | if (mFromMonth) {
46 | manager.toggleView();
47 | } else {
48 | manager.toggleToWeek(mActiveIndex);
49 | }
50 | mCalendarView.populateLayout();
51 | }
52 | }
53 | });
54 | }
55 |
56 | private void initMonthView() {
57 |
58 | mCalendarHolder = new SizeViewHolder(mCalendarView.getHeight(), 0);
59 | mCalendarHolder.setView(mCalendarView);
60 | mCalendarHolder.setDelay(0);
61 | mCalendarHolder.setDuration(1);
62 |
63 | mWeeksHolder = new SizeViewHolder(mWeeksView.getHeight(), 0);
64 | mWeeksHolder.setView(mWeeksView);
65 | mWeeksHolder.setDelay(0);
66 | mWeeksHolder.setDuration(1);
67 |
68 | mCalendarView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
69 | @Override
70 | public boolean onPreDraw() {
71 | mCalendarView.getViewTreeObserver().removeOnPreDrawListener(this);
72 |
73 | mCalendarHolder.setMaxHeight(mCalendarView.getHeight());
74 | mWeeksHolder.setMaxHeight(mWeeksView.getHeight());
75 |
76 | mCalendarView.getLayoutParams().height = mCalendarHolder.getMinHeight();
77 | mWeeksView.getLayoutParams().height = mCalendarHolder.getMinHeight();
78 |
79 | initializeChildren();
80 |
81 | setInitialized(true);
82 |
83 | return false;
84 | }
85 | });
86 | }
87 |
88 | private void initWeekView() {
89 |
90 | mCalendarHolder = new SizeViewHolder(0, mCalendarView.getHeight());
91 | mCalendarHolder.setView(mCalendarView);
92 | mCalendarHolder.setDelay(0);
93 | mCalendarHolder.setDuration(1);
94 |
95 | mWeeksHolder = new SizeViewHolder(0, mWeeksView.getHeight());
96 | mWeeksHolder.setView(mWeeksView);
97 | mWeeksHolder.setDelay(0);
98 | mWeeksHolder.setDuration(1);
99 |
100 | initializeChildren();
101 |
102 | mCalendarView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
103 | @Override
104 | public boolean onPreDraw() {
105 | mCalendarView.getViewTreeObserver().removeOnPreDrawListener(this);
106 |
107 | mCalendarHolder.setMinHeight(mCalendarView.getHeight());
108 | mWeeksHolder.setMinHeight(mWeeksView.getHeight());
109 |
110 | mCalendarView.getLayoutParams().height = mCalendarHolder.getMaxHeight();
111 | mWeeksView.getLayoutParams().height = mCalendarHolder.getMaxHeight();
112 |
113 | setInitialized(true);
114 |
115 | return false;
116 | }
117 | });
118 | }
119 |
120 | private void initializeChildren() {
121 |
122 | int childCount = mWeeksView.getChildCount();
123 |
124 | // FIXME do not assume that all views are the same height
125 | mViews = new AbstractViewHolder[childCount];
126 | for (int i = 0; i < childCount; i++) {
127 |
128 | View view = mWeeksView.getChildAt(i);
129 |
130 | int activeIndex = getActiveIndex();
131 |
132 | AbstractViewHolder holder;
133 | if (i == activeIndex) {
134 | holder = new StubViewHolder();
135 | } else {
136 | SizeViewHolder tmpHolder = new SizeViewHolder(0, view.getHeight());
137 |
138 | final int duration = mWeeksHolder.getMaxHeight() - view.getHeight();
139 |
140 | if (i < activeIndex) {
141 | tmpHolder.setDelay(view.getTop() * 1.0f / duration);
142 | } else {
143 | tmpHolder.setDelay((view.getTop() - view.getHeight()) * 1.0f / duration);
144 | }
145 | tmpHolder.setDuration(view.getHeight() * 1.0f / duration);
146 |
147 | holder = tmpHolder;
148 |
149 | view.setVisibility(View.GONE);
150 | }
151 |
152 | holder.setView(view);
153 |
154 | mViews[i] = holder;
155 | }
156 |
157 | }
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/manager/RangeUnit.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.manager;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 |
6 | import org.joda.time.DateTimeConstants;
7 | import org.joda.time.Days;
8 | import org.joda.time.LocalDate;
9 |
10 | /**
11 | * Created by Blaz Solar on 24/05/14.
12 | */
13 | public abstract class RangeUnit extends CalendarUnit {
14 |
15 | @Nullable
16 | private LocalDate mMinDate;
17 | @Nullable
18 | private LocalDate mMaxDate;
19 |
20 | protected RangeUnit(@NonNull LocalDate from, @NonNull LocalDate to, @NonNull LocalDate today,
21 | @Nullable LocalDate minDate, @Nullable LocalDate maxDate) {
22 | super(from, to, today);
23 |
24 | if (minDate != null && maxDate != null && minDate.isAfter(maxDate)) {
25 | throw new IllegalArgumentException("Min date should be before max date");
26 | }
27 | mMinDate = minDate;
28 | mMaxDate = maxDate;
29 | }
30 |
31 | @Nullable
32 | public LocalDate getMinDate() {
33 | return mMinDate;
34 | }
35 |
36 | @Nullable
37 | public LocalDate getMaxDate() {
38 | return mMaxDate;
39 | }
40 |
41 | /**
42 | *
43 | *
44 | * @param firstDayOfMonth First day of current month in range unit
45 | * @return Week of month of first enabled date, 0 if no dates are enabled.
46 | */
47 | public int getFirstWeek(@Nullable LocalDate firstDayOfMonth) {
48 | LocalDate from = getFrom();
49 | LocalDate date;
50 | if (mMinDate != null && mMinDate.isAfter(from)) { // TODO check if same month
51 | date = mMinDate;
52 | } else {
53 | date = firstDayOfMonth;
54 | }
55 |
56 | return getWeekInMonth(date);
57 | }
58 |
59 | LocalDate getFirstEnabled() {
60 | LocalDate from = getFrom();
61 | if (mMinDate != null && from.isBefore(mMinDate)) {
62 | return mMinDate;
63 | } else {
64 | return from;
65 | }
66 | }
67 |
68 | @Nullable
69 | abstract LocalDate getFirstDateOfCurrentMonth(@NonNull LocalDate currentMonth);
70 |
71 | protected int getWeekInMonth(@Nullable LocalDate date) {
72 | if (date != null) {
73 | LocalDate first = date.withDayOfMonth(1).withDayOfWeek(DateTimeConstants.MONDAY);
74 | Days days = Days.daysBetween(first, date);
75 | return days.dividedBy(DateTimeConstants.DAYS_PER_WEEK).getDays();
76 | } else {
77 | return 0;
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/manager/ResizeManager.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.manager;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 | import android.support.v4.view.MotionEventCompat;
6 | import android.view.MotionEvent;
7 | import android.view.VelocityTracker;
8 | import android.view.ViewConfiguration;
9 | import android.widget.Scroller;
10 |
11 | import com.android.calendarlibrary.CollapseCalendarView;
12 |
13 |
14 | /**
15 | * 重新设置尺寸管理器
16 | * @author MaJian
17 | *
18 | */
19 | public class ResizeManager {
20 |
21 | public static final String TAG = "ResizeManager";
22 |
23 | /** View to resize */
24 | @NonNull
25 | private CollapseCalendarView mCalendarView;
26 |
27 | /** Distance in px until drag has started */
28 | private final int mTouchSlop;
29 |
30 | private final int mMinFlingVelocity;
31 |
32 | private final int mMaxFlingVelocity;
33 |
34 | /** Y position on {@link MotionEvent#ACTION_DOWN} */
35 | private float mDownY;
36 |
37 | /** Y position on {@link MotionEvent#ACTION_DOWN} */
38 | private float mDownX;
39 |
40 | /** Y position when resizing started */
41 | private float mDragStartY;
42 |
43 | /** If calendar is currently resizing. */
44 | private State mState = State.IDLE;
45 |
46 | private VelocityTracker mVelocityTracker;
47 | private final Scroller mScroller;
48 | private int type = -1;
49 | public static final int LEFT = 0;
50 | public static final int RIGHT = 1;
51 |
52 | @Nullable
53 | private ProgressManager mProgressManager;
54 |
55 | public ResizeManager(@NonNull CollapseCalendarView calendarView) {
56 | mCalendarView = calendarView;
57 | mScroller = new Scroller(calendarView.getContext());
58 | ViewConfiguration viewConfig = ViewConfiguration.get(mCalendarView.getContext());
59 | mTouchSlop = viewConfig.getScaledTouchSlop();
60 | mMinFlingVelocity = viewConfig.getScaledMinimumFlingVelocity();
61 | mMaxFlingVelocity = viewConfig.getScaledMaximumFlingVelocity();
62 | }
63 |
64 | public boolean onInterceptTouchEvent(@NonNull MotionEvent ev) {
65 | final int action = MotionEventCompat.getActionMasked(ev);
66 | switch (action) {
67 | case MotionEvent.ACTION_DOWN:
68 | type = -1;
69 | return onDownEvent(ev);
70 | case MotionEvent.ACTION_MOVE:
71 | mVelocityTracker.addMovement(ev);
72 | return checkForResizing(ev);
73 | case MotionEvent.ACTION_UP:
74 | finishMotionEvent();
75 | if (type == LEFT) {
76 | mCalendarView.prev();
77 | } else if (type == RIGHT) {
78 | mCalendarView.next();
79 | }
80 | return false;
81 | }
82 | return false;
83 | }
84 |
85 | public boolean onTouchEvent(@NonNull MotionEvent event) {
86 | final int action = MotionEventCompat.getActionMasked(event);
87 | if (action == MotionEvent.ACTION_MOVE) {
88 | mVelocityTracker.addMovement(event);
89 | }
90 | if (mState == State.DRAGGING) {
91 | switch (action) {
92 | case MotionEvent.ACTION_MOVE:
93 | int deltaY = calculateDistanceForDrag(event);
94 | mProgressManager.applyDelta(deltaY);
95 | break;
96 | case MotionEvent.ACTION_UP:
97 | finishMotionEvent();
98 | if (type == LEFT) {
99 | mCalendarView.prev();
100 | } else if (type == RIGHT) {
101 | mCalendarView.next();
102 | }
103 | break;
104 | }
105 |
106 | } else if (action == MotionEvent.ACTION_MOVE) {
107 | checkForResizing(event);
108 | } else if (action == MotionEvent.ACTION_UP) {
109 | if (type == LEFT) {
110 | mCalendarView.prev();
111 | } else if (type == RIGHT) {
112 | mCalendarView.next();
113 | }
114 | }
115 |
116 | return true;
117 | }
118 |
119 | /**
120 | * Triggered
121 | * @param event Down event
122 | */
123 | private boolean onDownEvent(@NonNull MotionEvent event) {
124 | if (MotionEventCompat.getActionMasked(event) != MotionEvent.ACTION_DOWN) {
125 | throw new IllegalStateException("Has to be down event!");
126 | }
127 | if (mVelocityTracker == null) {
128 | mVelocityTracker = VelocityTracker.obtain();
129 | } else {
130 | mVelocityTracker.clear();
131 | }
132 | mDownY = event.getY();
133 | mDownX = event.getX();
134 |
135 | if (!mScroller.isFinished()) {
136 | mScroller.forceFinished(true);
137 | if (mScroller.getFinalY() == 0) {
138 | mDragStartY = mDownY + mScroller.getStartY() - mScroller.getCurrY();
139 | } else {
140 | mDragStartY = mDownY - mScroller.getCurrY();
141 | }
142 | mState = State.DRAGGING;
143 | return true;
144 | } else {
145 | return false;
146 | }
147 |
148 | }
149 |
150 | public void recycle() {
151 | if (mVelocityTracker != null) {
152 | mVelocityTracker.recycle();
153 | mVelocityTracker = null;
154 | }
155 | }
156 |
157 | public boolean checkForResizing(MotionEvent ev) { // FIXME this method should only return true / false. Make another method for starting animation
158 | if (mState == State.DRAGGING) {
159 | return true;
160 | }
161 |
162 | final float yDIff = calculateDistance(ev);
163 | if (Math.abs(calculateXDistanse(ev)) > Math.abs(calculateDistance(ev))) {
164 | if (calculateXDistanse(ev) > 100) {
165 | type = LEFT;
166 | } else if (calculateXDistanse(ev) < -100) {
167 | type = RIGHT;
168 | }
169 | } else {
170 | CalendarManager manager = mCalendarView.getManager();
171 | CalendarManager.State state = manager.getState();
172 | if (Math.abs(yDIff) > mTouchSlop) { // FIXME this should happen only if dragging int right direction
173 | mState = State.DRAGGING;
174 | mDragStartY = ev.getY();
175 | if (mProgressManager == null) {
176 | int weekOfMonth = manager.getWeekOfMonth();
177 | if (state == CalendarManager.State.WEEK) { // always animate in month view
178 | manager.toggleView();
179 | mCalendarView.populateLayout();
180 | }
181 | mProgressManager = new ProgressManagerImpl(mCalendarView, weekOfMonth, state == CalendarManager.State.MONTH);
182 | }
183 | return true;
184 | }
185 | }
186 | return false;
187 | }
188 |
189 | private void finishMotionEvent() {
190 | if (mProgressManager != null) {
191 | if (mProgressManager.isInitialized()) {
192 | startScolling();
193 | }
194 | }
195 | }
196 |
197 | private void startScolling() {
198 |
199 | mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
200 | int velocity = (int) mVelocityTracker.getYVelocity();
201 |
202 | if (!mScroller.isFinished()) {
203 | mScroller.forceFinished(true);
204 | }
205 |
206 | int progress = mProgressManager.getCurrentHeight();
207 | int end;
208 | if (Math.abs(velocity) > mMinFlingVelocity) {
209 |
210 | if (velocity > 0) {
211 | end = mProgressManager.getEndSize() - progress;
212 | } else {
213 | end = -progress;
214 | }
215 |
216 | } else {
217 |
218 | int endSize = mProgressManager.getEndSize();
219 | if (endSize / 2 <= progress) {
220 | end = endSize - progress;
221 | } else {
222 | end = -progress;
223 | }
224 |
225 | }
226 |
227 | mScroller.startScroll(0, progress, 0, end);
228 | mCalendarView.postInvalidate();
229 |
230 | mState = State.SETTLING;
231 |
232 | }
233 |
234 | private float calculateDistance(MotionEvent event) {
235 | return (event.getY() - mDownY);
236 | }
237 | /**
238 | * 计算X方向移动距离
239 | * @param event
240 | * @return
241 | */
242 | private int calculateXDistanse(MotionEvent event){
243 | return (int) (event.getX() - mDownX);
244 | }
245 |
246 | private int calculateDistanceForDrag(MotionEvent event) {
247 | return (int) (event.getY() - mDragStartY);
248 | }
249 |
250 | public void onDraw() {
251 | if (!mScroller.isFinished()) {
252 | mScroller.computeScrollOffset();
253 | float position = mScroller.getCurrY() * 1f / mProgressManager.getEndSize();
254 | mProgressManager.apply(position);
255 | mCalendarView.postInvalidate();
256 | } else if (mState == State.SETTLING) {
257 | mState = State.IDLE;
258 | float position = mScroller.getCurrY() * 1f / mProgressManager.getEndSize();
259 | mProgressManager.finish(position > 0);
260 | mProgressManager = null;
261 | }
262 |
263 | }
264 |
265 | private enum State {
266 | IDLE,
267 | DRAGGING,
268 | SETTLING
269 | }
270 | }
271 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/manager/Week.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.manager;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 |
6 | import org.joda.time.LocalDate;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /**
12 | * Created by Blaz Solar on 24/02/14.
13 | */
14 | public class Week extends RangeUnit {
15 |
16 | @NonNull
17 | private final List mDays = new ArrayList(7);
18 |
19 | public Week(@NonNull LocalDate date, @NonNull LocalDate today, @Nullable LocalDate minDate,
20 | @Nullable LocalDate maxDate) {
21 | super(
22 | date.withDayOfWeek(1),
23 | date.withDayOfWeek(7),
24 | today,
25 | minDate,
26 | maxDate
27 | );
28 |
29 | build();
30 | }
31 |
32 | @Override
33 | public boolean hasNext() {
34 | LocalDate maxDate = getMaxDate();
35 | if (maxDate == null) {
36 | return true;
37 | } else {
38 | return maxDate.isAfter(mDays.get(6).getDate());
39 | }
40 | }
41 |
42 | @Override
43 | public boolean hasPrev() {
44 | LocalDate minDate = getMinDate();
45 | if (minDate == null) {
46 | return true;
47 | } else {
48 | return minDate.isBefore(mDays.get(0).getDate());
49 | }
50 | }
51 |
52 | @Override
53 | public boolean next() {
54 | if (hasNext()) {
55 | setFrom(getFrom().plusWeeks(1));
56 | setTo(getTo().plusWeeks(1));
57 | build();
58 | return true;
59 | } else {
60 | return false;
61 | }
62 | }
63 |
64 | @Override
65 | public boolean prev() {
66 | if (hasPrev()) {
67 | setFrom(getFrom().minusWeeks(1));
68 | setTo(getTo().minusWeeks(1));
69 | build();
70 | return true;
71 | } else {
72 | return false;
73 | }
74 | }
75 |
76 | @Override
77 | public int getType() {
78 | return TYPE_WEEK;
79 | }
80 |
81 | @Override
82 | public void deselect(@NonNull LocalDate date) {
83 | if (date != null && getFrom().compareTo(date) <= 0 &&
84 | getTo().compareTo(date) >= 0) {
85 | setSelected(false);
86 |
87 | for (Day day : mDays) {
88 | day.setSelected(false);
89 | }
90 | }
91 | }
92 |
93 | @Override
94 | public boolean select(@NonNull LocalDate date) {
95 | if (date != null && getFrom().compareTo(date) <= 0 &&
96 | getTo().compareTo(date) >= 0) {
97 | setSelected(true);
98 |
99 | for (Day day : mDays) {
100 | day.setSelected(day.getDate().isEqual(date));
101 | }
102 | return true;
103 | } else {
104 | return false;
105 | }
106 | }
107 |
108 | @NonNull
109 | public List getDays() {
110 | return mDays;
111 | }
112 |
113 | @Override
114 | public void build() {
115 |
116 | mDays.clear();
117 |
118 | LocalDate date = getFrom();
119 | for(; date.compareTo(getTo()) <= 0; date = date.plusDays(1)) {
120 | Day day = new Day(date, date.equals(getToday()));
121 | day.setEnabled(isDayEnabled(date));
122 | mDays.add(day);
123 | }
124 |
125 | }
126 |
127 | private boolean isDayEnabled(@NonNull LocalDate date) {
128 |
129 | LocalDate minDate = getMinDate();
130 | if (minDate != null && date.isBefore(minDate)) {
131 | return false;
132 | }
133 |
134 | LocalDate maxDate = getMaxDate();
135 | if (maxDate != null && date.isAfter(maxDate)) {
136 | return false;
137 | }
138 |
139 | return true;
140 | }
141 |
142 | @Nullable
143 | @Override
144 | LocalDate getFirstDateOfCurrentMonth(@NonNull LocalDate currentMonth) {
145 |
146 | if (currentMonth != null) {
147 | int year = currentMonth.getYear();
148 | int month = currentMonth.getMonthOfYear();
149 |
150 | LocalDate date = getFrom();
151 | for (; date.compareTo(getTo()) <= 0; date = date.plusDays(1)) {
152 | int fromYear = date.getYear();
153 | int fromMonth = date.getMonthOfYear();
154 |
155 | if (year == fromYear && month == fromMonth) {
156 | return date;
157 | }
158 | }
159 | }
160 |
161 | return null;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/models/AbstractViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.models;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * Created by Blaz Solar on 16/04/14.
7 | */
8 | public abstract class AbstractViewHolder {
9 |
10 | private View view;
11 | private float delay;
12 | private float duration;
13 |
14 | private boolean mAnimating;
15 |
16 | public void animate(float time) {
17 | if (shouldAnimate(time)) {
18 | mAnimating = true;
19 | onAnimate(getRelativeTime(time));
20 | } else if (mAnimating) {
21 | mAnimating = false;
22 | onFinish(Math.round(getRelativeTime(time)) >= 1);
23 | } else if (delay > time) { // FIXME this should only be called once
24 | onFinish(false);
25 | } else if (time > getEnd()) { // FIXME this should only be called once
26 | onFinish(true);
27 | }
28 | }
29 |
30 | public void setView(View view) {
31 | this.view = view;
32 | }
33 |
34 | public void setDelay(float delay) {
35 | this.delay = delay;
36 | }
37 |
38 | public void setDuration(float duration) {
39 | this.duration = duration;
40 | }
41 |
42 | public View getView() {
43 | return view;
44 | }
45 |
46 | public float getDelay() {
47 | return delay;
48 | }
49 |
50 | public float getDuration() {
51 | return duration;
52 | }
53 |
54 | public abstract void onFinish(boolean done);
55 |
56 | protected float getEnd() {
57 | return delay + duration;
58 | }
59 |
60 | protected boolean shouldAnimate(float time) {
61 | return delay <= time && time <= getEnd();
62 | }
63 |
64 | protected abstract void onAnimate(float time);
65 |
66 | protected float getRelativeTime(float time) {
67 | return (time - getDelay()) * 1f / getDuration();
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/models/SizeViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.models;
2 |
3 | import android.view.View;
4 | import android.view.ViewGroup;
5 |
6 | /**
7 | * Created by Blaz Solar on 17/04/14.
8 | */
9 | public class SizeViewHolder extends AbstractViewHolder {
10 |
11 | private int mMinHeight;
12 | private int mMaxHeight;
13 |
14 | public SizeViewHolder(int minHeight, int maxHeight) {
15 | mMinHeight = minHeight;
16 | mMaxHeight = maxHeight;
17 | }
18 |
19 | public int getHeight() {
20 | return mMaxHeight - mMinHeight;
21 | }
22 |
23 | public void setMinHeight(int minHeight) {
24 | mMinHeight = minHeight;
25 | }
26 |
27 | public int getMaxHeight() {
28 | return mMaxHeight;
29 | }
30 |
31 | public int getMinHeight() {
32 | return mMinHeight;
33 | }
34 |
35 | public void setMaxHeight(int maxHeight) {
36 | mMaxHeight = maxHeight;
37 | }
38 |
39 | @Override
40 | public void onFinish(boolean done) {
41 | if (done) {
42 | onShown();
43 | } else {
44 | onHidden();
45 | }
46 | }
47 |
48 | public void onShown() {
49 | getView().getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
50 | getView().setVisibility(View.VISIBLE);
51 | }
52 |
53 | public void onHidden() {
54 | getView().getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
55 | getView().setVisibility(View.GONE);
56 | }
57 |
58 | @Override
59 | protected void onAnimate(float time) {
60 | View view = getView();
61 | view.setVisibility(View.VISIBLE);
62 | view.getLayoutParams().height = (int) (getMinHeight() + getHeight() * time);
63 | view.requestLayout();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/models/StubViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.models;
2 |
3 | /**
4 | * Created by Blaz Solar on 22/04/14.
5 | */
6 | public class StubViewHolder extends AbstractViewHolder {
7 |
8 | @Override
9 | protected void onAnimate(float time) {
10 | // do nothing
11 | }
12 |
13 | @Override
14 | public void onFinish(boolean done) {
15 | // do nothing
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/utils/ChinaDate.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.utils;
2 |
3 | import java.text.SimpleDateFormat;
4 | import java.util.ArrayList;
5 | import java.util.Calendar;
6 | import java.util.Date;
7 | import java.util.GregorianCalendar;
8 | import java.util.HashMap;
9 | import java.util.Locale;
10 |
11 | /**
12 | * @author jty
13 | * @date 2015年5月22日
14 | */
15 | public class ChinaDate {
16 | final private static long[] lunarInfo = new long[] { 0x04bd8, 0x04ae0,
17 | 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0,
18 | 0x055d2, 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540,
19 | 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, 0x04970, 0x0a4b0, 0x0b4b5,
20 | 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,
21 | 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3,
22 | 0x092e0, 0x1c8d7, 0x0c950, 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0,
23 | 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, 0x06ca0,
24 | 0x0b550, 0x15355, 0x04da0, 0x0a5d0, 0x14573, 0x052d0, 0x0a9a8,
25 | 0x0e950, 0x06aa0, 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570,
26 | 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0, 0x04dd5,
27 | 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0,
28 | 0x195a6, 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50,
29 | 0x06d40, 0x0af46, 0x0ab60, 0x09570, 0x04af5, 0x04970, 0x064b0,
30 | 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0,
31 | 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7,
32 | 0x025d0, 0x092d0, 0x0cab5, 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50,
33 | 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, 0x07954,
34 | 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260,
35 | 0x0ea65, 0x0d530, 0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0,
36 | 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0, 0x056d0,
37 | 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20,
38 | 0x0ada0 };
39 |
40 | //final private static int[] year20 = new int[] { 1, 4, 1, 2, 1, 2, 1, 1, 2,
41 | // 1, 2, 1 };
42 |
43 |
44 | //final private static int[] year2000 = new int[] { 0, 3, 1, 2, 1, 2, 1, 1,
45 | // 2, 1, 2, 1 };
46 |
47 | public final static String[] nStr1 = new String[] { "", "正", "二", "三", "四",
48 | "五", "六", "七", "八", "九", "十", "十一", "十二" };
49 |
50 | private final static String[] Gan = new String[] { "甲", "乙", "丙", "丁", "戊",
51 | "己", "庚", "辛", "壬", "癸" };
52 |
53 | private final static String[] Zhi = new String[] { "子", "丑", "寅", "卯", "辰",
54 | "巳", "午", "未", "申", "酉", "戌", "亥" };
55 |
56 | private final static String[] Animals = new String[] { "鼠", "牛", "虎", "兔",
57 | "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪" };
58 |
59 | //private final static String[] solarTerm = new String[] { "小寒", "大寒", "立春",
60 | // "雨水", "惊蛰", "春分", "清明", "谷雨", "立夏", "小满", "芒种", "夏至", "小暑", "大暑",
61 | // "立秋", "处暑", "白露", "秋分", "寒露", "霜降", "立冬", "小雪", "大雪", "冬至" };
62 |
63 | //private final static String[] sFtv = new String[] { "0101*元旦", "0214 情人节",
64 | // "0308 妇女节", "0312 植树节", "0315 消费者权益日", "0401 愚人节", "0501 劳动节",
65 | // "0504 青年节", "0512 护士节", "0601 儿童节", "0701 建党节", "0801 建军节",
66 | // "0808 父亲节", "0909 毛泽东逝世纪念", "0910 教师节", "0928 孔子诞辰", "1001*国庆节",
67 | // "1006 老人节", "1024 联合国日", "1112 孙中山诞辰", "1220 澳门回归", "1225 圣诞节",
68 | // "1226 毛泽东诞辰" };
69 |
70 | //private final static String[] lFtv = new String[] { "0101*农历春节",
71 | // "0115 元宵节", "0505 端午节", "0707 七夕情人节", "0815 中秋节", "0909 重阳节",
72 | // "1208 腊八节", "1224 小年", "0100*除夕" };
73 |
74 | /**
75 | * 传回农历 y年的总天数
76 | *
77 | * @param y
78 | * @return
79 | */
80 | final private static int lYearDays(int y) {
81 | int i, sum = 348;
82 | for (i = 0x8000; i > 0x8; i >>= 1) {
83 | if ((lunarInfo[y - 1900] & i) != 0)
84 | sum += 1;
85 | }
86 | return (sum + leapDays(y));
87 | }
88 |
89 | /**
90 | * 传回农历 y年闰月的天数
91 | *
92 | * @param y
93 | * @return
94 | */
95 | final private static int leapDays(int y) {
96 | if (leapMonth(y) != 0) {
97 | if ((lunarInfo[y - 1900] & 0x10000) != 0)
98 | return 30;
99 | else
100 | return 29;
101 | } else
102 | return 0;
103 | }
104 |
105 | /**
106 | * 传回农历 y年闰哪个月 1-12 , 没闰传回 0
107 | *
108 | * @param y
109 | * @return
110 | */
111 | final private static int leapMonth(int y) {
112 | return (int) (lunarInfo[y - 1900] & 0xf);
113 | }
114 |
115 | /**
116 | * 传回农历 y年m月的总天数
117 | *
118 | * @param y
119 | * @param m
120 | * @return
121 | */
122 | final private static int monthDays(int y, int m) {
123 | if ((lunarInfo[y - 1900] & (0x10000 >> m)) == 0)
124 | return 29;
125 | else
126 | return 30;
127 | }
128 |
129 | /**
130 | * 传回农历 y年的生肖
131 | *
132 | * @param y
133 | * @return
134 | */
135 | final public static String AnimalsYear(int y) {
136 | return Animals[(y - 4) % 12];
137 | }
138 |
139 | /**
140 | * 传入 月日的offset 传回干支,0=甲子
141 | *
142 | * @param num
143 | * @return
144 | */
145 | final private static String cyclicalm(int num) {
146 | return (Gan[num % 10] + Zhi[num % 12]);
147 | }
148 |
149 | /**
150 | * 传入 offset 传回干支, 0=甲子
151 | *
152 | * @param y
153 | * @return
154 | */
155 | final public static String cyclical(int y) {
156 | int num = y - 1900 + 36;
157 | return (cyclicalm(num));
158 | }
159 |
160 | /**
161 | * 传出农历.year0 .month1 .day2 .yearCyl3 .monCyl4 .dayCyl5 .isLeap6
162 | *
163 | * @param y
164 | * @param m
165 | * @return
166 | */
167 | //final private long[] Lunar(int y, int m) {
168 | // long[] nongDate = new long[7];
169 | // int i = 0, temp = 0, leap = 0;
170 | // Date baseDate = new GregorianCalendar(1900 + 1900, 1, 31).getTime();
171 | // Date objDate = new GregorianCalendar(y + 1900, m, 1).getTime();
172 | // long offset = (objDate.getTime() - baseDate.getTime()) / 86400000L;
173 | // if (y < 2000)
174 | // offset += year19[m - 1];
175 | // if (y > 2000)
176 | // offset += year20[m - 1];
177 | // if (y == 2000)
178 | // offset += year2000[m - 1];
179 | // nongDate[5] = offset + 40;
180 | // nongDate[4] = 14;
181 | //
182 | // for (i = 1900; i < 2050 && offset > 0; i++) {
183 | // temp = lYearDays(i);
184 | // offset -= temp;
185 | // nongDate[4] += 12;
186 | // }
187 | // if (offset < 0) {
188 | // offset += temp;
189 | // i--;
190 | // nongDate[4] -= 12;
191 | // }
192 | // nongDate[0] = i;
193 | // nongDate[3] = i - 1864;
194 | // leap = leapMonth(i); // 闰哪个月
195 | // nongDate[6] = 0;
196 | //
197 | // for (i = 1; i < 13 && offset > 0; i++) {
198 | // // 闰月
199 | // if (leap > 0 && i == (leap + 1) && nongDate[6] == 0) {
200 | // --i;
201 | // nongDate[6] = 1;
202 | // temp = leapDays((int) nongDate[0]);
203 | // } else {
204 | // temp = monthDays((int) nongDate[0], i);
205 | // }
206 | //
207 | // // 解除闰月
208 | // if (nongDate[6] == 1 && i == (leap + 1))
209 | // nongDate[6] = 0;
210 | // offset -= temp;
211 | // if (nongDate[6] == 0)
212 | // nongDate[4]++;
213 | // }
214 | //
215 | // if (offset == 0 && leap > 0 && i == leap + 1) {
216 | // if (nongDate[6] == 1) {
217 | // nongDate[6] = 0;
218 | // } else {
219 | // nongDate[6] = 1;
220 | // --i;
221 | // --nongDate[4];
222 | // }
223 | // }
224 | // if (offset < 0) {
225 | // offset += temp;
226 | // --i;
227 | // --nongDate[4];
228 | // }
229 | // nongDate[1] = i;
230 | // nongDate[2] = offset + 1;
231 | // return nongDate;
232 | //}
233 |
234 | /**
235 | * 传出y年m月d日对应的农历.year0 .month1 .day2 .yearCyl3 .monCyl4 .dayCyl5 .isLeap6
236 | *
237 | * @param y
238 | * @param m
239 | * @param d
240 | * @return
241 | */
242 | final public static long[] calElement(int y, int m, int d) {
243 | long[] nongDate = new long[7];
244 | int i = 0, temp = 0, leap = 0;
245 | Date baseDate = new GregorianCalendar(0 + 1900, 0, 31).getTime();
246 | Date objDate = new GregorianCalendar(y, m - 1, d).getTime();
247 | long offset = (objDate.getTime() - baseDate.getTime()) / 86400000L;
248 | nongDate[5] = offset + 40;
249 | nongDate[4] = 14;
250 |
251 | for (i = 1900; i < 2050 && offset > 0; i++) {
252 | temp = lYearDays(i);
253 | offset -= temp;
254 | nongDate[4] += 12;
255 | }
256 | if (offset < 0) {
257 | offset += temp;
258 | i--;
259 | nongDate[4] -= 12;
260 | }
261 | nongDate[0] = i;
262 | nongDate[3] = i - 1864;
263 | leap = leapMonth(i); // 闰哪个月,没闰返回0
264 | nongDate[6] = 0;
265 |
266 | for (i = 1; i < 13 && offset > 0; i++) {
267 | // 闰月
268 | if (leap > 0 && i == (leap + 1) && nongDate[6] == 0) {
269 | --i;
270 | nongDate[6] = 1;
271 | temp = leapDays((int) nongDate[0]);
272 | } else {
273 | temp = monthDays((int) nongDate[0], i);
274 | }
275 |
276 | // 解除闰月
277 | if (nongDate[6] == 1 && i == (leap + 1))
278 | nongDate[6] = 0;
279 | offset -= temp;
280 | if (nongDate[6] == 0)
281 | nongDate[4]++;
282 | }
283 |
284 | if (offset == 0 && leap > 0 && i == leap + 1) {
285 | if (nongDate[6] == 1) {
286 | nongDate[6] = 0;
287 | } else {
288 | nongDate[6] = 1;
289 | --i;
290 | --nongDate[4];
291 | }
292 | }
293 | if (offset < 0) {
294 | offset += temp;
295 | --i;
296 | --nongDate[4];
297 | }
298 | nongDate[1] = i;
299 | nongDate[2] = offset + 1;
300 | return nongDate;
301 | }
302 |
303 | /**
304 | * 传入天数可以 得到旧历的显示
305 | * @param day
306 | * @return
307 | */
308 | public final static String getChinaDate(int day) {
309 | String a = "";
310 | if (day == 10)
311 | return "初十";
312 | if (day == 20)
313 | return "二十";
314 | if (day == 30)
315 | return "卅十";
316 | int two = (int) ((day) / 10);
317 | if (two == 0)
318 | a = "初";
319 | if (two == 1)
320 | a = "十";
321 | if (two == 2)
322 | a = "廿";
323 | if (two == 3)
324 | a = "卅";
325 | int one = (int) (day % 10);
326 | switch (one) {
327 | case 1:
328 | a += "一";
329 | break;
330 | case 2:
331 | a += "二";
332 | break;
333 | case 3:
334 | a += "三";
335 | break;
336 | case 4:
337 | a += "四";
338 | break;
339 | case 5:
340 | a += "五";
341 | break;
342 | case 6:
343 | a += "六";
344 | break;
345 | case 7:
346 | a += "七";
347 | break;
348 | case 8:
349 | a += "八";
350 | break;
351 | case 9:
352 | a += "九";
353 | break;
354 | }
355 | return a;
356 | }
357 |
358 | public static String today() {
359 | Calendar today = Calendar.getInstance(Locale.SIMPLIFIED_CHINESE);
360 | int year = today.get(Calendar.YEAR);
361 | int month = today.get(Calendar.MONTH) + 1;
362 | int date = today.get(Calendar.DATE);
363 | long[] l = calElement(year, month, date);
364 | StringBuffer sToday = new StringBuffer();
365 | try {
366 | sToday.append(sdf.format(today.getTime()));
367 | sToday.append(" 农历");
368 | sToday.append(cyclical(year));
369 | sToday.append('(');
370 | sToday.append(AnimalsYear(year));
371 | sToday.append(")年");
372 | sToday.append(nStr1[(int) l[1]]);
373 | sToday.append("月");
374 | sToday.append(getChinaDate((int) (l[2])));
375 | return sToday.toString();
376 | } finally {
377 | sToday = null;
378 | }
379 | }
380 |
381 | private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy年M月d日 EEEEE");
382 |
383 | /**
384 | * 农历日历工具使用演示
385 | *
386 | * @param args
387 | */
388 | public static void test() {
389 | Calendar cal= Calendar.getInstance();
390 | cal.set(Calendar.MONTH, 3);
391 | cal.set(Calendar.DATE, 1);
392 | long[] l;
393 | for(int i=0;i<100;i++){
394 | l = calElement(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DATE));
395 | StringBuffer sToday = new StringBuffer();
396 | sToday.append(sdf.format(cal.getTime()));
397 | sToday.append(" 农历");
398 | sToday.append(cyclical(cal.get(Calendar.YEAR)));
399 | sToday.append('(');
400 | sToday.append(AnimalsYear(cal.get(Calendar.YEAR)));
401 | sToday.append(")年");
402 | sToday.append(nStr1[(int) l[1]]);
403 | sToday.append("月");
404 | sToday.append(getChinaDate((int) (l[2])));
405 |
406 | cal.add(Calendar.DATE, 1);
407 | }
408 | }
409 |
410 | public static ArrayList> getChinaDates(int currentyear, int month){
411 | SimpleDateFormat myfmt = new SimpleDateFormat("yyyy-MM-dd");
412 | ArrayList> list=new ArrayList>();
413 | Calendar cal= Calendar.getInstance(); //Locale.SIMPLIFIED_CHINESE
414 | cal.set(Calendar.YEAR, currentyear);
415 | cal.set(Calendar.MONTH, month);
416 | cal.set(Calendar.DATE, 1);
417 | long[] l;
418 | HashMap map;
419 | while(cal.get(Calendar.MONTH)==month){
420 | l = calElement(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DATE));
421 | map=new HashMap();
422 | map.put("date", myfmt.format(cal.getTime()));
423 | String chinadate=getChinaDate((int) (l[2]));
424 | if(chinadate.equals("初一")){
425 | chinadate=nStr1[(int) l[1]]+"月";
426 | }
427 | map.put("chinadate", chinadate);
428 | list.add(map);
429 | cal.add(Calendar.DATE, 1);
430 | }
431 | return list;
432 | }
433 |
434 |
435 | }
436 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/widget/DayView.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.widget.TextView;
6 |
7 | import com.android.calendarlibrary.R;
8 |
9 |
10 | /**
11 | * Created by Blaz Solar on 24/05/14.
12 | */
13 | public class DayView extends TextView {
14 |
15 | private static final int[] STATE_CURRENT = { R.attr.state_current };
16 |
17 | private boolean mCurrent;
18 |
19 | public DayView(Context context) {
20 | super(context);
21 | }
22 |
23 | public DayView(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | }
26 |
27 | public DayView(Context context, AttributeSet attrs, int defStyle) {
28 | super(context, attrs, defStyle);
29 | }
30 |
31 | public void setCurrent(boolean current) {
32 | if (mCurrent != current) {
33 | mCurrent = current;
34 | refreshDrawableState();
35 | }
36 | }
37 |
38 | public boolean isCurrent() {
39 | return mCurrent;
40 | }
41 |
42 | @Override
43 | protected int[] onCreateDrawableState(int extraSpace) {
44 | final int[] state = super.onCreateDrawableState(extraSpace + 1);
45 |
46 | if (mCurrent) {
47 | mergeDrawableStates(state, STATE_CURRENT);
48 | }
49 |
50 | return state;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/java/com/android/calendarlibrary/widget/WeekView.java:
--------------------------------------------------------------------------------
1 | package com.android.calendarlibrary.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 |
8 | /**
9 | * Created by Blaz Solar on 24/02/14.
10 | */
11 | public class WeekView extends ViewGroup {
12 |
13 | public static final String TAG = "WeekView";
14 |
15 | public WeekView(Context context, AttributeSet attrs) {
16 | super(context, attrs);
17 | }
18 |
19 | @Override
20 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
21 |
22 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
23 |
24 | int maxSize = widthSize / 7;
25 | int baseSize = 0;
26 |
27 | int cnt = getChildCount();
28 | for(int i = 0; i < cnt; i++) {
29 |
30 | View child = getChildAt(i);
31 |
32 | if(child.getVisibility() == View.GONE) {
33 | continue;
34 | }
35 |
36 | child.measure(
37 | MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST),
38 | MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST)
39 | );
40 |
41 | baseSize = Math.max(baseSize, child.getMeasuredHeight());
42 |
43 | }
44 |
45 | for (int i = 0; i < cnt; i++) {
46 |
47 | View child = getChildAt(i);
48 |
49 | if (child.getVisibility() == GONE) {
50 | continue;
51 | }
52 |
53 | child.measure(
54 | MeasureSpec.makeMeasureSpec(baseSize, MeasureSpec.EXACTLY),
55 | MeasureSpec.makeMeasureSpec(baseSize, MeasureSpec.EXACTLY)
56 | );
57 |
58 | }
59 |
60 | setMeasuredDimension(widthSize, getLayoutParams().height >= 0 ? getLayoutParams().height : baseSize + getPaddingBottom() + getPaddingTop());
61 |
62 | }
63 |
64 | @Override
65 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
66 |
67 | int cnt = getChildCount();
68 |
69 | int width = getMeasuredWidth();
70 | int part = width / cnt;
71 |
72 | for(int i = 0; i < cnt; i++) {
73 |
74 | View child = getChildAt(i);
75 | if(child.getVisibility() == View.GONE) {
76 | continue;
77 | }
78 |
79 | int childWidth = child.getMeasuredWidth();
80 |
81 | int x = i * part + ((part - childWidth) / 2);
82 | child.layout(x, 0, x + childWidth, child.getMeasuredHeight());
83 |
84 | }
85 |
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/color/text_calendar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/color/text_calendar_out_month.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/color/text_calendar_week.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/color/text_calendar_week_out_month.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/drawable-xxhdpi/ic_arrow_left_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenyongci/Android-Week-Calendar/7d8d27c67237158dbd04e36117240f2d31497101/calendarlibrary/src/main/res/drawable-xxhdpi/ic_arrow_left_normal.png
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/drawable-xxhdpi/ic_arrow_right_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenyongci/Android-Week-Calendar/7d8d27c67237158dbd04e36117240f2d31497101/calendarlibrary/src/main/res/drawable-xxhdpi/ic_arrow_right_normal.png
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/drawable/bg_btn_calendar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
6 |
7 |
8 |
9 |
10 | -
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/drawable/bg_btn_calendar_today.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 | -
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/drawable/bg_btn_calendar_today_selected.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 | -
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/drawable/bg_calendar_point_blue.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/drawable/bg_calendar_point_white.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/drawable/bg_day_type_blue.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/drawable/bg_day_type_gray.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/drawable/bg_day_type_green.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/drawable/bg_day_type_red.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/drawable/ic_arrow_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/drawable/ic_arrow_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/layout/calendar_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
9 |
10 |
15 |
16 |
24 |
25 |
34 |
35 |
43 |
44 |
45 |
46 |
58 |
59 |
60 |
61 |
66 |
67 |
76 |
77 |
86 |
87 |
96 |
97 |
106 |
107 |
116 |
117 |
126 |
127 |
136 |
137 |
138 |
139 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/layout/day_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
23 |
24 |
32 |
33 |
41 |
42 |
43 |
44 |
56 |
57 |
58 |
59 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/layout/week_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/values/color.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #4A4A4A
6 | #ababab
7 | #c0c0c0
8 | #88F37B7A
9 | #336F9C
10 | #77d4f6
11 | #F5F5F5
12 | #E9F6FF
13 | #9B9B9B
14 | #777777
15 | #FFFFFF
16 | #FFE54E36
17 | #3DDAA6
18 | #FFFC5C99
19 |
20 |
--------------------------------------------------------------------------------
/calendarlibrary/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Next
4 | Previous
5 |
6 |
7 | - 一
8 | - 二
9 | - 三
10 | - 四
11 | - 五
12 | - 六
13 | - 日
14 |
15 |
16 |
--------------------------------------------------------------------------------
/calendarlibrary/src/test/java/com/tuanhuo/android/calendarlibrary/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.tuanhuo.android.calendarlibrary;
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/chenyongci/Android-Week-Calendar/7d8d27c67237158dbd04e36117240f2d31497101/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jan 22 10:46:12 CST 2018
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/BMmh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenyongci/Android-Week-Calendar/7d8d27c67237158dbd04e36117240f2d31497101/screenshot/BMmh.png
--------------------------------------------------------------------------------
/screenshot/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenyongci/Android-Week-Calendar/7d8d27c67237158dbd04e36117240f2d31497101/screenshot/preview.gif
--------------------------------------------------------------------------------
/screenshot/shot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenyongci/Android-Week-Calendar/7d8d27c67237158dbd04e36117240f2d31497101/screenshot/shot.jpg
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':calendarlibrary'
2 |
--------------------------------------------------------------------------------