My goal is to find an Android library that will allow me to mark various dates on a calendar view based on an array. The dates may or may not be contiguous. My ideal scenario is to change the background color of each date. The significant complication is that I don't know this color until runtime, since it will come from a server query.
I've been researching this all day, and my best hope seems to be material-calendarview
(github). However, I am finding their code to be somewhat impenetrable, which is on me, but I am completely stuck.
I've added a calendar like this in my XML layout:
<com.prolificinteractive.materialcalendarview.MaterialCalendarView
android:id="@+id/calendar_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:mcv_showOtherDates="all"
app:mcv_selectionColor="#00F"/>
And then in my activity, I have these instance variables:
private MaterialCalendarView calendarView;
private ArrayList<Date> markedDates;
and this code in my onCreateView()
calendarView = (MaterialCalendarView) view.findViewById(R.id.calendar_view);
Ok, easy enough. But I cannot figure out how to mark the calendar from my array of dates. I am working on this method, but I just don't know how to proceed beyond what I have here:
private void initializeCalendar() {
calendarView.setOnDateChangedListener(context);
calendarView.setShowOtherDates(MaterialCalendarView.SHOW_ALL);
Calendar calendar = Calendar.getInstance();
calendarView.setSelectedDate(calendar.getTime());
calendar.set(calendar.get(Calendar.YEAR), Calendar.JANUARY, 1);
calendarView.setMinimumDate(calendar.getTime());
calendar.set(calendar.get(Calendar.YEAR), Calendar.DECEMBER, 31);
calendarView.setMaximumDate(calendar.getTime());
int bgColor = sharedVisualElements.getPrimaryColor();
calendarView.addDecorators(new EventDecorator(bgColor, ????));
}
That last line refers to this inner class:
private class EventDecorator implements DayViewDecorator {
private final int color;
private final HashSet<CalendarDay> dates;
public EventDecorator(int color, Collection<CalendarDay> dates) {
this.color = color;
this.dates = new HashSet<>(dates);
}
@Override
public boolean shouldDecorate(CalendarDay day) {
return dates.contains(day);
}
@Override
public void decorate(DayViewFacade view) {
view.addSpan(new DotSpan(5, color));
}
}
I think that my challenge to convert my ArrayList<Date> markedDates
to what they call Collection<CalendarDay> dates
. Agree? But this is where I really bog down. This data structure is bizarre to me. When I try to instantiate it by calling new CalendarDay()
my class immediately expands with about 10 new methods that I don't understand their role or what to do with them. Clearly, I am going off the rails here. It just can't be this tricky.
Has anyone used this library for this purpose and know how to accomplish this task? I'm at a grinding halt. Also, if there is a simpler library to allow me to set background colors using a color only known at run-time, I'm all ears.
Thanks for any help. I fear that I have written this in a confusing manner, which is a result of the fact that I am completely confused.
I solved this, so I'll post that solution in case anyone else has the same question. If there is a more efficient way, please post as a solution.
I mentioned that I have an array with a list of dates. What I need to do is to iterate over that array, converting each Date
into a Calendar
object set to the appropriate year, month, and day, and then adding that object to a different ArrayList
, this time an ArrayList<CalendarDay>
. For example:
List<CalendarDay> list = new ArrayList<CalendarDay>();
Calendar calendar = Calendar.getInstance();
for (Date date : markedDates) {
// might be a more elegant way to do this part, but this is very explicit
int year = date.getYear();
int month = date.getMonthOfYear() - 1; // months are 0-based in Calendar
int day = date.getDayOfMonth();
calendar.set(year, month, day);
CalendarDay calendarDay = CalendarDay.from(calendar);
list.add(calendarDay);
}
So now we've got this list of CalendarDay
objects, but we're not quite there. The final step in creating the data structure is to 'convert' this over to what I mentioned I was struggling with in the OP - a Collection<CalendarDay>
structure. Turns out this couldn't be any simpler once we get here. Simply assign it like this:
calendarDays = list;
And then when you want to add the decorator, you're all set up. Just do this:
calendarView.addDecorators(new EventDecorator(myColor, calendarDays));
One other thing bears mentioning, and this was a major source of my confusion. I didn't understand how to instantiate this Collection<CalendarDay>
object. Way up in the instance variable section (before the constructor), I added this code, almost all of which Android Studio filled in for me:
private Collection<CalendarDay> calendarDays = new Collection<CalendarDay>() {
@Override
public boolean add(CalendarDay object) {
return false;
}
@Override
public boolean addAll(Collection<? extends CalendarDay> collection) {
return false;
}
@Override
public void clear() {
}
@Override
public boolean contains(Object object) {
return false;
}
@Override
public boolean containsAll(Collection<?> collection) {
return false;
}
@Override
public boolean isEmpty() {
return false;
}
@NonNull
@Override
public Iterator<CalendarDay> iterator() {
return null;
}
@Override
public boolean remove(Object object) {
return false;
}
@Override
public boolean removeAll(Collection<?> collection) {
return false;
}
@Override
public boolean retainAll(Collection<?> collection) {
return false;
}
@Override
public int size() {
return 0;
}
@NonNull
@Override
public Object[] toArray() {
return new Object[0];
}
@NonNull
@Override
public <T> T[] toArray(T[] array) {
return null;
}
};
I hope that this helps someone. Again, if there is a better solution, please post and I'll delete mine.
If you want to change the background color of the selected programatically, use this method:-
MaterialCalendarView materialCalendar = (MaterialCalendarView)findViewById(R.id.materialCalenderView);
materialCalendar.setSelectionColor(Color.parseColor("#00BCD4"));
Using this code makes all selectors of same color, so if you want to have different color selectors depending on your condition, use decorator()
Why did you use the ArrayList instead of the HashSet ? The reason you cannot instantiate Collection is because it is an interface, hence you had to create anonymous class and override the methods.
Here is how I did something similar:
This method takes in two Calendar objects and adds all the days in between the two Calendar dates into a HashSet of CalendarDays.
private HashSet<CalendarDay> getCalendarDaysSet(Calendar cal1, Calendar cal2) {
HashSet<CalendarDay> setDays = new HashSet<>();
while (cal1.getTime().before(cal2.getTime())) {
CalendarDay calDay = CalendarDay.from(cal1);
setDays.add(calDay);
cal1.add(Calendar.DATE, 1);
}
return setDays;
}
on the onCreateView(...) method,I have dynamically set the two calendar dates, the difference between which will be stored in the HashSet. However, you can pass your own random set of dates in the HashSet.
Calendar cal1 = Calendar.getInstance();
cal1.set(2016, 8, 1);
Calendar cal2 = Calendar.getInstance();
cal2.set(2016, 9, 1);
HashSet<CalendarDay> setDays = getCalendarDaysSet(cal1, cal2);
int myColor = R.color.red;
mCalendarView.addDecorator(new BookingDecorator(myColor, setDays));
In my case BookingDecorator is the class that implements the DayViewDecorator interface.
private class BookingDecorator implements DayViewDecorator {
private int mColor;
private HashSet<CalendarDay> mCalendarDayCollection;
public BookingDecorator(int color, HashSet<CalendarDay> calendarDayCollection) {
mColor = color;
mCalendarDayCollection = calendarDayCollection;
}
@Override
public boolean shouldDecorate(CalendarDay day) {
return mCalendarDayCollection.contains(day);
}
@Override
public void decorate(DayViewFacade view) {
view.addSpan(new ForegroundColorSpan(mColor));
//view.addSpan(new BackgroundColorSpan(Color.BLUE));
view.setBackgroundDrawable(ContextCompat.getDrawable(getContext(),R.drawable.greenbox));
}
}
Your post was very helpful. Hope mine helps somebody as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With