Creating A Horizontal Scrolling Calendar Using Android SDK
Often we choose to use a horizontal scrolling calendar as a date picker while building new apps. Common examples are ordering apps where an order needs to be scheduled for a later date or a simple, OneNote sort of app with the ability to pick a date and take notes. The default calendar Widget that comes with Android Studio shows a month’s worth of dates and fills up the real-estate without much effort. What is interesting is to have the calendar widget where we can select a date and have a ListView or a similar view to display cart order for a given date.
In this horizontal scrolling calendar, I have used recyclerView, which is perhaps one of the most powerful Containers present in Android SDK. It is very flexible and extensible. However, it is usually seen as quite complex to use.
Here are the screenshot and a video link of this horizontal scrolling calendar.
Here are the features it supports.
a) It shows Sundays in Red Color for easy identification.
b) You can customize it for the number of days, presently it is for 30 days, starting at 2 days before today.
c) It has two ImageViews to control the horizontal scrolling on left and right.
d) In this example, I am using Calendar data, but you can alter it to use any other data by modifying MyCalendar and MyCalendar java files.
Using a recyclerView requires us to use the RecyclerView Adapter. The adapter is fed with data that needs to be displayed in the textViews. The adapter gets initialized from the MainActivity.
In this example, two textViews are used as Child views inside the recyclerView.
The content_main.xml contains the recyclerView and the reverse and forward ImageViews.
The date_list_row contains the TextViews to show the day of the week and time.
The month and year are also stored and can be accessed using ‘get methods’ from MyCalendar data holder. The myCalendarData generates the calendar dates based on inputs from MainActivity.
public myCalendarData (int start){ this.calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, start);
setThis();}
private void setThis (){
this.startday = calendar.get(Calendar.DAY_OF_MONTH);
this.currentmonth = calendar.get(Calendar.MONTH);
this.currentyear=calendar.get(Calendar.YEAR);
this.dayofweek= calendar.get(Calendar.DAY_OF_WEEK);
this.stringDayofWeek = dateFormat.format(calendar.getTime());}
The MainActivity would call the LayoutoutManager to generate a horizontal layout instead of vertical.
// horizontal RecyclerViewfinal RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.HORIZONTAL, false);recyclerView.setLayoutManager(mLayoutManager);
The CalendarAdapter code, as shown below, has the constructor for the viewHolder and the adapter. The viewholder holds the two TextViews, which are used as childviews for each day of the calendar.
public class CalendarAdapter extends RecyclerView.Adapter<CalendarAdapter.MyViewHolder> { private List<MyCalendar> mCalendar;
public class MyViewHolder extends RecyclerView.ViewHolder {
private TextView tb_day, tb_date;
public MyViewHolder(View view) {
super(view);
tb_day = (TextView) view.findViewById(R.id.day_1);
tb_date = (TextView) view.findViewById(R.id.date_1);
}
}public CalendarAdapter(List<MyCalendar> mCalendar) {
this.mCalendar = mCalendar;
}@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.date_list_row, parent, false); return new MyViewHolder(itemView);
}
Android would call onBindViewHolder() during initializing and refresh, this helps in getting the TextView’s text set with correct values.
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
MyCalendar calendar = mCalendar.get(position); holder.tb_day.setText(calendar.getDay()); holder.tb_date.setText(calendar.getDate());}
During view creation, CreateViewHolder() is called automatically by android, so I had to override it to LayoutInflator to get the two textViews that were to be used as ChildViews.
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.date_list_row, parent, false); return new MyViewHolder(itemView);
}
One interesting and challenging aspect of recyclerView is that it refreshes by default every time the screen scrolls and repaints the childViews again, this causes the index numbers to be reset internally. One of the challenges I had due to it was to identify Sundays and mark them in red. I found a simple way of doing it by overwriting the onScrolled() method in recyclerView.addOnScrollListener(). This proved to be much similar than other ways documented in the Android developer site. Here is the code snippet:-
recyclerView
.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView,
int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int totalItemCount = mLayoutManager.getChildCount();
for (int i = 0; i < totalItemCount; i++){
View childView = recyclerView.getChildAt(i);
TextView childTextView = (TextView) (childView.findViewById(R.id.day_1));
String childTextViewText = (String) (childTextView.getText()); if (childTextViewText.equals("Sun"))
childTextView.setTextColor(Color.RED);
else
childTextView.setTextColor(Color.BLACK); }
}
});
In MainActivity, I am populating the CalendarList for 30 calendar days as described earlier.
private void prepareCalendarData() { // run a for loop for all the next 30 days of the current month starting today
// initialize mycalendarData and get Instance
// getnext to get next set of date etc.. which can be used to populate MyCalendarList in for loop myCalendarData m_calendar = new myCalendarData(-2); for ( int i=0; i <30; i++) { MyCalendar calendar = new MyCalendar(m_calendar.getWeekDay(), String.valueOf(m_calendar.getDay()), String.valueOf(m_calendar.getMonth()), String.valueOf(m_calendar.getYear()),i);
m_calendar.getNextWeekDay(1); calendarList.add(calendar); }
In MainActivity onClick() and onLongClick (), methods are overwritten from recyclerView.addOnItemTouchListener to get demonstrate the clicked date can be used for further actions in the view, such as selecting an order for a shopping app, etc..
Hope you enjoyed reading this and feel free to reuse the code as required.
Source code can be found at https://github.com/atanudasgupta/myHorizontalCalendar
Here are my other articles.
“Using an IR sensor with Arduino” by Atanu Dasgupta https://medium.com/@atanudasgupta2014/using-an-ir-sensor-with-arduino-44839d0d1db1