Android Realm Database – Creating Reactive ToDo List Application using Realm Database

In this tutorial, we are going to learn how to create a ToDo List app using the android Realm database.

If you have not heard of android Realm database, this is a piece from their website.

“The Realm Mobile Database works on all major mobile platforms to provide offline-first functionality & data persistence through an easy-to-use API. When paired with the Realm Mobile Platform, all your data is automatically synced, with no work from you”.

Android Realm Database is not use as a wrapper for android native SQLite database. It is faster than most android database and even SQLite. It works directly with JSON object and it uses well know Plain Old Java Object (POJO) class rather than table structure.

Although there are few issues you can experience while working with Android Realm Database like its lack of support for incremental primary key.

Realm database makes writing persistence code faster and it is reactive in the sense that you can use notification or event to update UI view once there is a change in realm database.

We are going to create an android ToDo List App using the Realm database. Why do I want a ToDo List App? It is simple. By building android ToDo List App with Realm, we will get to understand how to perform create, read, update and delete (CRUD) in Realm database.

If you are working on a project that requires using the real-time Firebase database in your ToDo app, I wrote a tutorial on this topic which might be helpful to you.

On the other hand, if you are still trying to wrap your head around android default SQlite database, I will suggest you read my tutorial on Android SQLite Database Example Tutorial

In other to use Realm in your android project, we are going to add realm as an android plugin in our project build.gradle file.

To get an instance of a Realm object, we can simple call the getInstance() method. One important thing to note is that the Realm object we use in our component classes must be destroy in the onDestory callback method to free memory and also prevent memory leakage.

In other to get a visual understanding of what we are going to create in this android tutorial, I have add below some screen shots from the application.

SOME SCREENSHOT FROM THE APPLICATION

android realm database realm2 realm3

CREATE NEW ANDROID PROJECT

Lets start to soil our hands in code. Start up your IDE. For this tutorial, I am using the following tools and environment, feel free to use what works for you.

Windows 10

Android Studio

Sony Xperia ZL

Min SDK 14

Target SDK 25

To create a new android application project, follow the steps as stipulated below.

Go to File menu

Click on New menu

Click on Android Application

Enter Project name: AndroidRealmExample

Package: com.inducesmile.androidrealmexample

Select Empty Activity

Name your activity: MainActivity

Keep other default selections

Continue to click on next button until Finish button is active, then click on Finish Button.

ADD LIBRARY DEPENDENCIES IN BUILD.GRADLE

As stated before, to use the Realm database we need to add Realm as gradle plugin in our project build.gradle file.

Open your build.gradle (project) file and add the code below.

classpath "io.realm:realm-gradle-plugin:2.2.1"
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.1'
        classpath "io.realm:realm-gradle-plugin:2.2.1"
    }
}
allprojects {
    repositories {
        jcenter()
    }
}
task clean(type: Delete) {
    delete rootProject.buildDir
}

Then in the app level build.gradle file, at the top level of the page, add Realm as a plugin. Thereafter, we will add someother libraries that our project will make use of.

Open the app level build.gradle and add the code below.

apply plugin: 'com.android.application'
apply plugin: 'realm-android'
android {
    compileSdkVersion 25
    buildToolsVersion "24.0.1"
    defaultConfig {
        applicationId "com.inducesmile.androidrealmexample"
        minSdkVersion 14
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.0.1'
    compile 'com.intuit.sdp:sdp-android:1.0.3'
    compile 'com.android.support:design:25.0.1'
    compile 'com.android.support:cardview-v7:25.0.1'
    compile 'com.android.support:recyclerview-v7:25.0.1'
    compile 'com.android.support:support-v4:25.0.1'
    compile 'com.google.code.gson:gson:2.6.1'
    compile 'io.realm:android-adapters:1.4.0'
    testCompile 'junit:junit:4.12'
}

 STRINGS.XML

We are going to update our project strings.xml file located in the values folder inside the res folder. Open the file and add the code below to it.

<resources>
    <string name="app_name">TODO</string>
    <string name="add_task">ADD TASK</string>
    <string name="enter_task">Enter quick task</string>
    <string name="learn_android">I will learn android today</string>
    <string name="education">Education</string>
    <string name="task_date">22-05-2016 11:30 PM</string>
    <string name="add_new_task">Add task</string>
    <string name="add_task_description">Add task description</string>
    <string name="enter_task_name">Enter task name</string>
    <string name="select_task_category">Select task category</string>
    <string name="category">Category</string>
    <string name="view_task_in_calendar">View task in calendar</string>
    <string name="due_date">Due Date</string>
    <string name="due_time">Due time</string>
    <string name="add_new_menu_task">Add new task</string>
    <string name="set_alarm">Set alarm (optional)</string>
    <string name="reminder">Set a reminder alarm for this task</string>
    <string name="add_name_of_task_to_do">Add name of task to do</string>
    <string name="test">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, </string>
    <string name="task_due_date">Task due date</string>
    <string name="reminder_alarm_is_off">Reminder alarm is not set</string>
    <string name="delete_menu_task">delete_menu_task</string>
    <string name="edit_menu_task">Edit menu task</string>
    <string name="no_time">Due date not set</string>
    <string name="task_date_and_time">Task date and time must be add</string>
    <string name="invalid_input_values">Some input fields must be filled</string>
    <string-array name="select_task_category">
        <item>Education</item>
        <item>Event</item>
        <item>Shopping</item>
        <item>Church</item>
        <item>Hospital</item>
        <item>Meeting</item>
        <item>Work</item>
    </string-array>
</resources>

 COLORS.XML

Open the colors.xml file in the same location as the strings.xml file and add the code below to the file.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#8972FF</color>
    <color name="colorPrimaryDark">#7d67eb</color>
    <color name="colorAccent">#CB60F6</color>
    <color name="colorDull">#769961</color>
    <color name="colorWhite">#ffffff</color>
    <color name="colorBlack">#000000</color>
    <color name="colorBorder">#a3a3a3</color>
</resources>

 SplashActivity class

The launch activity for our project is the SplashActivity. Once the application is launched, the screen will appear and on clicking the page, it will take you right inside the app.

Open the SplashActivity and add the code below

import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
public class SplashActivity extends AppCompatActivity {
    private static final String TAG = SplashActivity.class.getSimpleName();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        ActionBar actionBar = getSupportActionBar();
        if(null != actionBar){
            actionBar.hide();
        }
        ImageView tapImage = (ImageView)findViewById(R.id.tap_image);
        tapImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent tapIntent = new Intent(SplashActivity.this, ToDoActivity.class);
                startActivity(tapIntent);
            }
        });
    }
}

 activity_splash.xml

For the layout file, it will contain a single ImageView widget. Open the activity_splash.xml and add the code below.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_splash"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.inducesmile.androidrealmexample.SplashActivity">
    <ImageView
        android:id="@+id/tap_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:adjustViewBounds="true"
        android:src="@drawable/todobg"
        android:scaleType="centerCrop"
        android:contentDescription="@string/app_name"/>
</RelativeLayout>

 ToDoActivity class

The ToDoActivity class will contain RecyclerView widget that will hold are the todo list we have schedule. At the bottom left side of the app, we will add a FloatingActionButtom, when clicked, it will open a pop-up window where a user can create a new task.

Create a new activity file in your package folder and name it TodoActivity.java or any name of your choice. Open this file and add the code below to it.

import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
import com.inducesmile.androidrealmexample.adapter.CustomArrayAdapter;
import com.inducesmile.androidrealmexample.adapter.RealmAdapter;
import com.inducesmile.androidrealmexample.realm.TaskModel;
import io.realm.Realm;
import io.realm.RealmResults;
public class ToDoActivity extends AppCompatActivity {
    private static final String TAG = ToDoActivity.class.getSimpleName();
    private RecyclerView recyclerView;
    private Realm realmInstance;
    private String selectedCategory;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_to_do);
        realmInstance = Realm.getDefaultInstance();
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // add new quick task
                addTaskDialog();
            }
        });
        recyclerView = (RecyclerView)findViewById(R.id.task_list);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(ToDoActivity.this);
        recyclerView.setLayoutManager(linearLayoutManager);
        RealmResults<TaskModel> toDoItems = realmInstance.where(TaskModel.class).findAllAsync();
        RealmAdapter rAdapter = new RealmAdapter(this, toDoItems, true);
        recyclerView.setHasFixedSize(true);
        recyclerView.setAdapter(rAdapter);
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        //return super.onCreateOptionsMenu(menu);
        getMenuInflater().inflate(R.menu.task_menu, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if(id == R.id.action_add_task){
            Intent addTaskIntent = new Intent(ToDoActivity.this, AddTaskActivity.class);
            startActivity(addTaskIntent);
        }
        return super.onOptionsItemSelected(item);
    }
    private void addTaskDialog(){
        LayoutInflater inflater = LayoutInflater.from(this);
        View subView = inflater.inflate(R.layout.add_task_layer, null);
        final EditText addTaskName = (EditText)subView.findViewById(R.id.add_task_name);
        final EditText addTaskDescription = (EditText)subView.findViewById(R.id.add_task_description);
        Spinner addTaskCategory = (Spinner)subView.findViewById(R.id.select_category);
        addTaskCategory.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                selectedCategory = (String)adapterView.getItemAtPosition(i);
            }
            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {
            }
        });
        CustomArrayAdapter mArrayAdapter = new CustomArrayAdapter(ToDoActivity.this, R.layout.spinner_list, getResources().getStringArray(R.array.select_task_category));
        mArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        addTaskCategory.setAdapter(mArrayAdapter);
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Add a new task");
        builder.setView(subView);
        builder.create();
        builder.setPositiveButton("ADD TASK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                final String taskName = addTaskName.getText().toString();
                final String taskDescription = addTaskDescription.getText().toString();
                if(TextUtils.isEmpty(taskName) || TextUtils.isEmpty(taskDescription) || TextUtils.isEmpty(selectedCategory)){
                    Toast.makeText(ToDoActivity.this, "All fields must be filled", Toast.LENGTH_LONG).show();
                }
                else{
                    final long rowId = getLastInsertedRowId();
                    // add the new task to Realm Database
                    realmInstance.executeTransaction(new Realm.Transaction() {
                        @Override
                        public void execute(Realm realm) {
                            TaskModel tModel = realmInstance.createObject(TaskModel.class, rowId);
                            tModel.setName(taskName);
                            tModel.setDescription(taskDescription);
                            tModel.setCategory(selectedCategory);
                        }
                    });
                }
            }
        });
        builder.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Toast.makeText(ToDoActivity.this, "Task cancelled", Toast.LENGTH_LONG).show();
            }
        });
        builder.show();
    }
    private long getLastInsertedRowId(){
        long id = realmInstance.where(TaskModel.class).max("id").longValue();
        return id + 1;
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        realmInstance.close();
    }
}
From the code above, we used the block code below to add a new task to the Realm database
 
final long rowId = getLastInsertedRowId();
// add the new task to Realm Database
realmInstance.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        TaskModel tModel = realmInstance.createObject(TaskModel.class, rowId);
        tModel.setName(taskName);
        tModel.setDescription(taskDescription);
        tModel.setCategory(selectedCategory);
    }
});

I am going to emphasis more on this later.

activity_to_do.xml

Open the activity_to_do.xml layout file and add the code below.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_to_do"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorWhite"
    tools:context="com.inducesmile.androidrealmexample.ToDoActivity">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/task_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="@dimen/_8sdp"
        android:scrollbars="none" />
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|right"
        android:layout_marginRight="@dimen/_16sdp"
        android:layout_marginEnd="@dimen/_16sdp"
        android:layout_marginBottom="@dimen/_16sdp"
        android:src="@android:drawable/ic_input_add" />
</FrameLayout>

ViewTasKActivity class

The ViewTaskActivity class will display each single task when selected from the RecyclerView item list. The layout file for this activity class will contain some TextViews and ImageView widgets.

