diff --git a/CHANGELOG.md b/CHANGELOG.md
index 39e6c2b0..ce26043e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,22 @@
Change Log
==========
+Version 1.3.0 *(2016-05-16)*
+----------------------------
+
+* New: MCV `goToNext` and `goToPrevious` API to programmatically trigger paging
+* New: Allow users to click on dates outside of current month with `setAllowClickDaysOutsideCurrentMonth`
+* New: Set tile width/height separately rather than single tile size
+* New: Attributes: mcv_tileWidth, mcv_tileHeight, mcv_calendarMode
+* Change: `CalendarMode.WEEK` officially marked `@Experimental`, use with caution
+* Change: `getTileSize` is deprecated, use `getTileWidth` and `getTileHeight`. `setTileSize` still works as a convenience method to set width and height at the same time.
+* Fix: Issue with arrow not enabled when setting maxDate
+* Fix: Issue with number of pages not calculated correctly with maxDate causing last page to be unreachable
+* Fix: TalkBack content descriptions for pager view, forward/back arrows, and ability to set them manually
+* Fix: Crash while in Week mode when `CalendarPagerAdapter#getItemPosition` is called
+* Fix: Calendar Mode is retained on restore instance state
+* Fix: Min/Max date range is retained on restore instance state
+* Issue: Week mode - Restore instance state shows the previous week of the one that was saved
+* Issue: Week mode - Some combinations of first day of week, min/max date can cause the last week not to be pagable
Version 1.2.1 *(2016-05-05)*
----------------------------
@@ -21,7 +38,7 @@ Version 1.1.0 *(2015-10-19)*
* New: Ability to disable month swiping with `setPagingEnabled()`
* Fix [#149](https://github.com/prolificinteractive/material-calendarview/issues/149):
save selected dates as a typed List instead of an array.
-* Change: Some preformance optimizations
+* Change: Some performance optimizations
Version 1.0.1 *(2015-09-30)*
----------------------------
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..02d2524f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2016 Prolific Interactive
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
deleted file mode 100644
index 07095092..00000000
--- a/LICENSE.txt
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright 2016 Prolific Interactive
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
\ No newline at end of file
diff --git a/README.md b/README.md
index 366e1a70..9f6dd3ad 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
+
Material Calendar View [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Material%20Calendar%20View-blue.svg?style=flat)](https://android-arsenal.com/details/1/1531)
======================
@@ -11,10 +11,11 @@ and feel, rather than 100% parity with the platform's implementation.
Usage
-----
-1. Add `compile 'com.prolificinteractive:material-calendarview:1.2.1'` to your dependencies.
+1. Add `compile 'com.prolificinteractive:material-calendarview:1.3.0'` to your dependencies.
2. Add `MaterialCalendarView` into your layouts or view hierarchy.
3. Set a `OnDateSelectedListener` or call `MaterialCalendarView.getSelectedDates()` when you need it.
+
[Javadoc Available Here](http://prolificinteractive.github.io/material-calendarview/)
Example:
@@ -29,6 +30,20 @@ Example:
app:mcv_selectionColor="#00F"
/>
```
+#### @Experimental
+`CalendarMode.WEEK` and all week mode functionality is officially marked `@Experimental`. All APIs
+marked `@Experimental` are subject to change quickly and should not be used in production code. They
+are allowed for testing and feedback.
+
+Major Change in 1.3.0
+---------------------
+* Breaking change: `getTileSize` is deprecated. Use `getTileWidth` or `getTileHeight`.
+* Added `goToNext` and `goToPrevious` API to programmatically trigger paging
+* Allow users to click on dates outside of current month with `setAllowClickDaysOutsideCurrentMonth`
+* Set tile width/height separately rather than single tile size with `setTileWidth` and `setTileHeight`
+* Attributes: mcv_tileWidth, mcv_tileHeight, mcv_calendarMode
+
+See other changes in the [CHANGELOG](/CHANGELOG.md).
Major Change in 1.2.0
---------------------
@@ -82,19 +97,14 @@ Contributing
Would you like to contribute? Fork us and send a pull request! Be sure to checkout our issues first.
-License
-=======
-
->Copyright 2016 Prolific Interactive
->
->Licensed under the Apache License, Version 2.0 (the "License");
->you may not use this file except in compliance with the License.
->You may obtain a copy of the License at
->
-> http://www.apache.org/licenses/LICENSE-2.0
->
->Unless required by applicable law or agreed to in writing, software
->distributed under the License is distributed on an "AS IS" BASIS,
->WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
->See the License for the specific language governing permissions and
->limitations under the License.
+## License
+
+Material Calendar View is Copyright (c) 2016 Prolific Interactive. It may be redistributed under the terms specified in the [LICENSE] file.
+
+[LICENSE]: /LICENSE
+
+## Maintainers
+
+![prolific](https://s3.amazonaws.com/prolificsitestaging/logos/Prolific_Logo_Full_Color.png)
+
+Material Calendar View is maintained and funded by Prolific Interactive. The names and logos are trademarks of Prolific Interactive.
diff --git a/build.gradle b/build.gradle
index 1aa30e02..df956abf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -12,7 +12,7 @@ allprojects {
repositories {
jcenter()
// Here for convience when testing new releases
- //maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
+// maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}
// Is Release Build?
ext.isReleaseVersion = has("release")
diff --git a/gradle.properties b/gradle.properties
index ad1a06c9..9481178d 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -18,8 +18,8 @@
# org.gradle.parallel=true
GROUP=com.prolificinteractive
-VERSION_NAME=1.2.1
-VERSION_CODE=13
+VERSION_NAME=1.3.0
+VERSION_CODE=14
POM_PACKAGING=aar
POM_NAME=Material CalendarView
diff --git a/images/hero.png b/images/hero.png
new file mode 100644
index 00000000..1f678e06
Binary files /dev/null and b/images/hero.png differ
diff --git a/images/ic_launcher-web.png b/images/ic_launcher-web.png
deleted file mode 100644
index 4a52a883..00000000
Binary files a/images/ic_launcher-web.png and /dev/null differ
diff --git a/library/build.gradle b/library/build.gradle
index 2071f030..2f3b9669 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -15,7 +15,7 @@ android {
}
dependencies {
- compile 'com.android.support:support-v4:23.1.1'
+ compile 'com.android.support:support-v4:23.4.0'
testCompile 'junit:junit:4.12'
}
diff --git a/library/src/main/java/com/prolificinteractive/materialcalendarview/CalendarMode.java b/library/src/main/java/com/prolificinteractive/materialcalendarview/CalendarMode.java
index 5555fbaa..da93e4e9 100644
--- a/library/src/main/java/com/prolificinteractive/materialcalendarview/CalendarMode.java
+++ b/library/src/main/java/com/prolificinteractive/materialcalendarview/CalendarMode.java
@@ -1,5 +1,6 @@
package com.prolificinteractive.materialcalendarview;
+@Experimental
public enum CalendarMode {
MONTHS(6),
diff --git a/library/src/main/java/com/prolificinteractive/materialcalendarview/CalendarPagerAdapter.java b/library/src/main/java/com/prolificinteractive/materialcalendarview/CalendarPagerAdapter.java
index e8c4997b..6926a377 100644
--- a/library/src/main/java/com/prolificinteractive/materialcalendarview/CalendarPagerAdapter.java
+++ b/library/src/main/java/com/prolificinteractive/materialcalendarview/CalendarPagerAdapter.java
@@ -12,7 +12,6 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Calendar;
import java.util.Collections;
import java.util.List;
@@ -40,7 +39,6 @@ abstract class CalendarPagerAdapter extends PagerAd
private DayFormatter dayFormatter = DayFormatter.DEFAULT;
private List decorators = new ArrayList<>();
private List decoratorResults = null;
- private int firstDayOfTheWeek = Calendar.SUNDAY;
private boolean selectionEnabled = true;
CalendarPagerAdapter(MaterialCalendarView mcv) {
@@ -85,13 +83,14 @@ public CalendarPagerAdapter> migrateStateAndReturn(CalendarPagerAdapter> new
newAdapter.color = color;
newAdapter.dateTextAppearance = dateTextAppearance;
newAdapter.weekDayTextAppearance = weekDayTextAppearance;
- newAdapter.dayFormatter = dayFormatter;
- newAdapter.decorators = decorators;
newAdapter.showOtherDates = showOtherDates;
newAdapter.minDate = minDate;
newAdapter.maxDate = maxDate;
newAdapter.selectedDates = selectedDates;
- newAdapter.firstDayOfTheWeek = firstDayOfTheWeek;
+ newAdapter.weekDayFormatter = weekDayFormatter;
+ newAdapter.dayFormatter = dayFormatter;
+ newAdapter.decorators = decorators;
+ newAdapter.decoratorResults = decoratorResults;
newAdapter.selectionEnabled = selectionEnabled;
return newAdapter;
}
@@ -122,9 +121,9 @@ public int getItemPosition(Object object) {
if (!(isInstanceOfView(object))) {
return POSITION_NONE;
}
- MonthView monthView = (MonthView) object;
- CalendarDay month = monthView.getMonth();
- if (month == null) {
+ CalendarPagerView pagerView = (CalendarPagerView) object;
+ CalendarDay firstViewDay = pagerView.getFirstViewDay();
+ if (firstViewDay == null) {
return POSITION_NONE;
}
int index = indexOf((V) object);
@@ -137,6 +136,7 @@ public int getItemPosition(Object object) {
@Override
public Object instantiateItem(ViewGroup container, int position) {
V pagerView = createView(position);
+ pagerView.setContentDescription(mcv.getCalendarContentDescription());
pagerView.setAlpha(0);
pagerView.setSelectionEnabled(selectionEnabled);
@@ -164,13 +164,6 @@ public Object instantiateItem(ViewGroup container, int position) {
return pagerView;
}
- public void setFirstDayOfWeek(int day) {
- firstDayOfTheWeek = day;
- for (V pagerView : currentViews) {
- pagerView.setFirstDayOfWeek(firstDayOfTheWeek);
- }
- }
-
public void setSelectionEnabled(boolean enabled) {
selectionEnabled = enabled;
for (V pagerView : currentViews) {
@@ -327,8 +320,4 @@ protected int getDateTextAppearance() {
protected int getWeekDayTextAppearance() {
return weekDayTextAppearance == null ? 0 : weekDayTextAppearance;
}
-
- public int getFirstDayOfWeek() {
- return firstDayOfTheWeek;
- }
}
diff --git a/library/src/main/java/com/prolificinteractive/materialcalendarview/CalendarPagerView.java b/library/src/main/java/com/prolificinteractive/materialcalendarview/CalendarPagerView.java
index 686c9526..60ff7de1 100644
--- a/library/src/main/java/com/prolificinteractive/materialcalendarview/CalendarPagerView.java
+++ b/library/src/main/java/com/prolificinteractive/materialcalendarview/CalendarPagerView.java
@@ -24,6 +24,8 @@
abstract class CalendarPagerView extends ViewGroup implements View.OnClickListener {
protected static final int DEFAULT_DAYS_IN_WEEK = 7;
+ protected static final int DEFAULT_MAX_WEEKS = 6;
+ protected static final int DAY_NAMES_ROW = 1;
private static final Calendar tempWorkingCalendar = CalendarUtils.getInstance();
private final ArrayList weekDayViews = new ArrayList<>();
private final ArrayList decoratorResults = new ArrayList<>();
@@ -90,26 +92,6 @@ protected int getFirstDayOfWeek() {
return firstDayOfWeek;
}
- public void setFirstDayOfWeek(int dayOfWeek) {
- this.firstDayOfWeek = dayOfWeek;
-
- Calendar calendar = resetAndGetWorkingCalendar();
- calendar.set(DAY_OF_WEEK, dayOfWeek);
- for (WeekDayView dayView : weekDayViews) {
- dayView.setDayOfWeek(calendar);
- calendar.add(DATE, 1);
- }
-
- calendar = resetAndGetWorkingCalendar();
- for (DayView dayView : dayViews) {
- CalendarDay day = CalendarDay.from(calendar);
- dayView.setDay(day);
- calendar.add(DATE, 1);
- }
-
- updateUi();
- }
-
protected abstract void buildDayViews(Collection dayViews, Calendar calendar);
protected abstract boolean isDayEnabled(CalendarDay day);
@@ -207,8 +189,8 @@ protected void invalidateDecorators() {
@Override
public void onClick(View v) {
if (v instanceof DayView) {
- DayView dayView = (DayView) v;
- mcv.onDateClicked(dayView.getDate(), !dayView.isChecked());
+ final DayView dayView = (DayView) v;
+ mcv.onDateClicked(dayView);
}
}
@@ -240,22 +222,24 @@ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec
}
//The spec width should be a correct multiple
- final int measureTileSize = specWidthSize / DEFAULT_DAYS_IN_WEEK;
+ final int measureTileWidth = specWidthSize / DEFAULT_DAYS_IN_WEEK;
+ final int measureTileHeight = specHeightSize / getRows();
//Just use the spec sizes
setMeasuredDimension(specWidthSize, specHeightSize);
int count = getChildCount();
+
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
- measureTileSize,
+ measureTileWidth,
MeasureSpec.EXACTLY
);
int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
- measureTileSize,
+ measureTileHeight,
MeasureSpec.EXACTLY
);
@@ -263,6 +247,12 @@ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec
}
}
+ /**
+ * Return the number of rows to display per page
+ * @return
+ */
+ protected abstract int getRows();
+
/**
* {@inheritDoc}
*/
diff --git a/library/src/main/java/com/prolificinteractive/materialcalendarview/DayView.java b/library/src/main/java/com/prolificinteractive/materialcalendarview/DayView.java
index 96d3c32b..09c98b9d 100644
--- a/library/src/main/java/com/prolificinteractive/materialcalendarview/DayView.java
+++ b/library/src/main/java/com/prolificinteractive/materialcalendarview/DayView.java
@@ -6,9 +6,7 @@
import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.LinearGradient;
import android.graphics.Rect;
-import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.graphics.drawable.ShapeDrawable;
@@ -43,6 +41,7 @@ class DayView extends CheckedTextView {
private final int fadeTime;
private Drawable customBackground = null;
private Drawable selectionDrawable;
+ private Drawable mCircleDrawable;
private DayFormatter formatter = DayFormatter.DEFAULT;
private boolean isInRange = true;
@@ -133,7 +132,7 @@ public CalendarDay getDate() {
private void setEnabled() {
boolean enabled = isInMonth && isInRange && !isDecoratedDisabled;
- super.setEnabled(enabled);
+ super.setEnabled(isInRange && !isDecoratedDisabled);
boolean showOtherMonths = showOtherMonths(showOtherDates);
boolean showOutOfRange = showOutOfRange(showOtherDates) || showOtherMonths;
@@ -153,6 +152,10 @@ private void setEnabled() {
shouldBeVisible |= isInMonth && isInRange;
}
+ if (!isInMonth && shouldBeVisible) {
+ setTextColor(getTextColors().getColorForState(
+ new int[]{-android.R.attr.state_enabled}, Color.GRAY));
+ }
setVisibility(shouldBeVisible ? View.VISIBLE : View.INVISIBLE);
}
@@ -168,11 +171,13 @@ protected void setupSelection(@ShowOtherDates int showOtherDates, boolean inRang
@Override
protected void onDraw(@NonNull Canvas canvas) {
if (customBackground != null) {
- canvas.getClipBounds(tempRect);
customBackground.setBounds(tempRect);
customBackground.setState(getDrawableState());
customBackground.draw(canvas);
}
+
+ mCircleDrawable.setBounds(tempRect);
+
super.onDraw(canvas);
}
@@ -180,16 +185,17 @@ private void regenerateBackground() {
if (selectionDrawable != null) {
setBackgroundDrawable(selectionDrawable);
} else {
- setBackgroundDrawable(generateBackground(selectionColor, fadeTime));
+ mCircleDrawable = generateBackground(selectionColor, fadeTime, tempRect);
+ setBackgroundDrawable(mCircleDrawable);
}
}
- private static Drawable generateBackground(int color, int fadeTime) {
+ private static Drawable generateBackground(int color, int fadeTime, Rect bounds) {
StateListDrawable drawable = new StateListDrawable();
drawable.setExitFadeDuration(fadeTime);
drawable.addState(new int[]{android.R.attr.state_checked}, generateCircleDrawable(color));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- drawable.addState(new int[]{android.R.attr.state_pressed}, generateRippleDrawable(color));
+ drawable.addState(new int[]{android.R.attr.state_pressed}, generateRippleDrawable(color, bounds));
} else {
drawable.addState(new int[]{android.R.attr.state_pressed}, generateCircleDrawable(color));
}
@@ -201,20 +207,27 @@ private static Drawable generateBackground(int color, int fadeTime) {
private static Drawable generateCircleDrawable(final int color) {
ShapeDrawable drawable = new ShapeDrawable(new OvalShape());
- drawable.setShaderFactory(new ShapeDrawable.ShaderFactory() {
- @Override
- public Shader resize(int width, int height) {
- return new LinearGradient(0, 0, 0, 0, color, color, Shader.TileMode.REPEAT);
- }
- });
+ drawable.getPaint().setColor(color);
return drawable;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
- private static Drawable generateRippleDrawable(final int color) {
+ private static Drawable generateRippleDrawable(final int color, Rect bounds) {
ColorStateList list = ColorStateList.valueOf(color);
Drawable mask = generateCircleDrawable(Color.WHITE);
- return new RippleDrawable(list, null, mask);
+ RippleDrawable rippleDrawable = new RippleDrawable(list, null, mask);
+// API 21
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
+ rippleDrawable.setBounds(bounds);
+ }
+
+// API 22. Technically harmless to leave on for API 21 and 23, but not worth risking for 23+
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP_MR1) {
+ int center = (bounds.left + bounds.right) / 2;
+ rippleDrawable.setHotspotBounds(center, bounds.top, center, bounds.bottom);
+ }
+
+ return rippleDrawable;
}
/**
@@ -242,4 +255,24 @@ void applyFacade(DayViewFacade facade) {
setText(getLabel());
}
}
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ calculateBounds(right - left, bottom - top);
+ regenerateBackground();
+ }
+
+ private void calculateBounds(int width, int height) {
+ final int radius = Math.min(height, width);
+ // Lollipop platform bug. Rect offset needs to be divided by 4 instead of 2
+ final int offsetDivisor = Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP ? 4 : 2;
+ final int offset = Math.abs(height - width) / offsetDivisor;
+
+ if (width >= height) {
+ tempRect.set(offset, 0, radius + offset, height);
+ } else {
+ tempRect.set(0, offset, width, radius + offset);
+ }
+ }
}
diff --git a/library/src/main/java/com/prolificinteractive/materialcalendarview/Experimental.java b/library/src/main/java/com/prolificinteractive/materialcalendarview/Experimental.java
new file mode 100644
index 00000000..7649aa9a
--- /dev/null
+++ b/library/src/main/java/com/prolificinteractive/materialcalendarview/Experimental.java
@@ -0,0 +1,41 @@
+package com.prolificinteractive.materialcalendarview;
+
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Inspired from https://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/annotations/Beta.java
+ */
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Signifies that a public API (public class, method or field) is will almost certainly
+ * be changed or removed in a future release. An API bearing this annotation should not
+ * be used or relied upon in production code. APIs exposed with this annotation exist
+ * to allow broad testing and feedback on experimental features.
+ **/
+@Retention(RetentionPolicy.CLASS)
+@Target({
+ ElementType.ANNOTATION_TYPE,
+ ElementType.CONSTRUCTOR,
+ ElementType.FIELD,
+ ElementType.METHOD,
+ ElementType.TYPE})
+@Documented
+@Experimental
+public @interface Experimental {
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/prolificinteractive/materialcalendarview/MaterialCalendarView.java b/library/src/main/java/com/prolificinteractive/materialcalendarview/MaterialCalendarView.java
index 93a1ed22..81826f9b 100644
--- a/library/src/main/java/com/prolificinteractive/materialcalendarview/MaterialCalendarView.java
+++ b/library/src/main/java/com/prolificinteractive/materialcalendarview/MaterialCalendarView.java
@@ -148,6 +148,7 @@ public class MaterialCalendarView extends ViewGroup {
*/
public static final int DEFAULT_TILE_SIZE_DP = 44;
private static final int DEFAULT_DAYS_IN_WEEK = 7;
+ private static final int DEFAULT_MAX_WEEKS = 6;
private static final int DAY_NAMES_ROW = 1;
private static final TitleFormatter DEFAULT_TITLE_FORMATTER = new DateFormatTitleFormatter();
@@ -160,7 +161,7 @@ public class MaterialCalendarView extends ViewGroup {
private CalendarPagerAdapter> adapter;
private CalendarDay currentMonth;
private LinearLayout topbar;
- private CalendarMode calendarMode = CalendarMode.MONTHS;
+ private CalendarMode calendarMode;
/**
* Used for the dynamic calendar height.
*/
@@ -204,13 +205,17 @@ public void onPageScrolled(int position, float positionOffset, int positionOffse
private OnDateSelectedListener listener;
private OnMonthChangedListener monthListener;
+ CharSequence calendarContentDescription;
private int accentColor = 0;
private int arrowColor = Color.BLACK;
private Drawable leftArrowMask;
private Drawable rightArrowMask;
- private int tileSize = -1;
+ private int tileHeight = -1;
+ private int tileWidth = -1;
@SelectionMode
private int selectionMode = SELECTION_MODE_SINGLE;
+ private boolean allowClickDaysOutsideCurrentMonth = true;
+ private int firstDayOfWeek;
public MaterialCalendarView(Context context) {
this(context, null);
@@ -230,21 +235,19 @@ public MaterialCalendarView(Context context, AttributeSet attrs) {
}
buttonPast = new DirectionButton(getContext());
+ buttonPast.setContentDescription(getContext().getString(R.string.previous));
title = new TextView(getContext());
buttonFuture = new DirectionButton(getContext());
+ buttonFuture.setContentDescription(getContext().getString(R.string.next));
pager = new CalendarPager(getContext());
- setupChildren();
-
title.setOnClickListener(onClickListener);
buttonPast.setOnClickListener(onClickListener);
buttonFuture.setOnClickListener(onClickListener);
titleChanger = new TitleChanger(title);
titleChanger.setTitleFormatter(DEFAULT_TITLE_FORMATTER);
- adapter = new MonthPagerAdapter(this);
- adapter.setTitleFormatter(DEFAULT_TITLE_FORMATTER);
- pager.setAdapter(adapter);
+
pager.setOnPageChangeListener(pageChangeListener);
pager.setPageTransformer(false, new ViewPager.PageTransformer() {
@Override
@@ -257,12 +260,36 @@ public void transformPage(View page, float position) {
TypedArray a = context.getTheme()
.obtainStyledAttributes(attrs, R.styleable.MaterialCalendarView, 0, 0);
try {
+ int calendarModeIndex = a.getInteger(
+ R.styleable.MaterialCalendarView_mcv_calendarMode,
+ 0
+ );
+ firstDayOfWeek = a.getInteger(
+ R.styleable.MaterialCalendarView_mcv_firstDayOfWeek,
+ -1
+ );
- int tileSize = a.getDimensionPixelSize(R.styleable.MaterialCalendarView_mcv_tileSize, -1);
+ if (firstDayOfWeek < 0) {
+ //Allowing use of Calendar.getInstance() here as a performance optimization
+ firstDayOfWeek = Calendar.getInstance().getFirstDayOfWeek();
+ }
+ setCalendarDisplayMode(CalendarMode.values()[calendarModeIndex]);
+
+ final int tileSize = a.getDimensionPixelSize(R.styleable.MaterialCalendarView_mcv_tileSize, -1);
if (tileSize > 0) {
setTileSize(tileSize);
}
+ final int tileWidth = a.getDimensionPixelSize(R.styleable.MaterialCalendarView_mcv_tileWidth, -1);
+ if (tileWidth > 0) {
+ setTileWidth(tileWidth);
+ }
+
+ final int tileHeight = a.getDimensionPixelSize(R.styleable.MaterialCalendarView_mcv_tileHeight, -1);
+ if (tileHeight > 0) {
+ setTileHeight(tileHeight);
+ }
+
setArrowColor(a.getColor(
R.styleable.MaterialCalendarView_mcv_arrowColor,
Color.BLACK
@@ -317,21 +344,21 @@ public void transformPage(View page, float position) {
SHOW_DEFAULTS
));
- int firstDayOfWeek = a.getInteger(
- R.styleable.MaterialCalendarView_mcv_firstDayOfWeek,
- -1
- );
- if (firstDayOfWeek < 0) {
- //Allowing use of Calendar.getInstance() here as a performance optimization
- firstDayOfWeek = Calendar.getInstance().getFirstDayOfWeek();
- }
- setFirstDayOfWeek(firstDayOfWeek);
+ setAllowClickDaysOutsideCurrentMonth(a.getBoolean(
+ R.styleable.MaterialCalendarView_mcv_allowClickDaysOutsideCurrentMonth,
+ true
+ ));
+
} catch (Exception e) {
e.printStackTrace();
} finally {
a.recycle();
}
+ // Adapter is created while parsing the TypedArray attrs, so setup has to happen after
+ adapter.setTitleFormatter(DEFAULT_TITLE_FORMATTER);
+ setupChildren();
+
currentMonth = CalendarDay.today();
setCurrentDate(currentMonth);
@@ -347,7 +374,6 @@ public void transformPage(View page, float position) {
}
private void setupChildren() {
-
topbar = new LinearLayout(getContext());
topbar.setOrientation(LinearLayout.HORIZONTAL);
topbar.setClipChildren(false);
@@ -421,6 +447,28 @@ public void setSelectionMode(final @SelectionMode int mode) {
adapter.setSelectionEnabled(selectionMode != SELECTION_MODE_NONE);
}
+ /**
+ * Go to previous month or week without using the button {@link #buttonPast}. Should only go to
+ * previous if {@link #canGoBack()} is true, meaning it's possible to go to the previous month
+ * or week.
+ */
+ public void goToPrevious() {
+ if (canGoBack()) {
+ pager.setCurrentItem(pager.getCurrentItem() - 1, true);
+ }
+ }
+
+ /**
+ * Go to next month or week without using the button {@link #buttonFuture}. Should only go to
+ * next if {@link #canGoForward()} is enabled, meaning it's possible to go to the next month or
+ * week.
+ */
+ public void goToNext() {
+ if (canGoForward()) {
+ pager.setCurrentItem(pager.getCurrentItem() + 1, true);
+ }
+ }
+
/**
* Set calendar display mode. The default mode is Months.
* When switching between modes will select todays date, or the selected date,
@@ -428,8 +476,9 @@ public void setSelectionMode(final @SelectionMode int mode) {
*
* @param mode - calendar mode
*/
+ @Experimental
public void setCalendarDisplayMode(CalendarMode mode) {
- if (calendarMode.equals(mode)) {
+ if (calendarMode != null && calendarMode.equals(mode)) {
return;
}
@@ -444,9 +493,18 @@ public void setCalendarDisplayMode(CalendarMode mode) {
default:
throw new IllegalArgumentException("Provided display mode which is not yet implemented");
}
- adapter = adapter.migrateStateAndReturn(newAdapter);
+ if (adapter == null) {
+ adapter = newAdapter;
+ } else {
+ adapter = adapter.migrateStateAndReturn(newAdapter);
+ }
pager.setAdapter(adapter);
+ setRangeDates(minDate, maxDate);
calendarMode = mode;
+
+ // Reset height params after mode change
+ pager.setLayoutParams(new LayoutParams(calendarMode.visibleWeeksCount + DAY_NAMES_ROW));
+
setCurrentDate(
selectionMode == SELECTION_MODE_SINGLE && !adapter.getSelectedDates().isEmpty()
? adapter.getSelectedDates().get(0)
@@ -470,10 +528,14 @@ public int getSelectionMode() {
}
/**
- * @return the size of tiles in pixels
+ * Use {@link #getTileWidth()} or {@link #getTileHeight()} instead. This method is deprecated
+ * and will just return the largest of the two sizes.
+ *
+ * @return tile height or width, whichever is larger
*/
+ @Deprecated
public int getTileSize() {
- return tileSize;
+ return Math.max(tileHeight, tileWidth);
}
/**
@@ -484,7 +546,8 @@ public int getTileSize() {
* @param size the new size for each tile in pixels
*/
public void setTileSize(int size) {
- this.tileSize = size;
+ this.tileWidth = size;
+ this.tileHeight = size;
requestLayout();
}
@@ -496,6 +559,56 @@ public void setTileSizeDp(int tileSizeDp) {
setTileSize(dpToPx(tileSizeDp));
}
+ /**
+ * @return the height of tiles in pixels
+ */
+ public int getTileHeight() {
+ return tileHeight;
+ }
+
+ /**
+ * Set the height of each tile that makes up the calendar.
+ *
+ * @param height the new height for each tile in pixels
+ */
+ public void setTileHeight(int height) {
+ this.tileHeight = height;
+ requestLayout();
+ }
+
+ /**
+ * @param tileHeightDp the new height for each tile in dips
+ * @see #setTileHeight(int)
+ */
+ public void setTileHeightDp(int tileHeightDp) {
+ setTileHeight(dpToPx(tileHeightDp));
+ }
+
+ /**
+ * @return the width of tiles in pixels
+ */
+ public int getTileWidth() {
+ return tileWidth;
+ }
+
+ /**
+ * Set the width of each tile that makes up the calendar.
+ *
+ * @param width the new width for each tile in pixels
+ */
+ public void setTileWidth(int width) {
+ this.tileWidth = width;
+ requestLayout();
+ }
+
+ /**
+ * @param tileWidthDp the new width for each tile in dips
+ * @see #setTileWidth(int)
+ */
+ public void setTileWidthDp(int tileWidthDp) {
+ setTileWidth(dpToPx(tileWidthDp));
+ }
+
private int dpToPx(int dp) {
return (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics()
@@ -507,7 +620,7 @@ TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics()
*
* @return true if there is a future month that can be shown
*/
- private boolean canGoForward() {
+ public boolean canGoForward() {
return pager.getCurrentItem() < (adapter.getCount() - 1);
}
@@ -527,7 +640,7 @@ public boolean onTouchEvent(MotionEvent event) {
*
* @return true if there is a previous month that can be shown
*/
- private boolean canGoBack() {
+ public boolean canGoBack() {
return pager.getCurrentItem() > 0;
}
@@ -574,6 +687,44 @@ public void setArrowColor(int color) {
invalidate();
}
+ /**
+ * Set content description for button past
+ *
+ * @param description String to use as content description
+ */
+ public void setContentDescriptionArrowPast(final CharSequence description) {
+ buttonPast.setContentDescription(description);
+ }
+
+ /**
+ * Set content description for button future
+ *
+ * @param description String to use as content description
+ */
+ public void setContentDescriptionArrowFuture(final CharSequence description) {
+ buttonFuture.setContentDescription(description);
+ }
+
+ /**
+ * Set content description for calendar
+ *
+ * @param description String to use as content description
+ */
+ public void setContentDescriptionCalendar(final CharSequence description) {
+ calendarContentDescription = description;
+ }
+
+ /**
+ * Get content description for calendar
+ *
+ * @return calendar's content description
+ */
+ public CharSequence getCalendarContentDescription() {
+ return calendarContentDescription != null
+ ? calendarContentDescription
+ : getContext().getString(R.string.calendar);
+ }
+
/**
* @return icon used for the left arrow
*/
@@ -823,6 +974,18 @@ public void setShowOtherDates(@ShowOtherDates int showOtherDates) {
adapter.setShowOtherDates(showOtherDates);
}
+ /**
+ * Allow the user to click on dates from other months that are not out of range. Go to next or
+ * previous month if a day outside the current month is clicked. The day still need to be
+ * enabled to be selected.
+ * Default value is true. Should be used with {@link #SHOW_OTHER_MONTHS}.
+ *
+ * @param enabled True to allow the user to click on a day outside current month displayed
+ */
+ public void setAllowClickDaysOutsideCurrentMonth(final boolean enabled) {
+ this.allowClickDaysOutsideCurrentMonth = enabled;
+ }
+
/**
* Set a formatter for weekday labels.
*
@@ -879,6 +1042,13 @@ public int getShowOtherDates() {
return adapter.getShowOtherDates();
}
+ /**
+ * @return true if allow click on days outside current month displayed
+ */
+ public boolean allowClickDaysOutsideCurrentMonth() {
+ return allowClickDaysOutsideCurrentMonth;
+ }
+
/**
* Set a custom formatter for the month/year title
*
@@ -943,13 +1113,17 @@ protected Parcelable onSaveInstanceState() {
ss.dateTextAppearance = adapter.getDateTextAppearance();
ss.weekDayTextAppearance = adapter.getWeekDayTextAppearance();
ss.showOtherDates = getShowOtherDates();
+ ss.allowClickDaysOutsideCurrentMonth = allowClickDaysOutsideCurrentMonth();
ss.minDate = getMinimumDate();
ss.maxDate = getMaximumDate();
ss.selectedDates = getSelectedDates();
ss.firstDayOfWeek = getFirstDayOfWeek();
ss.selectionMode = getSelectionMode();
- ss.tileSizePx = getTileSize();
+ ss.tileWidthPx = getTileWidth();
+ ss.tileHeightPx = getTileHeight();
ss.topbarVisible = getTopbarVisible();
+ ss.calendarMode = calendarMode;
+ ss.currentMonth = currentMonth;
return ss;
}
@@ -961,16 +1135,22 @@ protected void onRestoreInstanceState(Parcelable state) {
setDateTextAppearance(ss.dateTextAppearance);
setWeekDayTextAppearance(ss.weekDayTextAppearance);
setShowOtherDates(ss.showOtherDates);
+ setAllowClickDaysOutsideCurrentMonth(ss.allowClickDaysOutsideCurrentMonth);
+ minDate = ss.minDate;
+ maxDate = ss.maxDate;
setRangeDates(ss.minDate, ss.maxDate);
clearSelection();
for (CalendarDay calendarDay : ss.selectedDates) {
setDateSelected(calendarDay, true);
}
setFirstDayOfWeek(ss.firstDayOfWeek);
- setTileSize(ss.tileSizePx);
+ setTileWidth(ss.tileWidthPx);
+ setTileHeight(ss.tileHeightPx);
setTopbarVisible(ss.topbarVisible);
setSelectionMode(ss.selectionMode);
setDynamicHeightEnabled(ss.dynamicHeightEnabled);
+ setCalendarDisplayMode(ss.calendarMode);
+ setCurrentDate(ss.currentMonth);
}
@Override
@@ -998,14 +1178,18 @@ public static class SavedState extends BaseSavedState {
int dateTextAppearance = 0;
int weekDayTextAppearance = 0;
int showOtherDates = SHOW_DEFAULTS;
+ boolean allowClickDaysOutsideCurrentMonth = true;
CalendarDay minDate = null;
CalendarDay maxDate = null;
List selectedDates = new ArrayList<>();
int firstDayOfWeek = Calendar.SUNDAY;
- int tileSizePx = -1;
+ int tileWidthPx = -1;
+ int tileHeightPx = -1;
boolean topbarVisible = true;
int selectionMode = SELECTION_MODE_SINGLE;
boolean dynamicHeightEnabled = false;
+ CalendarMode calendarMode = CalendarMode.MONTHS;
+ CalendarDay currentMonth = null;
SavedState(Parcelable superState) {
super(superState);
@@ -1018,14 +1202,18 @@ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(dateTextAppearance);
out.writeInt(weekDayTextAppearance);
out.writeInt(showOtherDates);
+ out.writeByte((byte) (allowClickDaysOutsideCurrentMonth ? 1 : 0));
out.writeParcelable(minDate, 0);
out.writeParcelable(maxDate, 0);
out.writeTypedList(selectedDates);
out.writeInt(firstDayOfWeek);
- out.writeInt(tileSizePx);
+ out.writeInt(tileWidthPx);
+ out.writeInt(tileHeightPx);
out.writeInt(topbarVisible ? 1 : 0);
out.writeInt(selectionMode);
out.writeInt(dynamicHeightEnabled ? 1 : 0);
+ out.writeInt(calendarMode == CalendarMode.WEEKS ? 1 : 0);
+ out.writeParcelable(currentMonth, 0);
}
public static final Parcelable.Creator CREATOR
@@ -1045,15 +1233,19 @@ private SavedState(Parcel in) {
dateTextAppearance = in.readInt();
weekDayTextAppearance = in.readInt();
showOtherDates = in.readInt();
+ allowClickDaysOutsideCurrentMonth = in.readByte() != 0;
ClassLoader loader = CalendarDay.class.getClassLoader();
minDate = in.readParcelable(loader);
maxDate = in.readParcelable(loader);
in.readTypedList(selectedDates, CalendarDay.CREATOR);
firstDayOfWeek = in.readInt();
- tileSizePx = in.readInt();
+ tileWidthPx = in.readInt();
+ tileHeightPx = in.readInt();
topbarVisible = in.readInt() == 1;
selectionMode = in.readInt();
dynamicHeightEnabled = in.readInt() == 1;
+ calendarMode = in.readInt() == 1 ? CalendarMode.WEEKS : CalendarMode.MONTHS;
+ currentMonth = in.readParcelable(loader);
}
}
@@ -1079,14 +1271,42 @@ private static int getThemeAccentColor(Context context) {
* @see java.util.Calendar
*/
public void setFirstDayOfWeek(int day) {
- adapter.setFirstDayOfWeek(day);
+ firstDayOfWeek = day;
+ // TODO: 5/12/16 consider a less nuclear means of resetting the adapter when setting a new
+ // first day of week and how regular notifyDataSetChanged doesn't work (may require updating
+ // getItemPosition to flag current object and the ones to the left/right as changed)
+ CalendarPagerAdapter> newAdapter;
+ switch (calendarMode) {
+ case MONTHS:
+ newAdapter = new MonthPagerAdapter(this);
+ break;
+ case WEEKS:
+ newAdapter = new WeekPagerAdapter(this);
+ break;
+ default:
+ throw new IllegalArgumentException("Provided display mode which is not yet implemented");
+ }
+ if (adapter == null) {
+ adapter = newAdapter;
+ } else {
+ adapter = adapter.migrateStateAndReturn(newAdapter);
+ }
+ pager.setAdapter(adapter);
+ setRangeDates(minDate, maxDate);
+
+ setCurrentDate(
+ selectionMode == SELECTION_MODE_SINGLE && !adapter.getSelectedDates().isEmpty()
+ ? adapter.getSelectedDates().get(0)
+ : CalendarDay.today());
+ invalidateDecorators();
+ updateUi();
}
/**
* @return The first day of the week as a {@linkplain Calendar} day constant.
*/
public int getFirstDayOfWeek() {
- return adapter.getFirstDayOfWeek();
+ return firstDayOfWeek;
}
/**
@@ -1220,7 +1440,8 @@ protected void dispatchOnMonthChanged(final CalendarDay day) {
}
/**
- * Call by MonthView to indicate that a day was clicked and we should handle it
+ * Call by {@link CalendarPagerView} to indicate that a day was clicked and we should handle it.
+ * This method will always process the click to the selected date.
*
* @param date date of the day that was clicked
* @param nowSelected true if the date is now selected, false otherwise
@@ -1242,6 +1463,29 @@ protected void onDateClicked(@NonNull CalendarDay date, boolean nowSelected) {
}
}
+ /**
+ * Call by {@link CalendarPagerView} to indicate that a day was clicked and we should handle it
+ *
+ * @param dayView
+ */
+ protected void onDateClicked(DayView dayView) {
+ final int currentMonth = getCurrentDate().getMonth();
+ final int selectedMonth = dayView.getDate().getMonth();
+
+ if (calendarMode == CalendarMode.MONTHS) {
+ if (allowClickDaysOutsideCurrentMonth || currentMonth == selectedMonth) {
+ if (currentMonth > selectedMonth) {
+ goToPrevious();
+ } else if (currentMonth < selectedMonth) {
+ goToNext();
+ }
+ onDateClicked(dayView.getDate(), !dayView.isChecked());
+ }
+ } else {
+ onDateClicked(dayView.getDate(), !dayView.isChecked());
+ }
+ }
+
/**
* Called by the adapter for cases when changes in state result in dates being unselected
*
@@ -1314,10 +1558,18 @@ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec
int desiredTileHeight = desiredHeight / viewTileHeight;
int measureTileSize = -1;
+ int measureTileWidth = -1;
+ int measureTileHeight = -1;
- if (this.tileSize > 0) {
- //We have a tileSize set, we should use that
- measureTileSize = this.tileSize;
+ if (this.tileWidth > 0 || this.tileHeight > 0) {
+ if (this.tileWidth > 0) {
+ //We have a tileWidth set, we should use that
+ measureTileWidth = this.tileWidth;
+ }
+ if (this.tileHeight > 0) {
+ //We have a tileHeight set, we should use that
+ measureTileHeight = this.tileHeight;
+ }
} else if (specWidthMode == MeasureSpec.EXACTLY) {
if (specHeightMode == MeasureSpec.EXACTLY) {
//Pick the larger of the two explicit sizes
@@ -1331,14 +1583,24 @@ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec
measureTileSize = desiredTileHeight;
}
- //Uh oh! We need to default to something, quick!
- if (measureTileSize <= 0) {
- measureTileSize = dpToPx(DEFAULT_TILE_SIZE_DP);
+ if (measureTileSize > 0) {
+ //Use measureTileSize if set
+ measureTileHeight = measureTileSize;
+ measureTileWidth = measureTileSize;
+ } else if (measureTileSize <= 0) {
+ if (measureTileWidth <= 0) {
+ //Set width to default if no value were set
+ measureTileWidth = dpToPx(DEFAULT_TILE_SIZE_DP);
+ }
+ if (measureTileHeight <= 0) {
+ //Set height to default if no value were set
+ measureTileHeight = dpToPx(DEFAULT_TILE_SIZE_DP);
+ }
}
//Calculate our size based off our measured tile size
- int measuredWidth = measureTileSize * DEFAULT_DAYS_IN_WEEK;
- int measuredHeight = measureTileSize * viewTileHeight;
+ int measuredWidth = measureTileWidth * DEFAULT_DAYS_IN_WEEK;
+ int measuredHeight = measureTileHeight * viewTileHeight;
//Put padding back in from when we took it away
measuredWidth += getPaddingLeft() + getPaddingRight();
@@ -1359,12 +1621,12 @@ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec
LayoutParams p = (LayoutParams) child.getLayoutParams();
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
- DEFAULT_DAYS_IN_WEEK * measureTileSize,
+ DEFAULT_DAYS_IN_WEEK * measureTileWidth,
MeasureSpec.EXACTLY
);
int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
- p.height * measureTileSize,
+ p.height * measureTileHeight,
MeasureSpec.EXACTLY
);
diff --git a/library/src/main/java/com/prolificinteractive/materialcalendarview/MonthPagerAdapter.java b/library/src/main/java/com/prolificinteractive/materialcalendarview/MonthPagerAdapter.java
index 7123e383..00e72941 100644
--- a/library/src/main/java/com/prolificinteractive/materialcalendarview/MonthPagerAdapter.java
+++ b/library/src/main/java/com/prolificinteractive/materialcalendarview/MonthPagerAdapter.java
@@ -14,7 +14,7 @@ class MonthPagerAdapter extends CalendarPagerAdapter {
@Override
protected MonthView createView(int position) {
- return new MonthView(mcv, getItem(position), getFirstDayOfWeek());
+ return new MonthView(mcv, getItem(position), mcv.getFirstDayOfWeek());
}
@Override
diff --git a/library/src/main/java/com/prolificinteractive/materialcalendarview/MonthView.java b/library/src/main/java/com/prolificinteractive/materialcalendarview/MonthView.java
index 2926e4e5..4ee69c84 100644
--- a/library/src/main/java/com/prolificinteractive/materialcalendarview/MonthView.java
+++ b/library/src/main/java/com/prolificinteractive/materialcalendarview/MonthView.java
@@ -13,9 +13,6 @@
@SuppressLint("ViewConstructor")
class MonthView extends CalendarPagerView {
- private static final int DEFAULT_DAYS_IN_WEEK = 7;
- private static final int DEFAULT_MAX_WEEKS = 6;
-
public MonthView(@NonNull MaterialCalendarView view, CalendarDay month, int firstDayOfWeek) {
super(view, month, firstDayOfWeek);
}
@@ -37,4 +34,9 @@ public CalendarDay getMonth() {
protected boolean isDayEnabled(CalendarDay day) {
return day.getMonth() == getFirstViewDay().getMonth();
}
+
+ @Override
+ protected int getRows() {
+ return DEFAULT_MAX_WEEKS + DAY_NAMES_ROW;
+ }
}
diff --git a/library/src/main/java/com/prolificinteractive/materialcalendarview/WeekDayView.java b/library/src/main/java/com/prolificinteractive/materialcalendarview/WeekDayView.java
index 57b1535c..ac97c2c8 100644
--- a/library/src/main/java/com/prolificinteractive/materialcalendarview/WeekDayView.java
+++ b/library/src/main/java/com/prolificinteractive/materialcalendarview/WeekDayView.java
@@ -13,6 +13,7 @@
/**
* Display a day of the week
*/
+@Experimental
@SuppressLint("ViewConstructor")
class WeekDayView extends TextView {
diff --git a/library/src/main/java/com/prolificinteractive/materialcalendarview/WeekPagerAdapter.java b/library/src/main/java/com/prolificinteractive/materialcalendarview/WeekPagerAdapter.java
index dabfdd27..a1ab577b 100644
--- a/library/src/main/java/com/prolificinteractive/materialcalendarview/WeekPagerAdapter.java
+++ b/library/src/main/java/com/prolificinteractive/materialcalendarview/WeekPagerAdapter.java
@@ -6,6 +6,7 @@
import java.util.Date;
import java.util.concurrent.TimeUnit;
+@Experimental
public class WeekPagerAdapter extends CalendarPagerAdapter {
public WeekPagerAdapter(MaterialCalendarView mcv) {
@@ -14,7 +15,7 @@ public WeekPagerAdapter(MaterialCalendarView mcv) {
@Override
protected WeekView createView(int position) {
- return new WeekView(mcv, getItem(position), getFirstDayOfWeek());
+ return new WeekView(mcv, getItem(position), mcv.getFirstDayOfWeek());
}
@Override
@@ -30,7 +31,7 @@ protected boolean isInstanceOfView(Object object) {
@Override
protected DateRangeIndex createRangeIndex(CalendarDay min, CalendarDay max) {
- return new Weekly(min, max, getFirstDayOfWeek());
+ return new Weekly(min, max, mcv.getFirstDayOfWeek());
}
public static class Weekly implements DateRangeIndex {
@@ -41,7 +42,7 @@ public static class Weekly implements DateRangeIndex {
public Weekly(@NonNull CalendarDay min, @NonNull CalendarDay max, int firstDayOfWeek) {
this.min = getFirstDayOfWeek(min, firstDayOfWeek);
- this.count = weekNumberDifference(min, max);
+ this.count = weekNumberDifference(min, max) + 1;
}
@Override
diff --git a/library/src/main/java/com/prolificinteractive/materialcalendarview/WeekView.java b/library/src/main/java/com/prolificinteractive/materialcalendarview/WeekView.java
index 8653357a..3e93d1de 100644
--- a/library/src/main/java/com/prolificinteractive/materialcalendarview/WeekView.java
+++ b/library/src/main/java/com/prolificinteractive/materialcalendarview/WeekView.java
@@ -10,11 +10,10 @@
* Display a week of {@linkplain DayView}s and
* seven {@linkplain WeekDayView}s.
*/
+@Experimental
@SuppressLint("ViewConstructor")
public class WeekView extends CalendarPagerView {
- private static final int DEFAULT_DAYS_IN_WEEK = 7;
-
public WeekView(@NonNull MaterialCalendarView view,
CalendarDay firstViewDay,
int firstDayOfWeek) {
@@ -32,4 +31,9 @@ protected void buildDayViews(Collection dayViews, Calendar calendar) {
protected boolean isDayEnabled(CalendarDay day) {
return true;
}
+
+ @Override
+ protected int getRows() {
+ return DAY_NAMES_ROW + 1;
+ }
}
diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml
index d3deee50..7f1e0652 100644
--- a/library/src/main/res/values/attrs.xml
+++ b/library/src/main/res/values/attrs.xml
@@ -3,14 +3,14 @@
-
-
-
+
+
+
-
-
-
-
+
+
+
+
@@ -21,10 +21,14 @@
+
+
-
+
+
+
@@ -36,6 +40,10 @@
+
+
+
+
diff --git a/library/src/main/res/values/strings.xml b/library/src/main/res/values/strings.xml
new file mode 100644
index 00000000..549c67f7
--- /dev/null
+++ b/library/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+ Go to previous
+ Go to next
+ Calendar
+
diff --git a/sample/build.gradle b/sample/build.gradle
index 25052394..11d6b179 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -16,10 +16,10 @@ android {
dependencies {
// You should use the commented out line below in you're application.
// We depend on the source directly here so that development is easier.
- compile project(':library')
- //compile 'com.prolificinteractive:material-calendarview:1.2.1'
+// compile project(':library')
+ compile 'com.prolificinteractive:material-calendarview:1.3.0'
- compile 'com.android.support:appcompat-v7:23.3.0'
- compile 'com.android.support:recyclerview-v7:23.3.0'
+ compile 'com.android.support:appcompat-v7:23.4.0'
+ compile 'com.android.support:recyclerview-v7:23.4.0'
compile 'com.jakewharton:butterknife:7.0.1'
}
diff --git a/sample/src/main/java/com/prolificinteractive/materialcalendarview/sample/CustomizeCodeActivity.java b/sample/src/main/java/com/prolificinteractive/materialcalendarview/sample/CustomizeCodeActivity.java
index 3bb93ea3..62588674 100644
--- a/sample/src/main/java/com/prolificinteractive/materialcalendarview/sample/CustomizeCodeActivity.java
+++ b/sample/src/main/java/com/prolificinteractive/materialcalendarview/sample/CustomizeCodeActivity.java
@@ -4,6 +4,8 @@
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
+import com.prolificinteractive.materialcalendarview.CalendarDay;
+import com.prolificinteractive.materialcalendarview.CalendarMode;
import com.prolificinteractive.materialcalendarview.MaterialCalendarView;
import com.prolificinteractive.materialcalendarview.format.ArrayWeekDayFormatter;
import com.prolificinteractive.materialcalendarview.format.MonthArrayTitleFormatter;
@@ -35,7 +37,15 @@ protected void onCreate(Bundle savedInstanceState) {
widget.setTitleFormatter(new MonthArrayTitleFormatter(getResources().getTextArray(R.array.custom_months)));
widget.setWeekDayFormatter(new ArrayWeekDayFormatter(getResources().getTextArray(R.array.custom_weekdays)));
widget.setTileSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 36, getResources().getDisplayMetrics()));
- widget.setFirstDayOfWeek(Calendar.THURSDAY);
+
+ CalendarDay today = CalendarDay.from(2016, 5, 2);
+ widget.setCurrentDate(today);
+ widget.setSelectedDate(today);
+
+ widget.setFirstDayOfWeek(Calendar.WEDNESDAY);
+ widget.setMinimumDate(CalendarDay.from(2016, 4, 3));
+ widget.setMaximumDate(CalendarDay.from(2016, 5, 12));
+ widget.setCalendarDisplayMode(CalendarMode.WEEKS);
}
}
diff --git a/sample/src/main/java/com/prolificinteractive/materialcalendarview/sample/DynamicSettersActivity.java b/sample/src/main/java/com/prolificinteractive/materialcalendarview/sample/DynamicSettersActivity.java
index 66b13bf2..06056825 100644
--- a/sample/src/main/java/com/prolificinteractive/materialcalendarview/sample/DynamicSettersActivity.java
+++ b/sample/src/main/java/com/prolificinteractive/materialcalendarview/sample/DynamicSettersActivity.java
@@ -9,9 +9,11 @@
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.widget.DatePicker;
+import android.widget.LinearLayout;
import android.widget.NumberPicker;
import com.prolificinteractive.materialcalendarview.CalendarDay;
+import com.prolificinteractive.materialcalendarview.CalendarMode;
import com.prolificinteractive.materialcalendarview.MaterialCalendarView;
import java.util.Calendar;
@@ -28,6 +30,8 @@ public class DynamicSettersActivity extends AppCompatActivity {
MaterialCalendarView widget;
private int currentTileSize;
+ private int currentTileWidth;
+ private int currentTileHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -36,6 +40,8 @@ protected void onCreate(Bundle savedInstanceState) {
ButterKnife.bind(this);
currentTileSize = MaterialCalendarView.DEFAULT_TILE_SIZE_DP;
+ currentTileWidth = MaterialCalendarView.DEFAULT_TILE_SIZE_DP;
+ currentTileHeight = MaterialCalendarView.DEFAULT_TILE_SIZE_DP;
}
@OnClick(R.id.button_other_dates)
@@ -43,7 +49,8 @@ void onOtherDatesClicked() {
CharSequence[] items = {
"Other Months",
"Out Of Range",
- "Decorated Disabled"
+ "Decorated Disabled",
+ "Select days outside month"
};
final int[] itemValues = {
MaterialCalendarView.SHOW_OTHER_MONTHS,
@@ -51,25 +58,31 @@ void onOtherDatesClicked() {
MaterialCalendarView.SHOW_DECORATED_DISABLED,
};
int showOtherDates = widget.getShowOtherDates();
+
boolean[] initSelections = {
MaterialCalendarView.showOtherMonths(showOtherDates),
MaterialCalendarView.showOutOfRange(showOtherDates),
MaterialCalendarView.showDecoratedDisabled(showOtherDates),
+ widget.allowClickDaysOutsideCurrentMonth()
};
new AlertDialog.Builder(this)
.setTitle("Show Other Dates")
.setMultiChoiceItems(items, initSelections, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
- int showOtherDates = widget.getShowOtherDates();
- if (isChecked) {
- //Set flag
- showOtherDates |= itemValues[which];
- } else {
- //Unset flag
- showOtherDates &= ~itemValues[which];
+ if (which < 3) {
+ int showOtherDates = widget.getShowOtherDates();
+ if (isChecked) {
+ //Set flag
+ showOtherDates |= itemValues[which];
+ } else {
+ //Unset flag
+ showOtherDates &= ~itemValues[which];
+ }
+ widget.setShowOtherDates(showOtherDates);
+ } else if (which == 3) {
+ widget.setAllowClickDaysOutsideCurrentMonth(isChecked);
}
- widget.setShowOtherDates(showOtherDates);
}
})
.setPositiveButton(android.R.string.ok, null)
@@ -90,12 +103,22 @@ void onTextAppearanceChecked(boolean checked) {
widget.setShowOtherDates(checked ? MaterialCalendarView.SHOW_ALL : MaterialCalendarView.SHOW_NONE);
}
- @OnCheckedChanged(R.id.check_page_enabled)
+ @OnCheckedChanged(R.id.check_page_enabled)
void onPageEnabledChecked(boolean checked) {
widget.setPagingEnabled(checked);
}
- @OnClick(R.id.button_min_date)
+ @OnClick(R.id.button_previous)
+ void onPreviousClicked() {
+ widget.goToPrevious();
+ }
+
+ @OnClick(R.id.button_next)
+ void onNextClicked() {
+ widget.goToNext();
+ }
+
+ @OnClick(R.id.button_min_date)
void onMinClicked() {
showDatePickerDialog(this, widget.getMinimumDate(), new DatePickerDialog.OnDateSetListener() {
@Override
@@ -162,6 +185,37 @@ public void onClick(@NonNull DialogInterface dialog, int which) {
.show();
}
+ @OnClick(R.id.button_set_width_height)
+ void onTileWidthHeightClicked() {
+ final LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.HORIZONTAL);
+ final NumberPicker pickerWidth = new NumberPicker(this);
+ pickerWidth.setMinValue(24);
+ pickerWidth.setMaxValue(64);
+ pickerWidth.setWrapSelectorWheel(false);
+ pickerWidth.setValue(currentTileWidth);
+ final NumberPicker pickerHeight = new NumberPicker(this);
+ pickerHeight.setMinValue(24);
+ pickerHeight.setMaxValue(64);
+ pickerHeight.setWrapSelectorWheel(false);
+ pickerHeight.setValue(currentTileHeight);
+ layout.addView(pickerWidth);
+ layout.addView(pickerHeight);
+ new AlertDialog.Builder(this)
+ .setView(layout)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(@NonNull DialogInterface dialog, int which) {
+ currentTileWidth = pickerWidth.getValue();
+ currentTileHeight = pickerHeight.getValue();
+ widget.setTileSize(-1);
+ widget.setTileWidthDp(currentTileWidth);
+ widget.setTileHeightDp(currentTileHeight);
+ }
+ })
+ .show();
+ }
+
@OnClick(R.id.button_clear_selection)
void onClearSelection() {
widget.clearSelection();
@@ -202,6 +256,17 @@ void onFirstDayOfWeekClicked() {
widget.setFirstDayOfWeek(DAYS_OF_WEEK[index]);
}
+ @OnClick(R.id.button_weeks)
+ public void onSetWeekMode() {
+ widget.setCalendarDisplayMode(CalendarMode.WEEKS);
+ }
+
+ @OnClick(R.id.button_months)
+ public void onSetMonthMode() {
+ widget.setCalendarDisplayMode(CalendarMode.MONTHS);
+ }
+
+
public static void showDatePickerDialog(Context context, CalendarDay day,
DatePickerDialog.OnDateSetListener callback) {
if (day == null) {
diff --git a/sample/src/main/res/layout/activity_customization.xml b/sample/src/main/res/layout/activity_customization.xml
index 3e221fcd..c3461c01 100644
--- a/sample/src/main/res/layout/activity_customization.xml
+++ b/sample/src/main/res/layout/activity_customization.xml
@@ -27,6 +27,7 @@
app:mcv_monthLabels="@array/custom_months"
app:mcv_tileSize="36dp"
app:mcv_firstDayOfWeek="thursday"
+ app:mcv_calendarMode="week"
/>
diff --git a/sample/src/main/res/layout/activity_dynamic_setters.xml b/sample/src/main/res/layout/activity_dynamic_setters.xml
index 40846fc9..ad3fb8bd 100644
--- a/sample/src/main/res/layout/activity_dynamic_setters.xml
+++ b/sample/src/main/res/layout/activity_dynamic_setters.xml
@@ -46,6 +46,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -142,6 +186,13 @@
+
+
- #4590bd
- #88bfcb
+ #0093FF
+ #006BBA
\ No newline at end of file