In our internal company mobile project – FreQuest – an app for managing holidays, we needed to use a calendar widget. Of course we could use a classic CalendarView but the main problem was that this widget isn’t fully customizable. We had to use two types of calendar widgets: a date picker to select dates and a classic calendar to show absence days in the form of icons below the number of the day. That’s why we created our calendar widget, called MaterialCalendarView and shared it on Github:

calendar widget android github repo

 

In this post I will describe how to create a simple calendar app using our library.

 

How to start?

First thing we have to do is import the Material CalendarView library. To do it, open the project’s build.gradle file and make sure you have define the jcenter repository like below:

allprojects {
    repositories {
        jcenter()
    }
}

If yes, open the module’s build.gradle file and simply add the following line to the dependency part:

dependencies {
    compile 'com.applandeo:material-calendar-view:1.0.1'
}

Creating calendar app

Our simple app will consists of three views. The first one will contain the main calendar where we display our notes in form of icons. The second one will be a view which let us create a new note. The last view will let as preview that note.

As you can see in the first picture we are using a floating action button. Before you start you have to add compile ‘com.android.support:design:25.3.1’ to your dependency. If you do this you can start creating views:

 

activity.main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:context="com.applandeo.myapplication.MainActivity">
 
 <com.applandeo.materialcalendarview.CalendarView
   android:id="@+id/calendarView"
   android:layout_width="0dp"
   android:layout_height="0dp"
   app:headerColor="@color/colorPrimary"
   app:layout_constraintBottom_toBottomOf="parent"
   app:layout_constraintLeft_toLeftOf="parent"
   app:layout_constraintRight_toRightOf="parent"
   app:layout_constraintTop_toTopOf="parent" />
 
 <android.support.design.widget.FloatingActionButton
   android:id="@+id/floatingActionButton"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_margin="@dimen/margin_16"
   android:src="@drawable/ic_add_white_24dp"
   app:layout_constraintBottom_toBottomOf="parent"
   app:layout_constraintRight_toRightOf="parent" />
 
</android.support.constraint.ConstraintLayout>

add_note_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
 
   <com.applandeo.materialcalendarview.CalendarView
       android:id="@+id/datePicker"
       android:layout_width="0dp"
       android:layout_height="370dp"
       app:datePicker="true"
       app:headerColor="@color/colorPrimary"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent"
       app:selectionColor="@color/colorAccent"
       app:todayLabelColor="@color/colorPrimaryDark" />
 
   <android.support.v7.widget.AppCompatButton
       android:id="@+id/addNoteButton"
       android:layout_width="0dp"
       android:layout_height="wrap_content"
       android:text="@string/save"
       android:textColor="@android:color/white"
       app:backgroundTint="@color/colorAccent"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent" />
 
   <EditText
       android:id="@+id/noteEditText"
       android:layout_width="0dp"
       android:layout_height="72dp"
       android:layout_margin="@dimen/margin_8"
       android:background="@null"
       android:gravity="top"
       android:hint="@string/write_note"
       app:layout_constraintBottom_toTopOf="@id/addNoteButton"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toBottomOf="@id/datePicker" />
 
</android.support.constraint.ConstraintLayout>

note_preview_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
 
   <TextView
       android:id="@+id/note"
       android:layout_width="0dp"
       android:layout_height="wrap_content"
       android:layout_margin="@dimen/margin_16"
       android:hint="@string/no_note"
       android:textSize="24sp"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent" />
 
</android.support.constraint.ConstraintLayout>

 

I don’t need to describe all lines of the code because I assume you understand it and I will only focus on <com.applandeo.materialcalendarview.CalendarView>. As you can see our widget let you customize its header color, today’s number color, selection color in the picker mode and more. A list of all available tags is in our GitHub’s README. Another important tag is app:datePicker, by default its attribute is set false what means that we use the CalendarView as a standard calendar. If you set it true, as we did in the add_note_activity.xml, you can use it as a date picker that let you select date and then getting it using appropriate method I will describe in the further part.

If we have created views already we can take a look at the Java code:

 

MainActivity

public class MainActivity extends AppCompatActivity {
   public static final String RESULT = "result";
   public static final String EVENT = "event";
   private static final int ADD_NOTE = 44;
 
   private CalendarView mCalendarView;
   private List<EventDay> mEventDays = new ArrayList<>();
 
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
 
       mCalendarView = (CalendarView) findViewById(R.id.calendarView);
 
       FloatingActionButton floatingActionButton = (FloatingActionButton) findViewById(R.id.floatingActionButton);
       floatingActionButton.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               addNote();
           }
       });
 
       mCalendarView.setOnDayClickListener(new OnDayClickListener() {
           @Override
           public void onDayClick(EventDay eventDay) {
               previewNote(eventDay);
           }
       });
   }
 
   @Override
   protected void onActivityResult(int requestCode, int resultCode, Intent data) {
       if (requestCode == ADD_NOTE && resultCode == RESULT_OK) {
           MyEventDay myEventDay = data.getParcelableExtra(RESULT);
           mCalendarView.setDate(myEventDay.getCalendar());
           mEventDays.add(myEventDay);
           mCalendarView.setEvents(mEventDays);
       }
   }
 
   private void addNote() {
       Intent intent = new Intent(this, AddNoteActivity.class);
       startActivityForResult(intent, ADD_NOTE);
   }
 
   private void previewNote(EventDay eventDay) {
       Intent intent = new Intent(this, NotePreviewActivity.class);
       if(eventDay instanceof MyEventDay){
           intent.putExtra(EVENT, (MyEventDay) eventDay);
       }
       startActivity(intent);
   }
}

