Welcome to the third part of Custom Calendar View in Android. In the second part we created the project settings and XML layout files. Now, we will build the project Java files and start with application database.
The first step in this part is creating SQLite open helper class. In this class we can create SQLite database and also insert, delete and select methods which are required for our project.
Custom Calendar Open Helper Class Java File
First, we need to create a public Java class that contains public static final variables for database version, columns and tables names. You can copy the following code.
public class DBStructure { public static final String DB_NAME = "EVENTS_DB"; public static final int DB_VERSION = 2; public static final String EVENT_TABLE_NAME="eventstable" ; public static final String EVENT = "event"; public static final String TIME = "time"; public static final String DATE = "date"; public static final String MONTH = "month"; public static final String YEAR = "year"; public static final String ID = "ID"; public static final String Notify = "notify"; }
Next, we need to create Database Open Helper class. To know more about SQLiteOpenHelper class click her. For that, create a new Java class and name it DBOpenHelper and this class will extend SQLiteOpenHelper class. After inheritance the red line appears nuder class name click “alt” and “enter” buttons to override two methods one for onCreate and the another for onUpgrade. After that we need to create the constructors as the following code.
public class DBOpenHelper extends SQLiteOpenHelper { private static final String CREATE_EVENTS_TABLE = "create table "+DBStructure.EVENT_TABLE_NAME+"(ID INTEGER PRIMARY KEY AUTOINCREMENT, " +DBStructure.EVENT+" TEXT, "+DBStructure.TIME+" TEXT, "+DBStructure.DATE+" TEXT, "+DBStructure.MONTH+" TEXT, " +DBStructure.YEAR+" TEXT, "+DBStructure.Notify+" TEXT)"; private static final String DROP_EVENTS_TABLE= "DROP TABLE IF EXISTS "+DBStructure.EVENT_TABLE_NAME; public DBOpenHelper(@Nullable Context context) { super(context, DBStructure.DB_NAME, null, DBStructure.DB_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_EVENTS_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(DROP_EVENTS_TABLE); onCreate(db); } public void SaveEvent(String event,String time,String date,String month,String year,String notify,SQLiteDatabase database){ ContentValues contentValues = new ContentValues(); contentValues.put(DBStructure.EVENT,event); contentValues.put(DBStructure.TIME,time); contentValues.put(DBStructure.DATE,date); contentValues.put(DBStructure.MONTH,month); contentValues.put(DBStructure.YEAR,year); contentValues.put(DBStructure.Notify,notify); database.insert(DBStructure.EVENT_TABLE_NAME,null,contentValues); } public Cursor ReadEvents(String date,SQLiteDatabase database){ String [] Projections = {DBStructure.EVENT,DBStructure.TIME,DBStructure.DATE,DBStructure.MONTH,DBStructure.YEAR}; String Selection = DBStructure.DATE +"=?"; String [] SelectionArgs = {date}; return database.query(DBStructure.EVENT_TABLE_NAME,Projections,Selection,SelectionArgs,null,null,null); } public Cursor ReadIDEvents(String date,String event, String time,SQLiteDatabase database){ String [] Projections = {DBStructure.ID,DBStructure.Notify,DBStructure.TIME}; String Selection = DBStructure.DATE +"=? and "+DBStructure.EVENT+"=? and "+DBStructure.TIME+"=?"; String [] SelectionArgs = {date,event,time}; return database.query(DBStructure.EVENT_TABLE_NAME,Projections,Selection,SelectionArgs,null,null,null); } public Cursor ReadEventsperMonth(String month,String year,SQLiteDatabase database){ String [] Projections = {DBStructure.EVENT,DBStructure.TIME,DBStructure.DATE,DBStructure.MONTH,DBStructure.YEAR}; String Selection = DBStructure.MONTH +"=? and "+DBStructure.YEAR+"=?"; String [] SelectionArgs = {month,year}; return database.query(DBStructure.EVENT_TABLE_NAME,Projections,Selection,SelectionArgs,null,null,null); } public void deleteEvent(String event,String date,String time,SQLiteDatabase database){ String selection = DBStructure.EVENT+"=? and "+DBStructure.DATE+"=? and "+DBStructure.TIME+"=?"; String[] selectionArg = {event,date,time}; database.delete(DBStructure.EVENT_TABLE_NAME,selection,selectionArg); } public void updateEvent(String date,String event, String time,String notify,SQLiteDatabase database){ ContentValues contentValues = new ContentValues(); contentValues.put(DBStructure.Notify,notify); String Selection = DBStructure.DATE +"=? and "+DBStructure.EVENT+"=? and "+DBStructure.TIME+"=?"; String [] SelectionArgs = {date,event,time}; database.update(DBStructure.EVENT_TABLE_NAME,contentValues,Selection,SelectionArgs); } }
Custom Calendar View Java File
Next, custom calendar view needs to inflate the above XML layout with java class that extents LinearLayout. The Linear Layout super class overrides three constructors. The first, is context constructor and we will use it when to cast the context when we implement it in activity or fragment. The second is with context and attributeSet. With the attributeSet constructor we will initialize the context, initialize the layout and setup the calendar view and buttons click and also the click and long click listener of the grid layout.
Initialize layout method
In this method we will inflate the custom_calendar_layout.xml file and findViewById for buttons current date text view and grid view.
//this part of code is for clarification the final code is coming below.
private void IntializeLayout(){
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.calendar_layout,this);
NextButton = view.findViewById(R.id.nextBtn);
PreviousButton = view.findViewById(R.id.previousBtn);
CurrentDate = view.findViewById(R.id.current_Date);
gridView = view.findViewById(R.id.gridview);
}
setUpCalendar Method
The setUpCalendar method requires to create new Calendar Instance as general variable. We will use this calendar instance to move through next and previous months and cast it to the Calendar Grid Adapter. Then, we need to create new Calendar with name monthCalendar and clone the general calendar in it. Then we need to set the first day of the month of monthCalndar to day 1 and get the minus values of fist day of the week and add this values and monthCalendar DAY_OF_THE MONTH. At this point, we need to iterate through 42 cell of the GridView to add each date of month calendar in dates list.
Important note, the next code will create errors because we didn’t create the GridAdapter class. So that after copping the next code to your project, create a new Java class and name it “MyGridAdapter”.
Custom Calendar View Java File
public class CalendarCustomView extends LinearLayout { ImageButton PreviouseButton,NextButton; TextView CurrentDate; GridView gridView; private static final int MAX_CALENDAR_Days = 42; SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMMM yyyy", Locale.ENGLISH); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd",Locale.ENGLISH); SimpleDateFormat monthFormat = new SimpleDateFormat("MMMM",Locale.ENGLISH); SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy",Locale.ENGLISH); Calendar calendar = Calendar.getInstance(Locale.ENGLISH); Context context; List<Events> eventsList = new ArrayList<>(); List<Date> dateList = new ArrayList<>(); DBOpenHelper dbOpenHelper; AlertDialog alertDialog; MyGridAdapter adapter; public CalendarCustomView(Context context) { super(context); } public CalendarCustomView(final Context context, @Nullable AttributeSet attrs) { super(context, attrs); this.context=context; IntializeUILayout(); SetupCalendar(); PreviouseButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { calendar.add(Calendar.MONTH,-1); SetupCalendar(); } }); NextButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { calendar.add(Calendar.MONTH,1); SetupCalendar(); } }); gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, final int position, long id) { AlertDialog.Builder builder =new AlertDialog.Builder(context); builder.setCancelable(true); View eventView = LayoutInflater.from(parent.getContext()).inflate(R.layout.new_event_layout,null); final EditText EventBody = eventView.findViewById(R.id.envent); final TextView EventTime = eventView.findViewById(R.id.eventtime); Button SelectTime = eventView.findViewById(R.id.selecttime); Button AddEvent = eventView.findViewById(R.id.addevent); SelectTime.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Calendar calendar = Calendar.getInstance(); final int hours =calendar.get(Calendar.HOUR_OF_DAY); final int minuts = calendar.get(Calendar.MINUTE); TimePickerDialog timePickerDialog; timePickerDialog = new TimePickerDialog(getContext(),R.style.DialogTheme, new TimePickerDialog.OnTimeSetListener() { @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { Calendar c = Calendar.getInstance(); c.set(Calendar.HOUR_OF_DAY,hourOfDay); c.set(Calendar.MINUTE,minute); c.setTimeZone(TimeZone.getDefault()); SimpleDateFormat format = new SimpleDateFormat("K:mm a", Locale.ENGLISH); String PlannedTime = format.format(c.getTime()); EventTime.setText(PlannedTime); } },hours,minuts,false); timePickerDialog.show(); } }); final String date = dateFormat.format(dateList.get(position)); AddEvent.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { SaveEvent(EventBody.getText().toString(),EventTime.getText().toString(),date ,monthFormat.format(dateList.get(position)),yearFormat.format(dateList.get(position))); SetupCalendar(); alertDialog.dismiss(); } }); builder.setView(eventView); alertDialog = builder.create(); alertDialog.show(); } }); gridView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { final String date = dateFormat.format(dateList.get(position)); AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setCancelable(true); View showView = LayoutInflater.from(parent.getContext()).inflate(R.layout.show_events_layout,null); RecyclerView EventRV= (RecyclerView) showView.findViewById(R.id.eventsRV); RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(showView.getContext()); EventRV.setLayoutManager(layoutManager); EventRV.setHasFixedSize(true); EventRecyclerAdapter eventRecyclerAdapter = new EventRecyclerAdapter(showView.getContext() ,CollectEvent(date)); EventRV.setAdapter(eventRecyclerAdapter); eventRecyclerAdapter.notifyDataSetChanged(); builder.setView(showView); alertDialog =builder.create(); alertDialog.show(); return true; } }); } public CalendarCustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } private ArrayList<Events> CollectEvent(String date){ ArrayList<Events> arrayList = new ArrayList<>(); dbOpenHelper = new DBOpenHelper(context); SQLiteDatabase sqLiteDatabase = dbOpenHelper.getReadableDatabase(); Cursor cursor = dbOpenHelper.ReadEvent(date,sqLiteDatabase); while (cursor.moveToNext()){ String event = cursor.getString(cursor.getColumnIndex(DBStructure.EVENT)); String Time = cursor.getString(cursor.getColumnIndex(DBStructure.TIME)); String Date = cursor.getString(cursor.getColumnIndex(DBStructure.DATE)); String month = cursor.getString(cursor.getColumnIndex(DBStructure.MONTH)); String year = cursor.getString(cursor.getColumnIndex(DBStructure.YEAR)); Events events = new Events(event,Time,Date,month,year); arrayList.add(events); } cursor.close(); dbOpenHelper.close(); // Toast.makeText(context, String.valueOf(arrayList.size()), Toast.LENGTH_SHORT).show(); return arrayList; } private void IntializeUILayout(){ LayoutInflater inflater =(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.calendar_layout,this); PreviouseButton = view.findViewById(R.id.previous_month); NextButton = view.findViewById(R.id.next_month); CurrentDate = view.findViewById(R.id.display_current_date); gridView = view.findViewById(R.id.calendar_grid); } private void SetupCalendar(){ String StartDate = simpleDateFormat.format(calendar.getTime()); CurrentDate.setText(StartDate); dateList.clear(); Calendar monthCalendar = (Calendar)calendar.clone(); monthCalendar.set(Calendar.DAY_OF_MONTH,1); int FirstDayOfMonth = monthCalendar.get(Calendar.DAY_OF_WEEK)-1; monthCalendar.add(Calendar.DAY_OF_MONTH,-FirstDayOfMonth); COllectEventsPerMonth(monthFormat.format(calendar.getTime()),yearFormat.format(calendar.getTime())); while (dateList.size() < MAX_CALENDAR_Days){ dateList.add(monthCalendar.getTime()); monthCalendar.add(Calendar.DAY_OF_MONTH,1); } adapter = new MyGridAdapter(context,dateList,calendar,eventsList); gridView.setAdapter(adapter); } private void SaveEvent(String event,String time,String date,String Month,String Year){ dbOpenHelper = new DBOpenHelper(context); SQLiteDatabase database = dbOpenHelper.getWritableDatabase(); dbOpenHelper.SaveEvent(event,time,date,Month,Year,database); dbOpenHelper.close(); Toast.makeText(context, "Event Saved", Toast.LENGTH_SHORT).show(); } private Date convertStringToDate(String dateInString){ java.text.SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH); Date date = null; try { date = format.parse(dateInString); } catch (java.text.ParseException e) { e.printStackTrace(); } return date; } private void COllectEventsPerMonth(String Month,String Year){ eventsList.clear(); dbOpenHelper = new DBOpenHelper(context); SQLiteDatabase database = dbOpenHelper.getReadableDatabase(); Cursor cursor = dbOpenHelper.ReadEventpermonth(Month,Year,database); while (cursor.moveToNext()){ String event = cursor.getString(cursor.getColumnIndex(DBStructure.EVENT)); String Time = cursor.getString(cursor.getColumnIndex(DBStructure.TIME)); String Date = cursor.getString(cursor.getColumnIndex(DBStructure.DATE)); String month = cursor.getString(cursor.getColumnIndex(DBStructure.MONTH)); String year = cursor.getString(cursor.getColumnIndex(DBStructure.YEAR)); Events events = new Events(event,Time,Date,month,year); eventsList.add(events); } } }
MyGridAdapter Java Class
From the previous class we note that MyGridAdapter class that used in the setupCalendar Method depends on a List of Dates. This list has created by looping through the maximum calendar days (42 days). Furthermore, in each time we add in the dateList the monthCalendar Time followed by increasing the month calendar day value by one. The second dependency of the GridAdapter class is the updated calendar instance. Finally, the list of events that we collected it trough the method COllectEventsPerMonth().
public class MyGridAdapter extends ArrayAdapter {
List<Date> dates ;
Calendar currentDate;
LayoutInflater inflater;
List<Events> eventsList;
public MyGridAdapter( Context context,List<Date> dates,Calendar currentDate,List<Events> eventsList) {
super(context,R.layout.single_cell_layout);
this.dates = dates;
this.currentDate = currentDate;
inflater = LayoutInflater.from(context);
this.eventsList=eventsList;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Date monthDate = dates.get(position);
Calendar dateCalendar = Calendar.getInstance();
dateCalendar.setTime(monthDate);
int dayNo = dateCalendar.get(Calendar.DAY_OF_MONTH);
int displayMonth = dateCalendar.get(Calendar.MONTH)+1;
int displayYear = dateCalendar.get(Calendar.YEAR);
int currentYear = currentDate.get(Calendar.YEAR);
int currentMonth = currentDate.get(Calendar.MONTH)+1;
View view = convertView;
if(view == null){
view = inflater.inflate(R.layout.single_cell_layout,parent,false);
}
if (displayMonth == currentMonth && displayYear==currentYear){
view.setBackgroundColor(getContext().getResources().getColor(R.color.green));
}
else {
view.setBackgroundColor(Color.parseColor("#cccccc"));
}
TextView cellNumber = view.findViewById(R.id.calendat_day);
TextView eventText = view.findViewById(R.id.event_id);
cellNumber.setText(String.valueOf(dayNo));
Calendar eventCalendar = Calendar.getInstance();
ArrayList<String> arrayList = new ArrayList<>();
for (int i = 0;i < eventsList.size();i++){
eventCalendar.setTime(convertStringToDate(eventsList.get(i).getDate()));
if(dayNo == eventCalendar.get(Calendar.DAY_OF_MONTH) && displayMonth == eventCalendar.get(Calendar.MONTH)+1
&& displayYear == eventCalendar.get(Calendar.YEAR)){
arrayList.add(eventsList.get(i).getEvent());
eventText.setText(arrayList.size()+" events");
}
}
return view;
}
@Override
public int getCount() {
return dates.size();
}
@Nullable
@Override
public Object getItem(int position) {
return dates.get(position);
}
@Override
public int getPosition(@Nullable Object item) {
return dates.indexOf(item);
}
private Date convertStringToDate(String dateInString){
java.text.SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
Date date = null;
try {
date = format.parse(dateInString);
} catch (java.text.ParseException e) {
e.printStackTrace();
}
return date;
}
}
Events Recycler Adapter class
The next step, we need to show the events that we will add it in the calendar by long press method. This class is dependent on a List of Events Model. So, we first need to create a new Java class for events model the create a new one for Events Recycler Adapter.
Events Model Class:
public class Events { String Event, Time,Date,Month,Year; public Events(String event, String time, String date, String Month, String Year) { Event = event; Time = time; Date = date; } public String getMonth() { return Month; } public void setMonth(String month) { Month = month; } public String getYear() { return Year; } public void setYear(String year) { Year = year; } public String getEvent() { return Event; } public void setEvent(String event) { Event = event; } public String getTime() { return Time; } public void setTime(String time) { Time = time; } public String getDate() { return Date; } public void setDate(String date) { Date = date; } }
Events Recycler adapter class:
public class EventRecyclerAdapter extends RecyclerView.Adapter<EventRecyclerAdapter.MyViewHolder> {
Context context;
ArrayList<Events> arrayList;
public EventRecyclerAdapter(Context context, ArrayList<Events> arrayList) {
this.context = context;
this.arrayList = arrayList;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.event_rowlayout,parent,false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
Events events = arrayList.get(position);
holder.Event.setText(events.getEvent());
holder.Time.setText(events.getTime());
holder.Date.setText(events.getDate());
}
@Override
public int getItemCount() {
return arrayList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder{
TextView Event,Date,Time;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
Event = itemView.findViewById(R.id.event);
Date = itemView.findViewById(R.id.date);
Time = itemView.findViewById(R.id.eventtime);
}
}
}
Main Activity Java class
Finally, in main activity java class we need to initiate the custom calendar view class. Make sure that you added the custom calendar view in main activity xml layout file.
public class MainActivity extends AppCompatActivity {
CalendarCustomView calendarCustomView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
calendarCustomView = findViewById(R.id.custom_calendar);
}
}
after that, run your app and if your encountered errors please comment.
Thank you
Podrías enviarme el código por favor, tuve unos errores, gracias.
eligu1977@gmail.com
can you please send me the code by email?
I got an error in handler.java
Error in NonNull
and Nullable
can i use this code in firebase?
yes you can.
Something is a little strange between the
private void SaveEvent
and the
private void CollectEventsPerMonth
nomenclature. Im getting some errors on these. There is a lot of variation of the use of capitol and lower case letters and I dont know if thats the problem or what? Can you check what you have here and see if it needs to be updated. It seems different than what was entered in the videos too.
My app keep crashing with no error, how do i fix this
send me the errors that appear in logcat after app crashing.
Hi there,
Happy New Year!
Congrats for your very helpful tutorials..!
About the calendar.. The add event works like “you have to press on the date and the add new event opens like an alert dialog “..! What if in this dialog you have a list of info that you must set? I mean like, a list of events that you must select from there, location and more that must be added!
Is it possible this layout to have more info? Can this be supported in this way of implementation?
Thank you in advance for your time!
Hi Thalia, In this case you need to direct the user to a new activity and you can add all required data about the event, you can get and send the date of the pressed day on the calendar then send it with intent to the new activity and allow the user to save the data to the database.
EventRecyclerAdapter can you give full code your code is different in youtube video and here
i couldn’t get the gridview
I cannot use your code, please send your source code to my email address 785773559@qq.com