Android Architecture Components
A new collection of libraries that help you design robust, testable, and maintainable apps. — developer.android.com
We will see three main components :
- Room
- ViewModel
- LiveData
So first, let’s find out what these components actually are. Then, we’ll learn how we can use them.
Get Source code From HERE.
By creating an Event app I will demonstrate you the working of Android Architecture Components Room, ViewModel, LiveData.
Room Persistence Library
Major problem with SQLite usage is
- There is no compile-time verification of raw SQL queries.For example if you write a SQL query with a wrong column name that does not exist in real database then it will give exception during run time and you can not capture this issue during compile time.
- As your schema changes you need to update the affected SQL queries manually. This process can be time consuming and error prone.
- You need to use lots of boilerplate code to convert between SQL queries and Java data objects.
Room takes care of these concerns for you while providing an abstraction layer over SQLite.
There are three major components in Room:
Entity : A class annotated with the @Entity annotation is mapped to a table in database. Every entity is persisted in its own table and every field in class represents the column name.
- tableName attribute is used to define the name of the table
- Every entity class must have at-least one Primary Key field, annotated with @PrimaryKey
- Fields in entity class can be annotated with @ColumnInfo(name = “name_of_column”) annotation to give specific column names
- If you don’t want to include some field for the table but want to keep it in your model class than annotate those field with @Ignore
- If you want your data to get stored in some other format than that of the type of variable you defined in your model class than create another class add your conversion logic there and annotate those methods with @TypeConverter than add that class to your database class using annotation @TypeConverters.
@Entity(tableName = TABLE_NAME) public class Event { public static final String TABLE_NAME = "events"; @PrimaryKey(autoGenerate = true) private int id; private String title; private String description; @TypeConverters(DateConverter.class) private Date date; public Event() { } @Ignore public Event(int id, String title, String description) { this.id = id; this.title = title; this.description = description; this.date = date; } public int getId() { return id; } public void setId(int id) { this.id = id; } public void setTitle(String title) { this.title = title; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public void setDescription(String description) { this.description = description; } public String getDescription() { return description; } public String getTitle() { return title; }
DAO : Data Access Object is either be an interface or an abstract class annotated with @Doa annotation, containing all the methods to define the operations to be performed on data. The methods can be annotated with
- @Query to retrieve data from database
- @Insert to insert data into database
- @Delete to delete data from database
- @Update to update data in database
Note:The result of SQLite queries are composed into cursor object, DAO methods abstract the conversion of cursor to Entity objects and vice-versa.
@Dao public interface EventDao { @Query("SELECT * FROM " + Event.TABLE_NAME) LiveData<List<Event>> getEvents(); @Insert(onConflict = REPLACE) void addEvent(Event event); @Delete void deleteEvent(Event event); @Update(onConflict = REPLACE) void updateEvent(Event event); }
In case there are conflicts during such manipulation operations, we have to specify a conflict strategy too. In our example, we are using REPLACE. It means that the conflicting entry will be replaced by the current entry.
Database : Database is a container for tables. An abstract class annotated with @Database annotation is used to create a database with given name along with database version.
- version = intValueForDBVersion is used to define the database version
- entities = {EntityClassOne.class, ….} is used to define list of entities for database
@Database(entities = {Event.class}, version = 1) public abstract class EventDatabase extends RoomDatabase { private static final String DB_NAME = "Event_Database.db"; private static EventDatabase INSTANCE; public static EventDatabase getEventDatabase(Context context) { if (INSTANCE == null) { INSTANCE = Room.databaseBuilder(context, EventDatabase.class, DB_NAME).build(); } return INSTANCE; } public static void destroyInstance() { INSTANCE = null; } public abstract EventDao eventDao(); }
ViewModel
The ViewModel class is designed to hold and manage UI-related data in a life-cycle conscious way. This allows data to survive configuration changes such as screen rotations.
Previously, you might have used onRetainNonConfigurationInstance to save this data during a configuration change and unpack it on the other end.
The ViewModel exists when you first request a ViewModel (usually in the onCreate the Activity) until the Activity is finished and destroyed. onCreate may be called several times during the life of an Activity, such as when the app is rotated, but the ViewModel survives throughout.
There are two steps to setting up and using a ViewModel:
Step 1: Create a ViewModel class
public class EventViewModel extends AndroidViewModel { private EventDatabase eventDatabase; private LiveData<List<Event>> eventList; public EventViewModel(@NonNull Application application) { super(application); eventDatabase = EventDatabase.getEventDatabase(this.getApplication()); eventList = eventDatabase.eventDao().getEvents(); } public LiveData<List<Event>> getEventList() { return eventList; } public EventDatabase getEventDatabase() { return eventDatabase; } }
Step 2: Set up communications between your ViewModel and your UI controller and Use the ViewModel in your UI Controller.
private EventDatabase eventDatabase; private EventViewModel viewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewModel = ViewModelProviders.of(this).get(EventViewModel.class); eventDatabase = viewModel.getEventDatabase(); viewModel.getEventList();
The first time the ViewModelProviders.of method is called by MainActivity, it creates a new ViewModel instance. When this method is called again, which happens whenever onCreate is called, it will return the pre-existing ViewModel associated with the specific MainActivity. This is what preserves the data.
LiveData
LiveData is an observable lifecycle-aware data holder class. Your UI code subscribes to changes in the underlying data, tied into a LifecycleOwner, and LiveData makes sure the observer:
- Gets updates to the data while the Lifecycle is in an active state (STARTED or RESUMED)
- Is removed when the LifecycleOwner is destroyed
- Gets up-to-date data when the LifecycleOwnerrestarts due to a configuration change or is restarted from the back stack
Create LiveData objects
Inside Room DAO I have defined List<Event> as LiveData object.
@Query("SELECT * FROM " + Event.TABLE_NAME) LiveData<List<Event>> getEvents();
Observe LiveData objects
viewModel.getEventList().observe(this, new Observer<List<Event>>() { @Override public void onChanged(@Nullable List<Event> eventList) { events = eventList; adapter = new EventsAdapter(events, getApplicationContext()); recyclerView.setAdapter(adapter); checkListEmptyOrNot(); } });
Whenever there is a change in the data, the onChanged() callback is executed and we get the new data.We can update the UI accordingly.
Creating New Project
1.In Android Studio, go to File ⇒ New Project and fill all the details required to create a new project. When it prompts to select a default activity, select Blank Activity and proceed.
2.Open build.gradle and add Room ,Livedata,Viewmodel dependency and rebuild the project..
Build.gradle
dependencies { //recyclerview compile 'com.android.support:recyclerview-v7:26.1.0' //room library compile 'android.arch.persistence.room:runtime:1.0.0' annotationProcessor 'android.arch.persistence.room:compiler:1.0.0' //livedata and viewmodel compile 'android.arch.lifecycle:extensions:1.0.0' }
3. For Room we will create the model,Data Access Object and the Database.
Creating the Model
Event.Java
package com.example.lenovo.eventapp.entity; import android.arch.persistence.room.Entity; import android.arch.persistence.room.Ignore; import android.arch.persistence.room.PrimaryKey; import android.arch.persistence.room.TypeConverters; import com.example.lenovo.eventapp.db.DateConverter; import java.util.Date; import static com.example.lenovo.eventapp.entity.Event.TABLE_NAME; @Entity(tableName = TABLE_NAME) public class Event { public static final String TABLE_NAME = "events"; @PrimaryKey(autoGenerate = true) private int id; private String title; private String description; @TypeConverters(DateConverter.class) private Date date; public Event() { } @Ignore public Event(int id, String title, String description) { this.id = id; this.title = title; this.description = description; this.date = date; } public int getId() { return id; } public void setId(int id) { this.id = id; } public void setTitle(String title) { this.title = title; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public void setDescription(String description) { this.description = description; } public String getDescription() { return description; } public String getTitle() { return title; } @Override public String toString() { return "Event{" + "id=" + id + ", name='" + title + '\'' + ", description='" + description + '\'' + ", date='" + date + '\'' + '}'; } }
Data Access Object
EventDao.Java
package com.example.lenovo.eventapp.dao; import android.arch.lifecycle.LiveData; import android.arch.persistence.room.Dao; import android.arch.persistence.room.Delete; import android.arch.persistence.room.Insert; import android.arch.persistence.room.Query; import android.arch.persistence.room.Update; import com.example.lenovo.eventapp.entity.Event; import java.util.List; import static android.arch.persistence.room.OnConflictStrategy.REPLACE; @Dao public interface EventDao { @Query("SELECT * FROM " + Event.TABLE_NAME) LiveData<List<Event>> getEvents(); @Insert(onConflict = REPLACE) void addEvent(Event event); @Delete void deleteEvent(Event event); @Update(onConflict = REPLACE) void updateEvent(Event event); }
Creating the database
EventDatabase.Java
package com.example.lenovo.eventapp.db; import android.arch.persistence.room.Database; import android.arch.persistence.room.Room; import android.arch.persistence.room.RoomDatabase; import android.content.Context; import com.example.lenovo.eventapp.dao.EventDao; import com.example.lenovo.eventapp.entity.Event; @Database(entities = {Event.class}, version = 1) public abstract class EventDatabase extends RoomDatabase { private static final String DB_NAME = "Event_Database.db"; private static EventDatabase INSTANCE; public static EventDatabase getEventDatabase(Context context) { if (INSTANCE == null) { INSTANCE = Room.databaseBuilder(context, EventDatabase.class, DB_NAME).build(); } return INSTANCE; } public static void destroyInstance() { INSTANCE = null; } public abstract EventDao eventDao(); }
4. SQL cannot store data types like Date by default. That’s why we need a way to convert it into a compatible data type to store it in the database. We use the @TypeConverters to specify the converter for the date attribute. So to help us with this conversion, we’ll create a class called DateConverter.
DateConverter.Java
package com.example.lenovo.eventapp.db; import android.arch.persistence.room.TypeConverter; import java.util.Date; public class DateConverter { @TypeConverter public static Date toDate(Long timestamp) { return timestamp == null ? null : new Date(timestamp); } @TypeConverter public static Long toTimestamp(Date date) { return date == null ? null : date.getTime(); } }
5. Open activity_main layout and write the below code.
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=".MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view_list_events" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"> </android.support.v7.widget.RecyclerView> <TextView android:id="@+id/tv_no_events_found" android:layout_width="match_parent" android:layout_height="match_parent" android:fontFamily="sans-serif" android:gravity="center" android:text="NO EVENTS FOUND!" android:textColor="@color/colorPrimaryDark" android:textSize="25sp" android:visibility="gone" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab_add" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="16dp" android:layout_marginRight="16dp" app:elevation="4dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent" app:srcCompat="@drawable/ic_add_black_24dp" /> </android.support.constraint.ConstraintLayout>
6. Open event_dialog.xml and write the below code.Here this layout represents the view set up for alertdialog.
event_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="8dp"> <TextView android:id="@+id/dialog_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:fontFamily="sans-serif-medium" android:text="New Event" android:textColor="@color/colorAccent" android:textSize="22sp" android:textStyle="normal" /> <EditText android:id="@+id/edt_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/transparent" android:fontFamily="sans-serif" android:gravity="top" android:hint="Title" android:inputType="textCapSentences|textMultiLine" android:lines="2" android:textColor="@android:color/black" android:textColorHint="@color/colorPrimaryDark" android:textSize="18sp" /> <EditText android:id="@+id/edt_discription" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/transparent" android:fontFamily="sans-serif" android:gravity="top" android:hint="Discription" android:inputType="textCapSentences|textMultiLine" android:lines="3" android:textColor="@android:color/black" android:textColorHint="@color/colorPrimaryDark" android:textSize="18sp" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/tv_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_marginLeft="8dp" android:fontFamily="sans-serif" android:textColor="@android:color/black" android:textSize="18sp" /> <Button android:id="@+id/btn_setdate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:background="@color/colorAccent" android:text="set" android:textColor="@android:color/white" android:textStyle="bold" /> </RelativeLayout> </LinearLayout>
7. Open list_item_event.xml and write the below code.Here this layout represents the recyclerview list item.
list_item_event.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:padding="8dp"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:orientation="vertical"> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="sans-serif" android:textColor="@android:color/holo_orange_dark" android:textSize="20dp" android:textStyle="bold" /> <TextView android:id="@+id/tv_discription" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="6dp" android:fontFamily="sans-serif" android:textColor="@android:color/holo_green_light" android:textSize="16dp" /> </LinearLayout> <TextView android:id="@+id/tv_timestamp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:fontFamily="sans-serif" android:textColor="@color/colorAccent" android:textSize="16dp" android:textStyle="bold" /> </RelativeLayout>
8. Now since we will be displaying a list of items, we need a RecyclerView. So first, let’s create an adapter.
EventsAdapter.Java
package com.example.lenovo.eventapp; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.example.lenovo.eventapp.entity.Event; import java.util.List; public class EventsAdapter extends RecyclerView.Adapter<EventsAdapter.EventViewHolder> { private final Context context; private List<Event> items; public EventsAdapter(List<Event> items, Context context) { this.items = items; this.context = context; } public class EventViewHolder extends RecyclerView.ViewHolder { TextView titleTextView; TextView dateTextView; TextView descriptionTextView; EventViewHolder(View v) { super(v); titleTextView = (TextView) v.findViewById(R.id.tv_title); dateTextView = (TextView) v.findViewById(R.id.tv_timestamp); descriptionTextView = (TextView) v.findViewById(R.id.tv_discription); } } @Override public EventViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(context) .inflate(R.layout.list_item_event, parent, false); return new EventViewHolder(v); } @Override public void onBindViewHolder(EventViewHolder holder, int position) { Event item = items.get(position); holder.titleTextView.setText(item.getTitle()); holder.descriptionTextView.setText(item.getDescription()); holder.dateTextView.setText(item.getDate().toLocaleString().substring(0, 12)); } @Override public int getItemCount() { return items.size(); } }
9. For recyclerview I have created custom onitemclicklistener and the code is given below.
RecyclerTouchListener.Java
package com.example.lenovo.eventapp; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener { private ClickListener clicklistener; private GestureDetector gestureDetector; public RecyclerTouchListener(Context context, final RecyclerView recycleView, final ClickListener clicklistener) { this.clicklistener = clicklistener; gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { return true; } @Override public void onLongPress(MotionEvent e) { View child = recycleView.findChildViewUnder(e.getX(), e.getY()); if (child != null && clicklistener != null) { clicklistener.onLongClick(child, recycleView.getChildAdapterPosition(child)); } } }); } @Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { View child = rv.findChildViewUnder(e.getX(), e.getY()); if (child != null && clicklistener != null && gestureDetector.onTouchEvent(e)) { clicklistener.onClick(child, rv.getChildAdapterPosition(child)); } return false; } @Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { } @Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } public interface ClickListener { void onClick(View view, int position); void onLongClick(View view, int position); } }
Create the AndroidViewModel
EventViewModel.Java
package com.example.lenovo.eventapp; import android.app.Application; import android.arch.lifecycle.AndroidViewModel; import android.arch.lifecycle.LiveData; import android.support.annotation.NonNull; import com.example.lenovo.eventapp.db.EventDatabase; import com.example.lenovo.eventapp.entity.Event; import java.util.List; public class EventViewModel extends AndroidViewModel { private EventDatabase eventDatabase; private LiveData<List<Event>> eventList; public EventViewModel(@NonNull Application application) { super(application); eventDatabase = EventDatabase.getEventDatabase(this.getApplication()); eventList = eventDatabase.eventDao().getEvents(); } public LiveData<List<Event>> getEventList() { return eventList; } public EventDatabase getEventDatabase() { return eventDatabase; } }
10. Now create MainActivity.Java that extends AppCompatActivity to display a list of events.
MainActivity.Java
package com.example.lenovo.eventapp; import android.app.AlertDialog; import android.app.DatePickerDialog; import android.arch.lifecycle.Observer; import android.arch.lifecycle.ViewModelProviders; import android.content.DialogInterface; import android.os.AsyncTask; import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.DatePicker; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import com.example.lenovo.eventapp.db.EventDatabase; import com.example.lenovo.eventapp.entity.Event; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; public class MainActivity extends AppCompatActivity implements DatePickerDialog.OnDateSetListener { private RecyclerView recyclerView; private Date date; private DatePickerDialog datePickerDialog; private Calendar calendar; private EventDatabase eventDatabase; private EventViewModel viewModel; private TextView noEventsFound; private FloatingActionButton fab; private EventsAdapter adapter; private List<Event> events = new ArrayList<>(); private TextView tv_date; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); calendar = Calendar.getInstance(); datePickerDialog = new DatePickerDialog(this, this, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH)); viewModel = ViewModelProviders.of(this).get(EventViewModel.class); eventDatabase = viewModel.getEventDatabase(); noEventsFound = (TextView) findViewById(R.id.tv_no_events_found); recyclerView = (RecyclerView) findViewById(R.id.recycler_view_list_events); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.VERTICAL, false)); viewModel.getEventList().observe(this, new Observer<List<Event>>() { @Override public void onChanged(@Nullable List<Event> eventList) { events = eventList; adapter = new EventsAdapter(events, getApplicationContext()); recyclerView.setAdapter(adapter); checkListEmptyOrNot(); } }); //shows NO EVENTS FOUND when list is empty checkListEmptyOrNot(); recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getApplicationContext(), recyclerView, new RecyclerTouchListener.ClickListener() { @Override public void onClick(View view, int position) { } @Override public void onLongClick(View view, int position) { //show Alertdialog to edit or update the event showActionsDialog(position); } })); //add listener on FloatingActionButton to add event fab = (FloatingActionButton) findViewById(R.id.fab_add); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //adding new event showEventDialog(false, null, -1); } }); } private class DatabaseAsync extends AsyncTask<Object, Void, Void> { private EventDatabase db; DatabaseAsync(EventDatabase eventDatabase) { db = eventDatabase; } @Override protected Void doInBackground(Object... params) { Boolean shouldUpdate = (Boolean) params[0]; int position = (int) params[1]; String title = (String) params[2]; String detail = (String) params[3]; Date date = (Date) params[4]; //check whether to add add or update event if (shouldUpdate != null) { //update event if (shouldUpdate) { Event event = events.get(position); event.setTitle(title); event.setDescription(detail); event.setDate(date); //update event into the database db.eventDao().updateEvent(event); } else { //add event Event event = new Event(); event.setTitle(title); event.setDescription(detail); event.setDate(date); //add event into the database db.eventDao().addEvent(event); } } else { //delete event if (position != -1) { Event event = events.get(position); //delete event from database db.eventDao().deleteEvent(event); } } return null; } } private void showActionsDialog(final int position) { CharSequence colors[] = new CharSequence[]{"Edit", "Delete"}; AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Choose option"); builder.setItems(colors, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (which == 0) { //show Alertdialog to update the event showEventDialog(true, events.get(position), position); } else { //delete event from database deleteEvent(position); } } }); builder.show(); } private void deleteEvent(int position) { new DatabaseAsync(eventDatabase).execute(null, position, null, null, null); } private void showEventDialog(final Boolean shouldUpdate, final Event event, final int position) { LayoutInflater layoutInflater = LayoutInflater.from(getApplicationContext()); View view = layoutInflater.inflate(R.layout.event_dialog, null); AlertDialog.Builder alertDialogBuilderUserInput = new AlertDialog.Builder(this); alertDialogBuilderUserInput.setView(view); TextView dialog_title = (TextView) view.findViewById(R.id.dialog_title); final EditText edt_title = (EditText) view.findViewById(R.id.edt_title); final EditText edt_discription = (EditText) view.findViewById(R.id.edt_discription); tv_date = (TextView) view.findViewById(R.id.tv_date); Button btn_setdate = (Button) view.findViewById(R.id.btn_setdate); //add listener to button to open datepickerdialog btn_setdate.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //open datepickerdialog datePickerDialog.show(); } }); dialog_title.setText(!shouldUpdate ? "New Event" : "Edit Event"); //in case of update we want all the //fields to be set bydefault with text if (shouldUpdate && event != null) { edt_title.setText(event.getTitle()); edt_discription.setText(event.getDescription()); tv_date.setText(event.getDate().toLocaleString().substring(0, 11)); } alertDialogBuilderUserInput .setCancelable(false) .setPositiveButton(shouldUpdate ? "update" : "add", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogBox, int id) { } }) .setNegativeButton("cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogBox, int id) { dialogBox.cancel(); } }); final AlertDialog alertDialog = alertDialogBuilderUserInput.create(); alertDialog.show(); alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // Show toast message when no text is entered if (TextUtils.isEmpty(edt_title.getText().toString()) && !TextUtils.isEmpty(edt_discription.getText().toString())) { Toast.makeText(MainActivity.this, "Enter title!", Toast.LENGTH_SHORT).show(); } else if (!TextUtils.isEmpty(edt_title.getText().toString()) && TextUtils.isEmpty(edt_discription.getText().toString())) { Toast.makeText(MainActivity.this, "Enter description!", Toast.LENGTH_SHORT).show(); } else if (TextUtils.isEmpty(edt_title.getText().toString()) && TextUtils.isEmpty(edt_discription.getText().toString())) { Toast.makeText(MainActivity.this, "Enter title and description!", Toast.LENGTH_SHORT).show(); } else { alertDialog.dismiss(); } //Update or add data into the database only when both field are filled(i.e title,description) if (!TextUtils.isEmpty(edt_title.getText().toString()) && !TextUtils.isEmpty(edt_discription.getText().toString())) { // check if user updating note if (shouldUpdate && event != null) { // update event new DatabaseAsync(eventDatabase).execute(shouldUpdate, position, edt_title.getText().toString(), edt_discription.getText().toString(), date); } else { // create new event new DatabaseAsync(eventDatabase).execute(shouldUpdate, -1, edt_title.getText().toString(), edt_discription.getText().toString(), date); } } } }); } @Override public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) { calendar.set(Calendar.YEAR, year); calendar.set(Calendar.MONTH, month); calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth); date = calendar.getTime(); tv_date.setText(date.toLocaleString().substring(0, 12)); } public void checkListEmptyOrNot() { if (events.isEmpty()) noEventsFound.setVisibility(View.VISIBLE); else noEventsFound.setVisibility(View.GONE); } }
When you run the app it will look like this:
Related posts: