In this tutorial, you will learn how to build a simple android application which stores a list of tasks. Each task will have a status (whether that task is complete or not). For building an application like a TODO list, some form of data storage mechanism is required. The following outlines some of the possible ways of storing data in an Android app:

  1. Using SharedPreferences
  2. Using Files
  3. Using SQL databases.

Each mechanism has its own strengths and weaknesses which we will review in some other tutorial but for this one, we will choose SQL database as the storage mechanism. 

The Database

The database contains only one table called task to store the tasks. The columns of the task table are as follows:

  1. Id ( The unique id given to the task)
  2. Task ( The name of the task)
  3. Status (Status of task, True - done, False - not done)

Create the project

Create a project in Eclipse with an activity named ViewTaskActivity as shown below:

The manifest file looks like:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.example.tasker"
   android:versionCode="1"
   android:versionName="1.0" >
<uses-sdk
       android:minSdkVersion="17"
       android:targetSdkVersion="17" />
<application
       android:allowBackup="true"
       android:icon="@drawable/ic_launcher"
       android:label="@string/app_name"
       android:theme="@style/AppTheme" >
  <activity
           android:name="com.example.tasker.ViewTask"
           android:label="@string/app_name" >
    <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
  </activity>
</application>
</manifest>

Setup up the database helper

Create a Java class to model the task object. This will help us when we set up operations for the database.

package com.example.tasker;
public class Task {
    private String taskName;
    private int status;
    private int id;
    public Task()
    {
        this.taskName=null;
        this.status=0;
    }
    public Task(String taskName, 
int status) {
        super();
        this.taskName = taskName;
        this.status = status;
    }
    public int getId() {
        return id;
    }
    public void setId(int id)
    {
        this.id = id;
    }
    public String getTaskName() {
        return taskName;
    }
    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }
    public int getStatus() {
        return status;
    }
    public void setStatus(int status)
 {
        this.status = status;
    }
}

The next step is to create a database helper to perform SQL operations on the database. Define these three database operations: Create, Read and Update in the database helper. 

Create the Database helper by extending it with SQLiteOpenHelper. And also add these two methods: on Create() and onUpgrade().

onCreate() is called only one time when the database is accessed the first time. It will contain all the table create statements. 
onUpgrade() is called when the database is modified in any way. 
package com.example.tasker;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
public class TaskerDbHelper extends SQLiteOpenHelper {
    private static final int DATABASE_VERSION = 1;
    // Database Name
    private static final String DATABASE_NAME = “taskerManager”;
    // tasks table name
    private static final String TABLE_TASKS = “tasks”;
    // tasks Table Columns names
    private static final String KEY_ID = “id”;
    private static final String KEY_TASKNAME = “taskName”;
    private static final String KEY_STATUS = “status”;
    public TaskerDbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        String sql = “CREATE TABLE IF NOT EXISTS “ + TABLE_TASKS + ” ( “
                + KEY_ID + “INTEGER PRIMARY KEY AUTOINCREMENT, “
                + KEY_TASKNAME+ ” TEXT, “
                + KEY_STATUS + ” INTEGER)”;
        db.execSQL(sql);
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldV, int newV) {
         // Drop older table if existed
        db.execSQL(“DROP TABLE IF EXISTS “ + TABLE_TASKS);
        // Create tables again
        onCreate(db);
    }
}

Add Add Operation

public void addTask(Task task) {

  SQLiteDatabase db = this.getWritableDatabase();
  ContentValues values = new ContentValues();
  values.put(KEY_TASKNAME, task.getTaskName()); // task
 name

  // status of task- can be 0 for not done and 
;1 for done
  values.put(KEY_STATUS, task.getStatus());

  // Inserting Row
  db.insert(TABLE_TASKS, null, values);
  db.close(); // Closing database connection
}

Read the tasks from the database and store all values in an ArrayList.


public List<Task> getAllTasks() {
  List<Task> taskList = new ArrayList<Ta
sk>();
  // Select All Query
  String selectQuery = "SELECT  * FROM "
 + TABLE_TASKS;
  SQLiteDatabase db = this.getWritableDatabase();
  Cursor cursor = db.rawQuery(selectQuery, null);
  // looping through all rows and adding to list
  if (cursor.moveToFirst()) {
    do {
     Task task = new Task();
     task.setId(cursor.getInt(0));
     task.setTaskName(cursor.getString(1));
     task.setStatus(cursor.getInt(2));
     // Adding contact to list
     taskList.add(task);
    } while (cursor.moveToNext());
}
// return task list
return taskList;
}

Add the Update Operation

Update the status of the task. Status is set to 0 if the task is not done and it set to 1 if done.

public void updateTask(Task task) {
  // updating row
  SQLiteDatabase db = this.getWritableDatabase();
  ContentValues values = new ContentValues();
  values.put(KEY_TASKNAME, task.getTaskName());
  values.put(KEY_STATUS, task.getStatus());
  db.update(TABLE_TASKS, values, KEY_ID + " = ?",
  new String[]{String.valueOf(task.getId())});
}

The View

Include all options in one view so that the App has two sections in one screen. On the top add the "EditText" and the “Add” button to the add tasks view. And below it shows the list of tasks in a ListView. The XML file for this view is as follows: 

