Creating Material Designed Event Calender Part-2

Chintan Patel
Kotlindroid
Published in
6 min readFeb 26, 2018

Hello Developers, In this 2nd part of the material designed Event Calender, We’ll implement the main code part which shows dates and events for specific dates.

Let’s Start to implement our main function updateEventView() in CalenderView class.

Now, we’ll create variable named selectedCalender which will be used as current month calender and to count previous moth and next moth extra days to fill it out in week days. So Let’s start.

var selectedCalender: Calendar = Calendar.getInstance()
//This will get current calender time of system
selectedCalender.set(calender.get(Calendar.YEAR), calender.get(Calendar.MONTH), calender.get(Calendar.DATE))
//This will set selectedCalender to selected month and year by next/previous buttons
//This will set date to first
selectedCalender.set(Calendar.DATE, 1)
selectedCalender.set(Calendar.HOUR, 0) selectedCalender.set(Calendar.MINUTE, 0) selectedCalender.set(Calendar.SECOND, 0)
calender.set(Calendar.DATE, 1)//This will count total days in selected month
var totalDaysToShow: Int = calender.getActualMaximum(Calendar.DAY_OF_MONTH)
//total days extra to be added from previous month
val toFillInPreviousMonthDays = 1 - calender.get(Calendar.DAY_OF_WEEK)

calender.set(Calendar.DATE, totalDaysToShow)

//total days extra to be added from next month
val
toFillInNextMonthDays = 7 - calender.get(Calendar.DAY_OF_WEEK)

//total days to count max weeks in a month
totalDaysToShow += Math.abs(toFillInPreviousMonthDays) + toFillInNextMonthDays

calender.set(Calendar.DATE, 1)

if (toFillInPreviousMonthDays != 0) {
selectedCalender.add(Calendar.DAY_OF_YEAR, toFillInPreviousMonthDays)
}

Now, count max. number of weeks in a selected month do the below code which will give to show number of week layouts in a view and based on this make extra weeks visible or hidden and remove all the events which added before.

val totalRows = totalDaysToShow.div(7)when (totalRows) {
4 -> {
dayViewRow5.visibility = View.GONE
dayViewRow6.visibility = View.GONE
}
5 -> {
dayViewRow5.visibility = View.VISIBLE
dayViewRow6.visibility = View.GONE
}
else -> {
dayViewRow5.visibility = View.VISIBLE
dayViewRow6.visibility = View.VISIBLE
}
}
val dateFormatter = SimpleDateFormat(dateFormat, Locale.getDefault())

(dayViewRow1.findViewById(R.id.layout_tripEvents) as LinearLayout).removeAllViews()
(dayViewRow2.findViewById(R.id.layout_tripEvents) as LinearLayout).removeAllViews()...(dayViewRow6.findViewById(R.id.layout_tripEvents) as LinearLayout).removeAllViews()

Now, Check for the days and date and on base of this, add event views in the week row on specific date or in between dates.