Create a new Activity file and name it ViewTaskActivity.java. Open this file and add the code below to it.

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;
import com.inducesmile.androidrealmexample.adapter.TaskObject;
import com.inducesmile.androidrealmexample.helper.CustomApplication;
import com.inducesmile.androidrealmexample.realm.TaskModel;
import io.realm.Realm;
public class ViewTaskActivity extends AppCompatActivity {
    private static final String TAG = ViewTaskActivity.class.getSimpleName();
    private Realm viewRealm;
    private TaskObject viewTaskObject;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_task);
        setTitle("View Task");
        ActionBar actionBar = getSupportActionBar();
        if(actionBar != null){
            actionBar.setDisplayHomeAsUpEnabled(true);
        }
        viewRealm = Realm.getDefaultInstance();
        String taskObject = getIntent().getExtras().getString("TASK_OBJECT");
        viewTaskObject = ((CustomApplication)getApplication()).getGsonObject().fromJson(taskObject, TaskObject.class);
        TextView viewTaskName = (TextView)findViewById(R.id.view_task_name);
        TextView viewTaskDescription = (TextView)findViewById(R.id.view_task_description);
        TextView viewTaskCategory = (TextView)findViewById(R.id.view_task_category);
        TextView viewTaskDueDate = (TextView)findViewById(R.id.view_task_due_date);
        TextView reminderState = (TextView)findViewById(R.id.alarm_state);
        if(viewTaskObject != null){
            viewTaskName.setText(viewTaskObject.getName());
            viewTaskDescription.setText(viewTaskObject.getDescription());
            viewTaskCategory.setText(viewTaskObject.getCategory());
            if(TextUtils.isEmpty(viewTaskObject.getDateTime())){
                viewTaskDueDate.setText("Due date not set");
            }else{
                viewTaskDueDate.setText(viewTaskObject.getDateTime());
            }
            if(viewTaskObject.getReminder()){
                reminderState.setText("Reminder alarm is on");
            }else{
                reminderState.setText("Reminder alarm is off");
            }
        }else{
            Toast.makeText(ViewTaskActivity.this, "Something went wrong with this task data", Toast.LENGTH_LONG).show();
        }
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
       getMenuInflater().inflate(R.menu.edit_task_menu, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if(id == R.id.action_delete_task){
            deleteSingleRowInDatabase(viewTaskObject.getId());
            Intent listTaskIntent = new Intent(ViewTaskActivity.this, ToDoActivity.class);
            startActivity(listTaskIntent);
            return true;
        }
        if(id == R.id.action_edit_task){
            Intent editTaskIntent = new Intent(ViewTaskActivity.this, EditTaskActivity.class);
            String editObject = ((CustomApplication)getApplication()).getGsonObject().toJson(viewTaskObject);
            editTaskIntent.putExtra("TASK_OBJECT", editObject);
            startActivity(editTaskIntent);
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    private void deleteSingleRowInDatabase(final int id){
        viewRealm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                viewRealm.where(TaskModel.class).equalTo("id", id).findFirst().deleteFromRealm();
            }
        });
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        viewRealm.close();
    }
}

Note that the instance of the Realm database is closed in the OnDestroy() callback method of the activity class.

This class also contain two Menu action. One to delete a single task and the second one is used to edit a single task.

The Realm code to delete a single row item in Realm database using the index of the object is shown below.

viewRealm.where(TaskModel.class).equalTo("id", id).findFirst().deleteFromRealm();

 activity_view_task.xml

For the XML layout file, open the file and add the code below to it.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_view_task"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="@dimen/_4sdp"
    tools:context="com.inducesmile.androidrealmexample.ViewTaskActivity">
    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="1dp"
        card_view:cardCornerRadius="@dimen/_3sdp"
        card_view:cardElevation="@dimen/_2sdp"
        card_view:cardUseCompatPadding="true">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="@dimen/_16sdp"
                android:background="@color/colorBorder"
                android:orientation="vertical">
                <TextView
                    android:id="@+id/view_task_name"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="@color/colorWhite"
                    android:layout_gravity="center_vertical"
                    android:text="@string/learn_android"
                    android:textSize="@dimen/_16sdp"/>
            </LinearLayout>
            <TextView
                android:id="@+id/view_task_description"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@color/colorBlack"
                android:layout_gravity="center_vertical"
                android:padding="@dimen/_16sdp"
                android:text="@string/test"
                android:lineSpacingMultiplier="1.2"
                android:textSize="@dimen/_13sdp"/>
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="9"
                    android:padding="@dimen/_8sdp"
                    android:orientation="vertical">
                    <TextView
                        android:id="@+id/task_name"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/task_due_date"
                        android:textColor="@color/colorDull"
                        android:textSize="@dimen/_12sdp" />
                    <TextView
                        android:id="@+id/view_task_due_date"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/task_date"
                        android:textColor="@color/colorBorder"
                        android:layout_marginTop="@dimen/_8sdp"
                        android:textSize="@dimen/_11sdp" />
                </LinearLayout>
                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="3"
                    android:gravity="center"
                    android:paddingRight="@dimen/_6sdp"
                    android:orientation="vertical">
                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/category"
                        android:textColor="@color/colorDull"
                        android:layout_marginTop="@dimen/_8sdp"
                        android:textSize="@dimen/_12sdp" />
                    <TextView
                        android:id="@+id/view_task_category"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/education"
                        android:textColor="@color/colorBorder"
                        android:layout_marginTop="@dimen/_2sdp"
                        android:textSize="@dimen/_11sdp" />
                </LinearLayout>
            </LinearLayout>
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:layout_marginTop="@dimen/_12sdp"
                android:layout_marginBottom="@dimen/_12sdp"
                android:orientation="vertical">
                <TextView
                    android:id="@+id/alarm_state"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@string/reminder_alarm_is_off"
                    android:layout_gravity="center"
                    android:textColor="@color/colorDull"
                    android:textSize="@dimen/_12sdp" />
                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:contentDescription="@string/app_name"
                    android:src="@drawable/bellbig"
                    android:layout_marginTop="@dimen/_8sdp"
                    android:layout_marginBottom="@dimen/_8sdp"
                    android:layout_gravity="center"/>
            </LinearLayout>
        </LinearLayout>
    </android.support.v7.widget.CardView>
</LinearLayout>

EditTaskActivity class

The EditTaskActivity class is used to edit an object that has been stored in Realm database. It has almost the same UI with the AddTaskActivity.

The layout file for this activity class contains EditText, TextView and ImageView widgets.

Create a new activity file and name it EditTaskActivity.java. Open the file and add the following code below.

import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.Toast;
import com.inducesmile.androidrealmexample.adapter.CustomArrayAdapter;
import com.inducesmile.androidrealmexample.adapter.TaskObject;
import com.inducesmile.androidrealmexample.helper.CustomApplication;
import com.inducesmile.androidrealmexample.realm.TaskModel;
import java.util.Calendar;
import io.realm.Realm;
public class EditTaskActivity extends AppCompatActivity {
    private static final String TAG = EditTaskActivity.class.getSimpleName();
    private EditText taskName;
    private EditText taskDescription;
    private Spinner taskCategory;
    private static EditText taskDueDate;
    private static EditText taskDueTime;
    private CheckBox reminder;
    private boolean isAlarmSet = false;
    private String selectedCategory;
    private static String selectedTime;
    private static String selectedDate;
    private Realm realm;
    private TaskObject taskObject;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_edit_task);
        setTitle("Edit task");
        ActionBar actionBar = getSupportActionBar();
        if(actionBar != null){
            actionBar.setDisplayHomeAsUpEnabled(true);
        }
        realm = Realm.getDefaultInstance();
        String taskStringObject = getIntent().getExtras().getString("TASK_OBJECT");
        taskObject = ((CustomApplication)getApplication()).getGsonObject().fromJson(taskStringObject, TaskObject.class);
        taskName = (EditText)findViewById(R.id.add_task_name);
        taskDescription = (EditText)findViewById(R.id.add_task_description);
        taskDueDate = (EditText)findViewById(R.id.add_task_ending);
        taskDueTime = (EditText)findViewById(R.id.add_task_ending_time);
        reminder = (CheckBox)findViewById(R.id.set_task_alarm);
        if(!TextUtils.isEmpty(taskObject.getName())){
            taskName.setText(taskObject.getName());
        }
        if(!TextUtils.isEmpty(taskObject.getDescription())){
            taskDescription.setText(taskObject.getDescription());
        }
        if(taskObject.getReminder()){
            reminder.setChecked(true);
        }else{
            reminder.setChecked(false);
        }
        if(!TextUtils.isEmpty(taskObject.getDateTime())){
            String[] dateTimes = taskObject.getDateTime().split(" ");
           taskDueDate.setText(dateTimes[0]);
            taskDueTime.setText(dateTimes[1]);
        }
        reminder.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                if(b){
                    isAlarmSet = true;
                }else{
                    isAlarmSet = false;
                }
            }
        });
        // populate task category to the spinner object
        taskCategory = (Spinner)findViewById(R.id.select_category);
        CustomArrayAdapter mArrayAdapter = new CustomArrayAdapter(EditTaskActivity.this, R.layout.spinner_list, getResources().getStringArray(R.array.select_task_category));
        mArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        taskCategory.setAdapter(mArrayAdapter);
        taskCategory.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                selectedCategory = (String)adapterView.getItemAtPosition(i);
            }
            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {
            }
        });
        //add task date
        ImageView addTaskDate = (ImageView)findViewById(R.id.add_task_date);
        addTaskDate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new DatePickerFragment().show(getSupportFragmentManager(), "Edit Task Date");
            }
        });
        // delete task date
        ImageView deleteTaskDate = (ImageView)findViewById(R.id.delete_task_date);
        deleteTaskDate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                taskDueDate.setText("");
            }
        });
        ImageView addTaskTime = (ImageView)findViewById(R.id.add_task_time);
        addTaskTime.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new TimePicker().show(getSupportFragmentManager(), "Edit Task Time");
            }
        });
        ImageView deleteTaskTime = (ImageView)findViewById(R.id.delete_task_time);
        deleteTaskTime.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                taskDueTime.setText("");
            }
        });
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.add_task_menu, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if(id == R.id.action_add_task){
            //Edit and save task to database.
            validateInputAndSave();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    private void validateInputAndSave() {
        String name = taskName.getText().toString();
        String description = taskDescription.getText().toString();
        String dueDate = taskDueDate.getText().toString();
        String dueTime = taskDueTime.getText().toString();
        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(description) || TextUtils.isEmpty(selectedCategory)) {
            Toast.makeText(EditTaskActivity.this, R.string.invalid_input_values, Toast.LENGTH_LONG).show();
        } else if (TextUtils.isEmpty(dueDate) || TextUtils.isEmpty(dueTime)) {
            Toast.makeText(EditTaskActivity.this, R.string.task_date_and_time, Toast.LENGTH_LONG).show();
        } else {
            //add task to realm database
            long currentRowId = getLastInsertedRowId();
            String[] databaseValues = new String[]{name, description, selectedCategory, dueDate, dueTime};
            addNewTaskToRealmDatabase(databaseValues, isAlarmSet);
        }
    }
    private void addNewTaskToRealmDatabase(final String[] columnValues, final boolean isAlarm){
        //Insert to realm database
        realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                TaskModel mModel = realm.where(TaskModel.class).equalTo("id", taskObject.getId()).findFirst();
                mModel.setName(columnValues[0]);
                mModel.setDescription(columnValues[1]);
                mModel.setCategory(columnValues[2]);
                mModel.setDateTime(columnValues[3] + " " + columnValues[4]);
                mModel.setReminder(isAlarm);
            }
        });
        resetViewInput();
    }
    public static class TimePicker extends DialogFragment implements TimePickerDialog.OnTimeSetListener {
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final Calendar c = Calendar.getInstance();
            int hour = c.get(Calendar.HOUR_OF_DAY);
            int minute = c.get(Calendar.MINUTE);
            return new TimePickerDialog(getActivity(), this, hour, minute, DateFormat.is24HourFormat(getActivity()));
        }
        @Override
        public void onTimeSet(android.widget.TimePicker view, int hourOfDay, int minute) {
            selectedTime =  String.valueOf(hourOfDay) + ":" + String.valueOf(minute);
            taskDueTime.setText(String.valueOf(hourOfDay) + ":" + String.valueOf(minute));
        }
    }
    public static class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final Calendar c = Calendar.getInstance();
            int year = c.get(Calendar.YEAR);
            int month = c.get(Calendar.MONTH);
            int day = c.get(Calendar.DAY_OF_MONTH);
            return new DatePickerDialog(getActivity(), this, year, month, day);
        }
        public void onDateSet(DatePicker view, int year, int month, int day) {
            selectedDate = String.valueOf(year) + "-" + String.valueOf(month) + "-" + String.valueOf(day);
            taskDueDate.setText(String.valueOf(year) + "-" + String.valueOf(month) + "-" + String.valueOf(day));
        }
    }
    private long getLastInsertedRowId(){
        long id = realm.where(TaskModel.class).max("id").longValue();
        return id + 1;
    }
    private void resetViewInput(){
        taskName.setText("");
        taskDescription.setText("");
        taskDueDate.setText("");
        taskDueTime.setText("");
        reminder.setChecked(false);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        realm.close();
    }
}

 activity_edit_task.xml

For the layout file, open the file and add the code below to it.

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="none">
    <LinearLayout
        android:id="@+id/activity_add_task"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingLeft="@dimen/_16sdp"
        android:paddingRight="@dimen/_16sdp"
        tools:context="com.inducesmile.androidrealmexample.EditTaskActivity">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/colorBlack"
            android:text="@string/add_name_of_task_to_do"
            android:textStyle="bold"
            android:layout_marginTop="@dimen/_16sdp"/>
        <EditText
            android:id="@+id/add_task_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="@dimen/_12sdp"
            android:layout_marginTop="@dimen/_12sdp"
            android:inputType="text"
            android:background="@drawable/brown_border"
            android:maxLines="1"
            android:layout_marginRight="@dimen/_8sdp"
            android:layout_marginEnd="@dimen/_8sdp"
            android:textColor="@color/colorBlack"
            android:padding="@dimen/_12sdp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/colorBlack"
            android:text="@string/add_task_description"
            android:textSize="@dimen/_12sdp"
            android:textStyle="bold"
            android:layout_marginTop="@dimen/_16sdp"/>
        <EditText
            android:id="@+id/add_task_description"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/_12sdp"
            android:gravity="top|left"
            android:padding="@dimen/_8sdp"
            android:background="@drawable/brown_border"
            android:lines="6"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/colorBlack"
            android:text="@string/select_task_category"
            android:textSize="@dimen/_12sdp"
            android:textStyle="bold"
            android:layout_marginTop="@dimen/_16sdp"/>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/_12sdp"
            android:background="@drawable/brown_border">
            <Spinner
                android:id="@+id/select_category"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:popupBackground="@drawable/spinner_border"
                android:padding="@dimen/_8sdp">
            </Spinner>
        </LinearLayout>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/colorBlack"
            android:text="@string/due_date"
            android:textSize="@dimen/_12sdp"
            android:textStyle="bold"
            android:layout_marginTop="@dimen/_16sdp"/>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/_12sdp"
            android:orientation="horizontal">
            <EditText
                android:id="@+id/add_task_ending"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="4"
                android:textSize="@dimen/_12sdp"
                android:inputType="text"
                android:background="@drawable/brown_border"
                android:maxLines="1"
                android:textColor="@color/colorBlack"
                android:padding="@dimen/_12sdp"/>
            <ImageView
                android:id="@+id/add_task_date"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:contentDescription="@string/app_name"
                android:src="@drawable/addcalendar"
                android:layout_gravity="center_vertical"
                android:paddingLeft="@dimen/_20sdp"
                android:layout_weight="1"/>
            <ImageView
                android:id="@+id/delete_task_date"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:contentDescription="@string/app_name"
                android:src="@drawable/cross"
                android:layout_gravity="center"
                android:layout_weight="1"/>
        </LinearLayout>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/colorBlack"
            android:text="@string/due_time"
            android:textSize="@dimen/_12sdp"
            android:textStyle="bold"
            android:layout_marginTop="@dimen/_16sdp"/>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/_12sdp"
            android:orientation="horizontal">
            <EditText
                android:id="@+id/add_task_ending_time"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="4"
                android:textSize="@dimen/_12sdp"
                android:inputType="text"
                android:background="@drawable/brown_border"
                android:maxLines="1"
                android:textColor="@color/colorBlack"
                android:padding="@dimen/_12sdp"/>
            <ImageView
                android:id="@+id/add_task_time"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:contentDescription="@string/app_name"
                android:src="@drawable/timer"
                android:layout_gravity="center_vertical"
                android:layout_marginTop="@dimen/_1sdp"
                android:paddingLeft="@dimen/_20sdp"
                android:layout_weight="1"/>
            <ImageView
                android:id="@+id/delete_task_time"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:contentDescription="@string/app_name"
                android:src="@drawable/cross"
                android:layout_gravity="center"
                android:layout_weight="1"/>
        </LinearLayout>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/colorBlack"
            android:text="@string/set_alarm"
            android:textSize="@dimen/_12sdp"
            android:textStyle="bold"
            android:layout_marginTop="@dimen/_16sdp"/>
        <CheckBox
            android:id="@+id/set_task_alarm"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/_12sdp"
            android:text="@string/reminder"
            android:layout_marginBottom="@dimen/_16sdp"/>
    </LinearLayout>
</ScrollView>

 AddTaskActivity class

Create a new activity file and add the following code below.

import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.Toast;
import com.inducesmile.androidrealmexample.adapter.CustomArrayAdapter;
import com.inducesmile.androidrealmexample.realm.TaskModel;
import java.util.Calendar;
import io.realm.Realm;
public class AddTaskActivity extends AppCompatActivity {
    private static final String TAG = AddTaskActivity.class.getSimpleName();
    private EditText taskName;
    private EditText taskDescription;
    private Spinner taskCategory;
    private static EditText taskDueDate;
    private static EditText taskDueTime;
    private CheckBox reminder;
    private boolean isAlarmSet = false;
    private String selectedCategory;
    private static String selectedTime;
    private static String selectedDate;
    private Realm realm;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_task);
        setTitle("Add new task");
        ActionBar actionBar = getSupportActionBar();
        if(actionBar != null){
            actionBar.setDisplayHomeAsUpEnabled(true);
        }
        realm = Realm.getDefaultInstance();
        taskName = (EditText)findViewById(R.id.add_task_name);
        taskDescription = (EditText)findViewById(R.id.add_task_description);
        taskDueDate = (EditText)findViewById(R.id.add_task_ending);
        taskDueTime = (EditText)findViewById(R.id.add_task_ending_time);
        reminder = (CheckBox)findViewById(R.id.set_task_alarm);
        reminder.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                if(b){
                    isAlarmSet = true;
                }else{
                    isAlarmSet = false;
                }
            }
        });
        // populate task category to the spinner object
        taskCategory = (Spinner)findViewById(R.id.select_category);
        CustomArrayAdapter mArrayAdapter = new CustomArrayAdapter(AddTaskActivity.this, R.layout.spinner_list, getResources().getStringArray(R.array.select_task_category));
        mArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        taskCategory.setAdapter(mArrayAdapter);
        taskCategory.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                selectedCategory = (String)adapterView.getItemAtPosition(i);
            }
            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {
            }
        });
        //add task date
        ImageView addTaskDate = (ImageView)findViewById(R.id.add_task_date);
        addTaskDate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new DatePickerFragment().show(getSupportFragmentManager(), "Task Date");
            }
        });
        // delete task date
        ImageView deleteTaskDate = (ImageView)findViewById(R.id.delete_task_date);
        deleteTaskDate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                taskDueDate.setText("");
            }
        });
        ImageView addTaskTime = (ImageView)findViewById(R.id.add_task_time);
        addTaskTime.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new TimePicker().show(getSupportFragmentManager(), "Task Time");
            }
        });
        ImageView deleteTaskTime = (ImageView)findViewById(R.id.delete_task_time);
        deleteTaskTime.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                taskDueTime.setText("");
            }
        });
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.add_task_menu, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if(id == R.id.action_add_task){
            //call method to save in database.
            validateInputAndSave();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    private void validateInputAndSave() {
        String name = taskName.getText().toString();
        String description = taskDescription.getText().toString();
        String dueDate = taskDueDate.getText().toString();
        String dueTime = taskDueTime.getText().toString();
        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(description) || TextUtils.isEmpty(selectedCategory)) {
            Toast.makeText(AddTaskActivity.this, R.string.invalid_input_values, Toast.LENGTH_LONG).show();
        } else if (TextUtils.isEmpty(dueDate) || TextUtils.isEmpty(dueTime)) {
            Toast.makeText(AddTaskActivity.this, R.string.task_date_and_time, Toast.LENGTH_LONG).show();
        } else {
            //add task to realm database
            long currentRowId = getLastInsertedRowId();
            String[] databaseValues = new String[]{name, description, selectedCategory, dueDate, dueTime};
            addNewTaskToRealmDatabase(databaseValues, isAlarmSet, currentRowId);
        }
    }
    private void addNewTaskToRealmDatabase(final String[] columnValues, final boolean isAlarm, final long id){
        //Insert to realm database
        realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                TaskModel mModel = realm.createObject(TaskModel.class, id);
                mModel.setName(columnValues[0]);
                mModel.setDescription(columnValues[1]);
                mModel.setCategory(columnValues[2]);
                mModel.setDateTime(columnValues[3] + " " + columnValues[4]);
                mModel.setReminder(isAlarm);
            }
        });
        resetViewInput();
    }
    public static class TimePicker extends DialogFragment implements TimePickerDialog.OnTimeSetListener {
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final Calendar c = Calendar.getInstance();
            int hour = c.get(Calendar.HOUR_OF_DAY);
            int minute = c.get(Calendar.MINUTE);
            return new TimePickerDialog(getActivity(), this, hour, minute, DateFormat.is24HourFormat(getActivity()));
        }
        @Override
        public void onTimeSet(android.widget.TimePicker view, int hourOfDay, int minute) {
            selectedTime =  String.valueOf(hourOfDay) + ":" + String.valueOf(minute);
            taskDueTime.setText(String.valueOf(hourOfDay) + ":" + String.valueOf(minute));
        }
    }
    public static class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final Calendar c = Calendar.getInstance();
            int year = c.get(Calendar.YEAR);
            int month = c.get(Calendar.MONTH);
            int day = c.get(Calendar.DAY_OF_MONTH);
            return new DatePickerDialog(getActivity(), this, year, month, day);
        }
        public void onDateSet(DatePicker view, int year, int month, int day) {
            selectedDate = String.valueOf(year) + "-" + String.valueOf(month) + "-" + String.valueOf(day);
            taskDueDate.setText(String.valueOf(year) + "-" + String.valueOf(month) + "-" + String.valueOf(day));
        }
    }
    private long getLastInsertedRowId(){
        long id = realm.where(TaskModel.class).max("id").longValue();
        return id + 1;
    }
    private void resetViewInput(){
        taskName.setText("");
        taskDescription.setText("");
        taskDueDate.setText("");
        taskDueTime.setText("");
        reminder.setChecked(false);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        realm.close();
    }
}

 activity_add_task.xml

Open the layout file and add the code below to it.

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="none">
<LinearLayout
    android:id="@+id/activity_add_task"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:paddingLeft="@dimen/_16sdp"
    android:paddingRight="@dimen/_16sdp"
    tools:context="com.inducesmile.androidrealmexample.AddTaskActivity">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/colorBlack"
        android:text="@string/add_name_of_task_to_do"
        android:textStyle="bold"
        android:layout_marginTop="@dimen/_16sdp"/>
    <EditText
        android:id="@+id/add_task_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="@dimen/_12sdp"
        android:layout_marginTop="@dimen/_12sdp"
        android:inputType="text"
        android:background="@drawable/brown_border"
        android:maxLines="1"
        android:layout_marginRight="@dimen/_8sdp"
        android:layout_marginEnd="@dimen/_8sdp"
        android:textColor="@color/colorBlack"
        android:padding="@dimen/_12sdp"/>
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/colorBlack"
        android:text="@string/add_task_description"
        android:textSize="@dimen/_12sdp"
        android:textStyle="bold"
        android:layout_marginTop="@dimen/_16sdp"/>
    <EditText
        android:id="@+id/add_task_description"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/_12sdp"
        android:gravity="top|left"
        android:padding="@dimen/_8sdp"
        android:background="@drawable/brown_border"
        android:lines="6"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/colorBlack"
        android:text="@string/select_task_category"
        android:textSize="@dimen/_12sdp"
        android:textStyle="bold"
        android:layout_marginTop="@dimen/_16sdp"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/_12sdp"
        android:background="@drawable/brown_border">
        <Spinner
            android:id="@+id/select_category"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:popupBackground="@drawable/spinner_border"
            android:padding="@dimen/_8sdp">
        </Spinner>
    </LinearLayout>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/colorBlack"
        android:text="@string/due_date"
        android:textSize="@dimen/_12sdp"
        android:textStyle="bold"
        android:layout_marginTop="@dimen/_16sdp"/>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/_12sdp"
        android:orientation="horizontal">
        <EditText
            android:id="@+id/add_task_ending"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="4"
            android:textSize="@dimen/_12sdp"
            android:inputType="text"
            android:background="@drawable/brown_border"
            android:maxLines="1"
            android:textColor="@color/colorBlack"
            android:padding="@dimen/_12sdp"/>
        <ImageView
            android:id="@+id/add_task_date"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:contentDescription="@string/app_name"
            android:src="@drawable/addcalendar"
            android:layout_gravity="center_vertical"
            android:paddingLeft="@dimen/_20sdp"
            android:layout_weight="1"/>
        <ImageView
            android:id="@+id/delete_task_date"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:contentDescription="@string/app_name"
            android:src="@drawable/cross"
            android:layout_gravity="center"
            android:layout_weight="1"/>
    </LinearLayout>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/colorBlack"
        android:text="@string/due_time"
        android:textSize="@dimen/_12sdp"
        android:textStyle="bold"
        android:layout_marginTop="@dimen/_16sdp"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/_12sdp"
        android:orientation="horizontal">
        <EditText
            android:id="@+id/add_task_ending_time"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="4"
            android:textSize="@dimen/_12sdp"
            android:inputType="text"
            android:background="@drawable/brown_border"
            android:maxLines="1"
            android:textColor="@color/colorBlack"
            android:padding="@dimen/_12sdp"/>
        <ImageView
            android:id="@+id/add_task_time"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:contentDescription="@string/app_name"
            android:src="@drawable/timer"
            android:layout_gravity="center_vertical"
            android:layout_marginTop="@dimen/_1sdp"
            android:paddingLeft="@dimen/_20sdp"
            android:layout_weight="1"/>
        <ImageView
            android:id="@+id/delete_task_time"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:contentDescription="@string/app_name"
            android:src="@drawable/cross"
            android:layout_gravity="center"
            android:layout_weight="1"/>
    </LinearLayout>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/colorBlack"
        android:text="@string/set_alarm"
        android:textSize="@dimen/_12sdp"
        android:textStyle="bold"
        android:layout_marginTop="@dimen/_16sdp"/>
    <CheckBox
        android:id="@+id/set_task_alarm"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/_12sdp"
        android:text="@string/reminder"
        android:layout_marginBottom="@dimen/_16sdp"/>
</LinearLayout>
</ScrollView>

 Create Adapters and Entity Object

Since we used a RecyclerView to list all the current task the user will do so we are going to use an adapter that will bind the RecyclerView list item with its data source.

Realm database uses its own custom RecyclerView Adapter class name RealmRecyclerViewAdapter. We are going to extend this class to create our own custom adapter.

First, let us create a new package folder and name it adapter. Inside the adapter package folder, create a java class and name it RealmAdapter.java

RealmAdapter.java

Open the create java class and add the code below to it.

import android.app.Activity;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.inducesmile.androidrealmexample.R;
import com.inducesmile.androidrealmexample.ToDoActivity;
import com.inducesmile.androidrealmexample.ViewTaskActivity;
import com.inducesmile.androidrealmexample.helper.CustomApplication;
import com.inducesmile.androidrealmexample.realm.TaskModel;
import io.realm.OrderedRealmCollection;
import io.realm.RealmRecyclerViewAdapter;
public class RealmAdapter extends RealmRecyclerViewAdapter<TaskModel, RealmAdapter.TaskViewHolder>{
    private ToDoActivity activity;
    private View itemView;
    public RealmAdapter(@NonNull ToDoActivity activity, @Nullable OrderedRealmCollection<TaskModel> data, boolean autoUpdate) {
        super(activity, data, autoUpdate);
        this.activity = activity;
    }
    @Override
    public TaskViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.task_layout, parent, false);
        itemView = view;
        return new TaskViewHolder(view);
    }
    @Override
    public void onBindViewHolder(TaskViewHolder holder, int position) {
        TaskModel mTaskModel = getData().get(position);
        final TaskObject taskModel = new TaskObject(mTaskModel.getId(), mTaskModel.getName(), mTaskModel.getDescription(),
                mTaskModel.getDateTime(), mTaskModel.getCategory(), mTaskModel.getReminder());
        holder.taskName.setText(mTaskModel.getName());
        if(!TextUtils.isEmpty(mTaskModel.getDateTime())){
            holder.taskDueDate.setText(mTaskModel.getDateTime());
        }else{
            holder.taskDueDate.setText(R.string.no_time);
        }
        holder.taskCategory.setText(mTaskModel.getCategory());
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent viewTaskIntent = new Intent(context, ViewTaskActivity.class);
                //use Gson to serialize TaskObject to string
                String serializedTaskObject = ((CustomApplication)((Activity)context).getApplication()).getGsonObject().toJson(taskModel);
                viewTaskIntent.putExtra("TASK_OBJECT", serializedTaskObject);
                context.startActivity(viewTaskIntent);
            }
        });
    }
    class TaskViewHolder extends RecyclerView.ViewHolder{
        public TextView taskName;
        public TextView taskDueDate;
        public TextView taskCategory;
        public TaskViewHolder(View itemView) {
            super(itemView);
            taskName = (TextView)itemView.findViewById(R.id.task_name);
            taskDueDate = (TextView)itemView.findViewById(R.id.task_date_time);
            taskCategory = (TextView)itemView.findViewById(R.id.task_category);
        }
    }
}

 CustomArrayAdapter class

Since our todo list app has a built-in category option to choose from, we are going to store our categories as a string arrays in the strings.xml and use a custom arrayadapter to populate out spinner view widget.

Create a new java class file and name it CustomArrayAdapter. Let the class extends from ArrayAdapter class and override the getView method.

Open this file and add the code below to it.

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import com.inducesmile.androidrealmexample.R;
public class CustomArrayAdapter extends ArrayAdapter<String>{
    private String[] items;
    private int viewResourceId;
    public CustomArrayAdapter(Context context, int viewResourceId, String[] items) {
        super(context, viewResourceId, items);
        this.items = items;
        this.viewResourceId = viewResourceId;
    }
    public View getView(int position, View convertView, ViewGroup parent) {
        View v = convertView;
        if (v == null) {
            LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = vi.inflate(viewResourceId, null);
        }
        String adapterContent = (String)getItem(position);
        if (adapterContent != null) {
            TextView spinnerItem = (TextView) v.findViewById(R.id.textView);
            spinnerItem.setText(adapterContent);
        }
        return v;
    }
}

 TaskModel.java

The TaskModel class will represent our Realm Model class. Since we are creating a todo list app, we will add all the columns our table will contain if we are using Sqlite database as a class variables to TaskModel.

In order to convert this as a Realm database model, this class will extend from the RealmObject class.

Note that the Realm database uses annotation to specify behavours of class properties.

Open the file and add the code below to it.

public class TaskModel extends RealmObject{
    @PrimaryKey
    private int id;
    @Required
    private String name;
    @Required
    private String description;
    private String dateTime;
    private String category;
    private boolean reminder;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public String getDateTime() {
        return dateTime;
    }
    public void setDateTime(String dateTime) {
        this.dateTime = dateTime;
    }
    public String getCategory() {
        return category;
    }
    public void setCategory(String category) {
        this.category = category;
    }
    public boolean getReminder() {
        return reminder;
    }
    public void setReminder(boolean reminder) {
        this.reminder = reminder;
    }
}

 TaskObject.java

The TaskObject class is similar to the TaskModel class excerpt that it does not extends from the RealmObject class.

Create a new file and name it TaskObject.java. Open the file and add the code below to it.

public class TaskObject{
    private int id;
    private String name;
    private String description;
    private String dateTime;
    private String category;
    private boolean reminder;
    public TaskObject(int id, String name, String description, String dateTime, String category, boolean reminder) {
        this.id = id;
        this.name = name;
        this.description = description;
        this.dateTime = dateTime;
        this.category = category;
        this.reminder = reminder;
    }
    public TaskObject(String name, String description, String dateTime, String category, boolean reminder) {
        this.name = name;
        this.description = description;
        this.dateTime = dateTime;
        this.category = category;
        this.reminder = reminder;
    }
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public String getDescription() {
        return description;
    }
    public String getDateTime() {
        return dateTime;
    }
    public String getCategory() {
        return category;
    }
    public boolean getReminder() {
        return reminder;
    }
}

 Create a helper package folder

Right click on the main project folder and create a new package folder and name it helper.

CustomApplication.java

Create a new java file inside the helper package folder and name it CustomApplication.java. This class will extend from the default android Application class.

Inside the onCreate callback method of this class, we will initialise the RealmConfiguraton class. We will also create an instance of the Gson class so that it will be availale everywhere withn our application class.

Open this class and add the code below to it.

import android.app.Application;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.realm.Realm;
import io.realm.RealmConfiguration;
public class CustomApplication extends Application{
    private Gson gson;
    private GsonBuilder builder;
    @Override
    public void onCreate() {
        super.onCreate();
        builder = new GsonBuilder();
        gson = builder.create();
        Realm.init(getApplicationContext());
        RealmConfiguration realmConfiguration = new RealmConfiguration.Builder()
                .deleteRealmIfMigrationNeeded()
                .build();
        Realm.setDefaultConfiguration(realmConfiguration);
    }
    public Gson getGsonObject(){
        return gson;
    }
}

 AndroidManifest.xml

Since we have extended the Application class, we have to make a little change in our project AmdroidManifest.xml file.

Add a name property to the apllication element and use the name of the custom application class as its value. The change in the androidmenifest.xml file is shown below.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.inducesmile.androidrealmexample">
    <application
        android:name=".helper.CustomApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".SplashActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".ToDoActivity"
            />
        <activity android:name=".AddTaskActivity"
            android:parentActivityName=".ToDoActivity">
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value=".ToDoActivity"/>
        </activity>
        <activity android:name=".ViewTaskActivity"
            android:parentActivityName=".ToDoActivity">
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value=".ToDoActivity"/>
        </activity>
        <activity android:name=".EditTaskActivity"
            android:parentActivityName=".ToDoActivity">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value=".ToDoActivity"/>
        </activity>
    </application>
</manifest>

 Menu Folder

As stated before, we have added some menu action in some of the ativity classes. If you project does not have a menu folder yet, create a new folder inside the res folder and name it menu.

Create the following menu resource files inside the menu folder.

add_task_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_add_task"
        android:orderInCategory="200"
        android:title="@string/add_new_menu_task"
        android:icon="@drawable/mark"
        app:showAsAction="ifRoom" />
</menu>

 edit_task_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_delete_task"
        android:orderInCategory="200"
        android:title="@string/delete_menu_task"
        android:icon="@drawable/bin"
        app:showAsAction="ifRoom" />
    <item
        android:id="@+id/action_edit_task"
        android:orderInCategory="300"
        android:title="@string/edit_menu_task"
        android:icon="@drawable/edit"
        app:showAsAction="ifRoom" />
</menu>

 task_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_add_task"
        android:orderInCategory="300"
        android:title="@string/add_new_task"
        android:icon="@drawable/addtask"
        app:showAsAction="ifRoom" />
</menu>

This brings us to the end of this tutorial. I hope that you have learn something. Run your app and see for yourself.

Remember to subscribe with your email address to be among the first to receive my new android blog post once it is published.

Get Source Code

I have extended this example tutorial to contain other features like

  1. adding image in your ToDo task
  2. adding voice recorded file in your ToDo task
  3. Adding video file in your ToDo task
  4. Search feature
  5. Ability to store your notes on remote server
  6. Registration and login feature.

You can download the sample app here to test it. Please kindly let me know if you have a suggestion or question on how it can be better.

Note that the source code is different.

The first source code is the simple Todo App which I have explained in this tutorial. You can download the source code below.

 

If you want the source code for the added features in the app, move over to my source code store to download it. Note that it is not free.

OTHER INTERESTING POSTS:

Add a Comment