<RelativeLayout 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”
tools:context=“.ViewTask” >
<EditText
android:id=“@+id/editText1
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignParentLeft=“true”
android:layout_alignParentTop=“true”
android:ems=“10” >
<requestFocus />
</EditText>
<Button
android:id=“@+id/button1
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignParentRight=“true”
android:layout_alignParentTop=“true”
android:layout_marginRight=“14dp”
android:text=“@string/button”
android:onClick=“addTaskNow”/>
<ListView
android:id=“@+id/listView1
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_alignParentLeft=“true”
android:layout_below=“@+id/button1” >
</ListView>
</RelativeLayout>

Add a checkbox to show the status of the task as well as the description of the task.

Create a new XML file in your layout folder called list_inner_view.xm l and add the following controls to it:

<?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="match_parent" >
<CheckBox
       android:id="@+id/checkBox1"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentLeft="true"
       android:focusable="false"
       android:focusableInTouchMode="false"
       android:text="CheckBox" />
</RelativeLayout>

For now, since there are no task in your todo list, your app will look like this:

Add the logic

Now to populate the list view, write a custom data adapter MyAdapter as a private class extending an ArrayAdapter.

private class MyAdapter extends ArrayAdapter<Task>
{
  Context context;
  List<Task> taskList=new ArrayList<Task>();
  int layoutResourceId;
  public MyAdapter(Context context, int layoutResourceId,
  List<Task> objects) {
  super(context, layoutResourceId, objects);
  this.layoutResourceId = layoutResourceId;
  this.taskList=objects;
  this.context=context;
}
/**This method will DEFINe what the view inside the list view will finally look like
* Here we are going to code that the checkbox state is the status of task and
* check box text is the task name
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
  LayoutInflater inflater = (LayoutInflater) context
  .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  View rowView = inflater.inflate(R.layout.list_inner_view, parent, false);
  CheckBox chk=(CheckBox)rowView.findViewById(R.id.chkStatus);
  Task current=taskList.get(position);
  chk.setText(current.getTaskName());
  chk.setChecked(current.getStatus()==1?true:false);

  return rowView;
  }
}

Add the following lines in the onCreate(...) method:

protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_view_task);
  //db is a variable of type TaskerDbHelper
  db=new TaskerDbHelper(this);
  list=db.getAllTasks();
  adapt=new MyAdapter(this,R.layout.list_inner_view , list);
  ListView listTask=(ListView)findViewById(R.id.listView1);
  listTask.setAdapter(adapt);
}

Next up is to refresh the list all the tasks in the list view whenever a task is added. So in the XML file don’t forget to declare the action listener for the Add button. Add a task and your app will look like this:

Lastly, we need to change the status of the task whenever it marked done(checkbox is checked). Go back adapter code and add a click listener onto the checkbox element.

The update code with the click listener will be as follows:

package com.example.tasker;
import java.util.ArrayList;
import java.util.List;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class ViewTask extends Activity {
    protected TaskerDbHelper db;
    List<Task> list;
    MyAdapter adapt;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_task);
        db = new TaskerDbHelper(this);
        list = db.getAllTasks();
        adapt = new MyAdapter(this, R.layout.list_inner_view, list);
        ListView listTask = (ListView) findViewById(R.id.listView1);
        listTask.setAdapter(adapt);
    }
    public void addTaskNow(View v) {
        EditText t = (EditText) findViewById(R.id.editText1);
        String s = t.getText().toString();
        if (s.equalsIgnoreCase(“”)) {
            Toast.makeText(this, “enter the task description first!!”,
                    Toast.LENGTH_LONG);
        } else {
            Task task = new Task(s, 0);
            db.addTask(task);
            Log.d(“tasker”, “data added”);
            t.setText(“”);
            adapt.add(task);
            adapt.notifyDataSetChanged();
        }
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.activity_view_task, menu);
        return true;
    }
    private class MyAdapter extends ArrayAdapter<Task> {
        Context context;
        List<Task> taskList = new ArrayList<Task>();
        int layoutResourceId;
        public MyAdapter(Context context, int layoutResourceId,
                List<Task> objects) {
            super(context, layoutResourceId, objects);
            this.layoutResourceId = layoutResourceId;
            this.taskList = objects;
            this.context = context;
        }
        /**
         * This method will DEFINe what the view inside the list view will
         * finally look like Here we are going to code that the checkbox state
         * is the status of task and check box text is the task name
         */
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            CheckBox chk = null;
            if (convertView == null) {
                LayoutInflater inflater = (LayoutInflater) context
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = inflater.inflate(R.layout.list_inner_view,
                        parent, false);
                chk = (CheckBox) convertView.findViewById(R.id.chkStatus);
                convertView.setTag(chk);
                chk.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        CheckBox cb = (CheckBox) v;
                        Task changeTask = (Task) cb.getTag();
                        changeTask.setStatus(cb.isChecked() == true ? 1 : 0);
                        db.updateTask(changeTask);
                        Toast.makeText(
                                getApplicationContext(),
                                “Clicked on Checkbox: “ + cb.getText() + ” is “
                                        + cb.isChecked(), Toast.LENGTH_LONG)
                                .show();
                    }
                });
            } else {
                chk = (CheckBox) convertView.getTag();
            }
            Task current = taskList.get(position);
            chk.setText(current.getTaskName());
            chk.setChecked(current.getStatus() == 1 ? true : false);
            chk.setTag(current);
            Log.d(“listener”, String.valueOf(current.getId()));
            return convertView;
        }
    }
}

Test it, mark the task complete, by checking the checkbox and check the database to verify the change has been saved.

In the app UI, the task will be displayed as checked.

So that is it for now folks. Hope you enjoyed this tutorial. The project source code is attached with the tutorial.

TODO App Project