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:
- Using SharedPreferences
- Using Files
- 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:
- Id ( The unique id given to the task)
- Task ( The name of the task)
- 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.