for (i in 1..totalDaysToShow step 7) {
when (i) {
in 1..7 -> {
val layout_tripEvents = dayViewRow1.findViewById(R.id.layout_tripEvents) as LinearLayout
...
}

in 8..14 -> {
val layout_tripEvents = dayViewRow2.findViewById(R.id.layout_tripEvents) as LinearLayout
...
}
... in 36..42 -> {
val layout_tripEvents = dayViewRow6.findViewById(R.id.layout_tripEvents) as LinearLayout
...
}
}

Now, In a specific week row, assuming 1st row (i.e. 1..7), we’ll show dates falling on that week (i.e. 26, 27, 28, 1, 2, 3, 4) which are 26–28 are of previous month while 1–4 are of current month. So let code for 1st Week row and other’s will be the same except defining dayViewRow(n) as per the week n.

val layout_tripEvents = dayViewRow1.findViewById(R.id.layout_tripEvents) as LinearLayout

//will take this date as min. date of this week row
val minDate = selectedCalender.time

val day1 = dayViewRow1.findViewById(R.id.day1) as TextView
day1.text = selectedCalender.get(Calendar.DATE).toString()
selectedCalender = addSingleDay(selectedCalender)

var widthOfText = day1.measuredWidth

val day2 = dayViewRow1.findViewById(R.id.day2) as TextView
day2.text = selectedCalender.get(Calendar.DATE).toString()
selectedCalender = addSingleDay(selectedCalender)

val day3 = dayViewRow1.findViewById(R.id.day3) as TextView
day3.text = selectedCalender.get(Calendar.DATE).toString()
selectedCalender = addSingleDay(selectedCalender)

...

val day6 = dayViewRow1.findViewById(R.id.day6) as TextView
day6.text = selectedCalender.get(Calendar.DATE).toString()
selectedCalender = addSingleDay(selectedCalender)
//will take 7th day date as max. date of this week
val maxDate = selectedCalender.time

val day7 = dayViewRow1.findViewById(R.id.day7) as TextView
day7.text = selectedCalender.get(Calendar.DATE).toString()
selectedCalender = addSingleDay(selectedCalender)

Using above code, we’ve set dates fallen in this 1st week. Create addSingleDay(), isDateInBetween(), isSameDay(), getDaysBetween() function to add single day, and calculate dates and gap between dates in a calender in CalenderView class file.

private fun addSingleDay(calendar: Calendar): Calendar {
val currentDayOfMonth = calendar.get(Calendar.DAY_OF_MONTH)
val currentDayOfYear = calendar.get(Calendar.DAY_OF_YEAR)

val copyCalender: Calendar = calendar.clone() as Calendar
copyCalender.set(Calendar.DATE, 1)

val maxDaysInMonth = copyCalender.getMaximum(Calendar.DAY_OF_MONTH)

if (currentDayOfMonth == maxDaysInMonth) {
if (currentDayOfYear == calendar.getActualMaximum(Calendar.DAY_OF_YEAR)) {
calendar.add(Calendar.YEAR, 1)
calendar.set(Calendar.MONTH, Calendar.JANUARY)
calendar.set(Calendar.DAY_OF_YEAR, 1)
} else {
calendar.add(Calendar.MONTH, 1)
calendar.set(Calendar.DATE, 1)
}
} else {
calendar.add(Calendar.DAY_OF_YEAR, 1)
}

return calendar
}
private fun isDateInBetween(date: Date, minDate: Date, maxDate: Date): Boolean {
date.hours = 0; date.minutes = 0; date.seconds = 0
minDate.hours = 0; minDate.minutes = 0; minDate.seconds = 0
maxDate.hours = 0; maxDate.minutes = 0; maxDate.seconds = 0
if (isSameDay(date, minDate) || isSameDay(date, maxDate)) {
return true
}
if (date.compareTo(minDate) >= 0 && date.compareTo(maxDate) <= 0) {
return true
}
return false
}

private fun isSameDay(date1: Date, date2: Date): Boolean {
val calender1 = Calendar.getInstance()
val calender2 = Calendar.getInstance()
calender1.time = date1; calender2.time = date2
return calender1.get(Calendar.YEAR) == calender2.get(Calendar.YEAR) && calender1.get(Calendar.DAY_OF_YEAR) == calender2.get(Calendar.DAY_OF_YEAR)
}

private fun getDaysBetween(startDate: Date, endDate: Date): Int {
startDate.hours = 0; startDate.minutes = 0; startDate.seconds = 0
endDate.hours = 0; endDate.minutes = 0; endDate.seconds = 0

val diff = endDate.time - startDate.time

return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS).toInt()
}

Now, Create layout named layout_event_line.xml to draw event view on week days.

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardElevation="3dp"
app:cardCornerRadius="2dp"
>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:layout_marginTop="2dp"
android:background="@color/colorAccent"
android:gravity="center"
android:id="@+id/txt_eventTitle"
android:singleLine="true"
android:textColor="@color/white"
android:textSize="14sp"
/>

</android.support.v7.widget.CardView>

Now, Continuing on the 1st week row’s code as this will add event views on the dates.