As you may notice in the main class we are using three methods available in our CalendarView: setEvents(), setOnDayClickListener() and setDate(). As the name suggests setEvents() method adds events to the calendar. To use it we have to create a list of EventDay objects and pass it to the method.

List<EventDay> mEventDays = new ArrayList<>();

CalendarView mCalendarView = (CalendarView) findViewById(R.id.calendarView);
MyEventDay myEventDay = …
 
mEventDays.add(myEventDay);
mCalendarView.setEvents(mEventDays);

EventDay object’s constructor takes two variables – a date in the form of Calendar object and an image resource:

new EventDay(Calendar day, int imageResource);

 

In our sample app we have to add a note to the created event yet. That’s why we have to extend EventDay as in example below:

class MyEventDay extends EventDay implements Parcelable {
   private String mNote;
 
   MyEventDay(Calendar day, int imageResource, String note) {
       super(day, imageResource);
       mNote = note;
   }
 
   String getNote() {
       return mNote;
   }
 
   private MyEventDay(Parcel in) {
       super((Calendar) in.readSerializable(), in.readInt());
       mNote = in.readString();
   }
 
   public static final Creator<MyEventDay> CREATOR = new Creator<MyEventDay>() {
       @Override
       public MyEventDay createFromParcel(Parcel in) {
           return new MyEventDay(in);
       }
 
       @Override
       public MyEventDay[] newArray(int size) {
           return new MyEventDay[size];
       }
   };
 
   @Override
   public void writeToParcel(Parcel parcel, int i) {
       parcel.writeSerializable(getCalendar());
       parcel.writeInt(getImageResource());
       parcel.writeString(mNote);
   }
 
   @Override
   public int describeContents() {
       return 0;
   }
}

*Parcelable interface is needed to pass MyEventDays through Intents.

 

The second method, setOnDayClickListener() is responsible for handling clicks. When user taps any day row listener will return EventDay object with appropriate date and note.

mCalendarView.setOnDayClickListener(new OnDayClickListener() {
   @Override
   public void onDayClick(EventDay eventDay) {
       ...
   }
});

The third method simply scrolls the calendar to a proper calendar page. In our case to date selected in the AddNoteActivity.

mCalendarView.setDate(myEventDay.getCalendar());

 

AddNoteActivity

public class AddNoteActivity extends AppCompatActivity {
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.add_note_activity);
 
       final CalendarView datePicker = (CalendarView) findViewById(R.id.datePicker);
       Button button = (Button) findViewById(R.id.addNoteButton);
       final EditText noteEditText = (EditText) findViewById(R.id.noteEditText);
 
       button.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               Intent returnIntent = new Intent();
 
               MyEventDay myEventDay = new MyEventDay(datePicker.getSelectedDate(),
                       R.drawable.ic_message_black_48dp, noteEditText.getText().toString());
 
               returnIntent.putExtra(MainActivity.RESULT, myEventDay);
               setResult(Activity.RESULT_OK, returnIntent);
               finish();
           }
       });
   }
}

In this class we are creating MyEventDay objects using another important method that is getSelectedDate(). When the CalendarView is in the picker mode we can use that method to get selected date in the form of Calendar object.

MyEventDay myEventDay = 
new MyEventDay(datePicker.getSelectedDate(), R.drawable.ic_message_black_48dp, noteEditText.getText().toString());

 

NotePreviewActivity

public class NotePreviewActivity extends AppCompatActivity {
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.note_preview_activity);
 
       Intent intent = getIntent();
 
       TextView note = (TextView) findViewById(R.id.note);
 
       if (intent != null) {
           Object event = intent.getParcelableExtra(MainActivity.EVENT);
 
           if(event instanceof MyEventDay){
               MyEventDay myEventDay = (MyEventDay)event;
 
               getSupportActionBar().setTitle(getFormattedDate(myEventDay.getCalendar().getTime()));
               note.setText(myEventDay.getNote());
 
               return;
           }
 
           if(event instanceof EventDay){
               EventDay eventDay = (EventDay)event;
               getSupportActionBar().setTitle(getFormattedDate(eventDay.getCalendar().getTime()));
           }
       }
   }
 
   public static String getFormattedDate(Date date) {
       SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.getDefault());
       return simpleDateFormat.format(date);
   }
}

In the last one class we only display informations from MyEventDay object, nothing more.
 

To sum up

That’s all, simple calendar and simple usage. Of course you can expand that sample app, for instance implement data base or even if you want you can improve our Material CalendarView. It’s open source, feel free!