if (!firstTime) {
day1.postDelayed({
widthOfText = day1.measuredWidth
var eventAddCount = 0
//this will check for all events falls in this week
for
(j in 0 until eventList.size) {
val startDate = dateFormatter.parse(eventList.get(j).start)
val endDate = dateFormatter.parse(eventList.get(j).end)

if (eventAddCount == MAX_EVENT) {
break
}

startDate.hours = 0; startDate.minutes = 0; startDate.seconds = 0
endDate.hours = 0; endDate.minutes = 0; endDate.seconds = 0

//At this position of code, we'll do step by step as shown below

}
}, POST_TIME)
}

Now, doing step by step code at the position shown above, 1st we’ll fetch event line view and set background color and title for event with click listener. We’ll implement event click listener later.

val eventTrip = layoutInflater.inflate(R.layout.layout_event_line, null) as CardView
val eventTitle = eventTrip.findViewById(R.id.txt_eventTitle) as TextView
eventTrip.setCardBackgroundColor(Color.parseColor(eventList.get(j).color))
eventTitle.setBackgroundColor(Color.parseColor(eventList.get(j).color))
eventTitle.text = eventList.get(j).title
eventTitle.setOnClickListener {
eventClickListener
?.onEventClick(eventList[j])
}

Now, Check if start date of an event falls in between of min date of a week and ax date of a week, on base of that this will draw an event line from starting date to end of a week.

if (isDateInBetween(startDate, minDate, maxDate)) {

val daysBetween = if (isSameDay(minDate, startDate)) 0 else getDaysBetween(minDate, startDate) + 1

val startMarginDays = (widthOfText * daysBetween) + EXTRA_MARGIN
var
endMarginDays = 0

//Below condition will check for end days is a week's last day or in between of start day and max day
if (isDateInBetween(endDate, minDate, maxDate)) {
endMarginDays = (widthOfText * getDaysBetween(endDate, maxDate)) + EXTRA_MARGIN
}

val params = LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
params.setMargins(startMarginDays, 5, endMarginDays, 5)
layout_tripEvents.addView(eventTrip, params)

eventAddCount++

}

If above condition will false to check whether start date of an event is not in between min & max dates of a week, then it will check for end date of event comes in between of min & max dates of a week.

else if (isDateInBetween(endDate, minDate, maxDate)) {
val startMarginDays = 0
val endMarginDays = (widthOfText * getDaysBetween(endDate, maxDate)) + EXTRA_MARGIN

val
params = LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
params.setMargins(startMarginDays, 5, endMarginDays, 5)
layout_tripEvents.addView(eventTrip, params)
eventAddCount++
}

If above condition gets true, it will draw event line from week start day to the end date comes on the week day. Even if this condition gets false, it will check for last condition which checks if min. date of week falls under start date of an event and max date of a week AND max. date of week falls under start date of an event and end date of event.

else if (isDateInBetween(minDate, startDate, maxDate) && isDateInBetween(maxDate, startDate, endDate)) {

val startMarginDays = 0
val endMarginDays = 0

val params = LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
params.setMargins(startMarginDays, 5, endMarginDays, 5)
layout_tripEvents.addView(eventTrip, params)
eventAddCount++
}

If above condition will executed, then it will draw line on whole week.

Now, create a function through we can add events and show it on the calender. So create functions in CalenderView class file.

fun addEventList(eventListItems: ArrayList<EventItem>, clearOldData: Boolean = false) {
if (clearOldData) eventList.clear()
(dayViewRow1.findViewById(R.id.layout_tripEvents) as LinearLayout).removeAllViews()
(dayViewRow2.findViewById(R.id.layout_tripEvents) as LinearLayout).removeAllViews()
...
(dayViewRow6.findViewById(R.id.layout_tripEvents) as LinearLayout).removeAllViews()

val sortedList = eventListItems.sortedWith(compareBy { it.getStartDateToSort() })

eventList.clear()
eventList.addAll(sortedList)
updateEventView()
}

This code will draw events with different provided colors and titles on it.

Now, we’ve successfully created the custom Event Calender but not implemented yet in our application. So to view, how to implement this view with it’s different methods, We’ll go through Part 3.

Thank you for your time to view this code and read article. If you liked this, Please give 1, 2, 5 or 50… claps for this article. Thank you for your support.

--

--

Chintan Patel
Kotlindroid

Project Manager | Content Writer | Mentor | Senior Mobile App Developer | Enthusiastic | Energetic | Public Speaker