Mobile
Article

Starting Android Development, Creating a Todo App

By Aldo Ziflaj

Using Android Studio? Download our Handy Cheat Sheet of Keyboard Shortcuts!

This article was updated in April 2016 to reflect changes in Android

Despite many articles related to Android on SitePoint, it’s been a while since we had a real ‘beginners’ tutorials. How better to fill that gap than with a staple of beginners tutorials, the ‘To Do’ App.

Needed Software

The ingredients every Android developer needs are:

  1. Android Studio, the official Android IDE (integrated development environment ). There may be still developers using the old ADT plugin for Eclipse, but that’s not maintained anymore. IntelliJ IDEA also supports Android development, so you may use that too.
  2. The Android SDK is the toolchain that manages everything required to build an Android app. It ships with Android Studio, but if you decide to use another IDE, you’ll have to download it.

It’s useful to have an Android device so you can test the app during development. If you can’t get your hands on an Android device, you can use the default emulator or Genymotion.

Note: Android’s biggest positive and flaw is it’s flexibility. I will use a particular IDE and SDK version, if your setup is different, then settings, code and screenshots may also vary.

You will need basic Java knowledge to follow this tutorial.

Getting Started

You can find the final code for this project on GitHub.

To create a new project, open Android Studio and click Start a new Android Studio project. Name the application “TodoList”, and add your company domain, which will be the application package. There can’t be two apps on the Play Store with the same package name or called “com.example”. I will name mine com.aziflaj.todolist.

Next, pick the platforms you want to develop for. I recommend setting the minimum SDK to support API level 15 and above. This means the application will support every smartphone with Android 4.0.3 or later.

On the next screen select Empty Activity and keep the name as MainActivity.

When Android Studio finishes generating the project you have the default “Hello, World” app.

Hello World

Building the View

In MainActivity.java, you should have code something like the below:

package com.aziflaj.todolist; // This will refelect your package name

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

On line 11, you set the view of this activity to R.layout.activity_main, which points to a file called activity_main.xml in the /res/layout directory of the project. A view controls layout of the Android interface and looks like this:

<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.aziflaj.todolist.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />
</RelativeLayout>

In the main view, you will add a ListView, which will contain a ToDo item in each row. To do this, replace the TextView element with the code below:

<ListView
  android:id="@+id/list_todo"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content" />

Now you will define a list item, which will represent a task in the interface.

Create a new layout file in the /res/layout folder called item_todo.xml. You will add two elements to this file, a TextView to show the task, and a “Done” Button to delete the task. Add this code to item_todo.xml, replacing anything that is already there.

<?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"
    android:layout_gravity="center_vertical">

    <TextView
        android:id="@+id/task_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:text="Hello"
        android:textSize="20sp" />

    <Button
        android:id="@+id/task_delete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:text="Done" />

</RelativeLayout>

The app needs a menu item to allow user to add more tasks. Add a main_menu.xml file in the /res/menu directory with the following code:

<?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:icon="@android:drawable/ic_menu_add"
        android:title="Add Task"
        app:showAsAction="always" />
</menu>

Add the code below to the MainActivity.java file, after the onCreate method:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main_menu, menu);
    return super.onCreateOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_add_task:
            Log.d(TAG, "Add a new task");
            return true;

        default:
            return super.onOptionsItemSelected(item);
    }
}

Android developers frequently create a TAG constant with the name of the class for logging. Add this to the beginning of the MainActivity class:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
...

The onCreateOptionsMenu() method inflates (renders) the menu in the main activity, and uses the onOptionsItemSelected() method to react to different user interactions with the menu item(s). If you run the application, it should look something like this:

Adding an Item

If you click the add button, you will see something like this in the Android Studio log:

03-26 22:12:50.327 2549-2549/? D/MainActivity: Add a new task

Next, you will add an AlertDialog to get the task from the user when the add item button is clicked. You already know where to add the code to react to the user, so replace the logging statement with this:

final EditText taskEditText = new EditText(this);
AlertDialog dialog = new AlertDialog.Builder(this)
        .setTitle("Add a new task")
        .setMessage("What do you want to do next?")
        .setView(taskEditText)
        .setPositiveButton("Add", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                String task = String.valueOf(taskEditText.getText());
                Log.d(TAG, "Task to add: " + task);
            }
        })
        .setNegativeButton("Cancel", null)
        .create();
dialog.show();

Now, clicking the plus button gives you this:

Add a new Task

Enter some text and when you click the add button, the Android Studio log (“logcat”) will show something like this:

03-26 23:32:18.294 12549-12549/? D/MainActivity: Task to add: I want to learn Android Development

Storing and Retrieving Data

Android ships with an embedded SQLite database. The database needs a table before it can store any tasks, called “TaskTable”. Create a new db folder in the same location as MainActivity.java. Then create a new class called TaskContract with the file name TaskContract.java:

Add this code to TaskContract.java, changing the two package names appropriately.

package com.aziflaj.todolist.db;

import android.provider.BaseColumns;

public class TaskContract {
    public static final String DB_NAME = "com.aziflaj.todolist.db";
    public static final int DB_VERSION = 1;

    public class TaskEntry implements BaseColumns {
        public static final String TABLE = "tasks";

        public static final String COL_TASK_TITLE = "title";
    }
}

The TaskContract class defines constants which used to access the data in the database. You also need a helper class called TaskDbHelper to open the database. Create this class in the db package and add the following code:

package com.aziflaj.todolist.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class TaskDbHelper extends SQLiteOpenHelper {

    public TaskDbHelper(Context context) {
        super(context, TaskContract.DB_NAME, null, TaskContract.DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String createTable = "CREATE TABLE " + TaskContract.TaskEntry.TABLE + " ( " +
                TaskContract.TaskEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
                TaskContract.TaskEntry.COL_TASK_TITLE + " TEXT NOT NULL);";

        db.execSQL(createTable);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + TaskContract.TaskEntry.TABLE);
        onCreate(db);
    }
}

On lines 15 to 17 is this SQL query:

CREATE TABLE tasks (
    _id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL
);

Now you need to adapt MainActivity to store data in the database. Add this code where you defined the DialogInterface.OnClickListener() for the AlertDialog‘s add button, replacing:

String task = String.valueOf(taskEditText.getText());
Log.d(TAG, "Task to add: " + task);

with:

String task = String.valueOf(taskEditText.getText());
SQLiteDatabase db = mHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(TaskContract.TaskEntry.COL_TASK_TITLE, task);
db.insertWithOnConflict(TaskContract.TaskEntry.TABLE,
        null,
        values,
        SQLiteDatabase.CONFLICT_REPLACE);
db.close();

This makes the whole onOptionsItemSelected() method look like:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_add_task:
            final EditText taskEditText = new EditText(this);
            AlertDialog dialog = new AlertDialog.Builder(this)
                    .setTitle("Add a new task")
                    .setMessage("What do you want to do next?")
                    .setView(taskEditText)
                    .setPositiveButton("Add", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            String task = String.valueOf(taskEditText.getText());
                            SQLiteDatabase db = mHelper.getWritableDatabase();
                            ContentValues values = new ContentValues();
                            values.put(TaskContract.TaskEntry.COL_TASK_TITLE, task);
                            db.insertWithOnConflict(TaskContract.TaskEntry.TABLE,
                                    null,
                                    values,
                                    SQLiteDatabase.CONFLICT_REPLACE);
                            db.close();
                        }
                    })
                    .setNegativeButton("Cancel", null)
                    .create();
            dialog.show();
            return true;

        default:
            return super.onOptionsItemSelected(item);
    }
}

Add a private instance of TaskDbHelper in the MainActivity class:

private TaskDbHelper mHelper;

And initialize it in the onCreate() method:

mHelper = new TaskDbHelper(this);

If you run the application, you won’t see any differences in the UI, but you can check that the database is working by executing these commands on the terminal:

[local] $ adb shell
[android] $ run-as com.aziflaj.todolist
[android] $ cd databases
[android] $ sqlite3 com.aziflaj.todolist.db
sqlite3> .dump

Note: If the two last commands don’t work for you, the SQLite3 utility is not included in most production devices, but you can install it by yourself.

Now you need to fetch all the data from the database and show it in the main view.

Replace your MainActivity.onCreate() method with this:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mHelper = new TaskDbHelper(this);
    SQLiteDatabase db = mHelper.getReadableDatabase();
    Cursor cursor = db.query(TaskContract.TaskEntry.TABLE,
            new String[]{TaskContract.TaskEntry._ID, TaskContract.TaskEntry.COL_TASK_TITLE},
            null, null, null, null, null);
    while(cursor.moveToNext()) {
        int idx = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_TITLE);
        Log.d(TAG, "Task: " + cursor.getString(idx));
    }
    cursor.close();
    db.close();
}

When you run the application, LogCat will show a list of all the tasks stored in the database. Next you will display data in the main view using an Adapter.

Get a reference to the ListView created in activity_main.xml file by adding an instance of the ListView:

private ListView mTaskListView;

Initialize the reference by adding this line of code to the onCreate() method, right after creating mHelper:

mTaskListView = (ListView) findViewById(R.id.list_todo);

Move the code (with some changes) that was logging the tasks into a private method called updateUI():

private void updateUI() {
    ArrayList<String> taskList = new ArrayList<>();
    SQLiteDatabase db = mHelper.getReadableDatabase();
    Cursor cursor = db.query(TaskContract.TaskEntry.TABLE,
            new String[]{TaskContract.TaskEntry._ID, TaskContract.TaskEntry.COL_TASK_TITLE},
            null, null, null, null, null);
    while (cursor.moveToNext()) {
        int idx = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_TITLE);
        taskList.add(cursor.getString(idx));
    }

    if (mAdapter == null) {
        mAdapter = new ArrayAdapter<>(this,
                R.layout.item_todo,
                R.id.task_title,
                taskList);
        mTaskListView.setAdapter(mAdapter);
    } else {
        mAdapter.clear();
        mAdapter.addAll(taskList);
        mAdapter.notifyDataSetChanged();
    }

    cursor.close();
    db.close();
}

Add this private field to the MainActivity class:

private ArrayAdapter<String> mAdapter;

This ArrayAdapter will help populate the ListView with the data.

If you don’t understand the updateUI() method, that’s OK. Instead of logging the tasks, add them into an ArrayList of Strings. Then check if mAdapter is created or not. If it isn’t, and mAdapter is null, create and set it as the adapter of the ListView:

mAdapter = new ArrayAdapter<>(this,
        R.layout.item_todo, // what view to use for the items
        R.id.task_title, // where to put the String of data
        taskList); // where to get all the data

mTaskListView.setAdapter(mAdapter); // set it as the adapter of the ListView instance

If the adapter is already created (which implies that it’s assigned to the ListView), clear it, re-populate it and notify the view that the data has changed. This means that the view will repaint on the screen with the new data.

To see the updated data, you need to call the updateUI() method every time the underlying data of the app changes. So, add it in two places:

  • In the onCreate() method, that initially shows all the data
  • After adding a new task using the AlertDialog

tasks listed

Deleting Tasks

After finishing a task, it should be deleted from the list.

Open the item_todo.xml layout and add this line to the Button tag:

android:onClick="deleteTask"

When the button is clicked, it calls this method deleteTask() in the MainActivity class:

public void deleteTask(View view) {
    View parent = (View) view.getParent();
    TextView taskTextView = (TextView) parent.findViewById(R.id.task_title);
    String task = String.valueOf(taskTextView.getText());
    SQLiteDatabase db = mHelper.getWritableDatabase();
    db.delete(TaskContract.TaskEntry.TABLE,
            TaskContract.TaskEntry.COL_TASK_TITLE + " = ?",
            new String[]{task});
    db.close();
    updateUI();
}

Now, clicking the Done button will delete the task from the list and the SQLite database.

Final Words

After writing all this code, you have a simple TodoList application, built with Java for Android. If this tutorial has got you interested in learning more, then your next step is looking through the rest of SitePoint’s Android content. Enjoy!

More:
  • nice one! keep writing!

    • Aldo Ziflaj

      Thanks :) I think I will write a sequel to this, introducing content providers and so

      • sure i will follow it!

      • puppeter

        keep up! i’m waiting for the next :D

      • mikiplus

        yes please! :)

      • matt

        It would be helpful to learn more about some of the generated files. Such as R.java. No need for an in depth explanation as that is easily searchable, however a brief explanation of it’s importance in developing android applications and what is generated inside of R.java. This would be good if you are planning to continue a tutorial series.

        • Aldo Ziflaj

          There are no much stuff I can say about the R.java file. It is just a Java class that you can’t change and that holds information about your resources (like strings in string.xml, layout elements, menus, etc)

  • Moazam

    Really very poor written. This tutorial does not given any hint or description what code does. I copied following menu text “public boolean onCreateOptionsMenu(Menu menu) {” and got an error. Author should also mention that we need to import android.view.Menu; import android.view.MenuItem; classes. Overall, I wasted my 15 minutes in following this tutorial

    • Aldo Ziflaj

      I don’t know what IDE do you use mate, but let me tell you something about really good IDEs. IntelliJ IDEA (which I used) and Android Studio (Google recommends it, based on IntelliJ) will import the classes in the moment you write them. If not, try to use the hint and choose ‘import ‘.
      When you use Eclipse, you get an error like ‘import ‘ and when you click it, you will see that the error is gone.

      The most important hint I could give to you is ‘Learn how to use your IDE’

    • just if your using Eclipse Shift+ctrl+o this will solve ur problem of importing!

      • WooDzu

        In my case Android Studio 0.8.6 did not recognize SimpleCursorAdapter and I had to look it up via the docs. Generally the app crashed on my Galaxy S2. I’m sure I must’ve missed something but it would be nice if the complete code was available. I’d recommend publishing it on Github. Thanks!

        • Aldo Ziflaj

          The complete code is available on GitHub, you can find a link in the end of the article

          • WooDzu

            Ah, I must’ve missed it. Thanks

    • Chris Ward

      Hi Moazam. The code is pretty well explained above after each block. There are also a few mentions of making sure to add imports. One of the main issues with Android development (as mentioned) is that all IDEs and setups can be a little different, it’s sometimes hard to cover all bases. Have you tried running the code from github?

    • Antonio

      I’m not quite sure which tutorial you’re following Moazam. This tutorial is excellent, simple, concise and contains all the step by step information that’s required to get this application working.

      From the problems you claim to be having it’s obvious you have no prior knowledge of java programming. If you read the requirements at the very top I think you’ll find Aldo has clearly stated “basic Java knowledge” as one of them. It really aggravates me when people criticise work they don’t understand.

      If you’re reading this Aldo, brilliant little tutorial here, so easy to follow. Managed to try this out and got it working in no time! I’m currently attempting to get this working with a OO Database, little tricky but i’ll get there.

  • Aldo Ziflaj
    • Brian Owuoche

      Hi Aldo, What can be the possible reason of viewing a blank white screen on the todo app?

  • AJ

    actually works

  • Patrik Rozgonyi

    I think there’s a prolem in activity class code.

    You write in example “public class MainActivity extends Activity” but i think it should be public class MainActivity extends ActionBarActivity otherwise onCreateOptionsMenu not called and option menu not shown!

  • Joe Martin

    Hey man. Really appreciated the tutorial. For anyone that auto generated the MainActivity class and had it extending ActionBarActivity, I believe this implies that the app is using appcompat_v7. This was the case for me, and as a result changing “MainActivity extends ActionBarActivity” to “MainActivity extends ListActivity” will cause the menu above the listview to dissapear. This can be fixed by keeping ActionBarActivity and then changing:

    this.setListAdapter(listAdapter);

    to

    // Display the list view
    ListView listView = (ListView) findViewById(R.id.list);
    listView.setAdapter(listAdapter);

    Also: Your DB query for delete will cause all todo-items with the same name to be deleted, but maybe this doesn’t matter for the sake of the tutorial

    • Edwin

      It didnt work for me, I had to change the
      android:id=”@android:id/list” ==> android:id=”@+id/list” :D

  • cryothic

    Two things:
    listAdapter = new SimpleCursorAdapter(…

    I think it needs a type declaration in front of it. Like
    SimpleCursorAdapter listAdapter = new SimpleCursorAdapter(…

    Second, I keep getting:
    Unable to start activity ComponentInfo{com.cryothic.tutorial/com.cryothic.tutorial.MainActivity}: java.lang.IllegalArgumentException: column ‘_id’ does not exist

    I’ve tried renaming them all to _ID (also in the create class) but still no luck :(

    • Mike Michaels

      I declared this right under my public class MainActivity.. line
      private ListAdapter listAdapter;

      seems to be working after that.

  • abhishek kumar

    i’m not getting that ‘+’ icon…what shoul i do?

  • IT

    Nice tutorial!
    The action bar is not displayed any more, any idea on how to put it back?
    Thank you.

  • Indika Wijesooriya

    Hi, Nice tutorial.
    BUT
    The problem occurs when we add two tasks with the same name
    It deletes both when pressing ‘Done’

    is there a way we can access the ID of it? :-( I’m kind of stucked there

    • Selin

      same problem here :( @aldoziflaj:disqus

    • Alan Beam

      Add this to task_view.xml:

      Then change the contents of onDoneButtonClick() to:

      View v = (View) view.getParent();
      TextView taskIdView = (TextView) v.findViewById(R.id.task_id);
      String task_id = taskIdView.getText().toString();

      Uri uri = TaskContract.CONTENT_URI;
      this.getContentResolver().delete(uri,
      TaskContract.Columns._ID + “=?”,
      new String[]{task_id});

      updateUI();

  • Pat Hardikar

    Hello, and Thank for this amazing tutorial. I’m trying to get this implemented in a navigation drawer type setup. So I’m extending Activity instead of ListActivity. I’m having the exact same issue as Joe Martin, but i cant figure out for the life of me why the below code is not working for me.
    I tried changing changing:

    this.setListAdapter(listAdapter);

    to

    // Display the list view
    ListView listView = (ListView) findViewById(R.id.list);
    listView.setAdapter(listAdapter);

    But it force closes the app as List is returned as null and it gives out NULL exception.
    Any help will be greatly appreciated. Thank you. .

    • Aakrit Shrestha

      in the activity_main.xml file . change
      android:id=”@android:id/list” to

      android:id=”@+id/list”
      and keep the extends ActionBarActivity. ..

  • sourabh sauda

    thank you to provide us such an amazing coding. thank you…
    i have a humble request for… will you please provide us update coding for the same app…
    please provide us… ASAP

    • Bobby Gaylor

      “i have a humble request for…”
      and then:
      “please provide us… ASAP”

      SMH

  • Hikaru Fan

    Thank you very much!!! Very clear explaination!!!! Great tutorial, keep it up Bro!!!!

  • Good Tutorial :)

  • Joynab Alam

    I have error in this code –> listAdapter = new SimpleCursorAdapter(
    this,
    R.layout.task_view,
    cursor,
    new String[] {TaskContrct.Columns.TASK},
    new int[] {R.id.taskTextView},
    0
    );
    I couldn’t understand how could i solve the error. Help me please.

  • Chris Ward

    Hey all, thanks for all your comments, as many have pointed out, this is an older article and likely things have changed. I’ve contacted the author to answer some of your questions and we will look at maybe issuing an update.

  • Антон Волков

    Hi! Thank a lot for tutorial – it’s really great.

    Still my Done button doesn’t work – doesn’t delete any notes – why can this be?

    • Антон Волков

      Already found myself – forgot to change XML

      • Chris Ward

        Jolly good :)

  • Chris Ward

    To any of you who have had issues, we updated the post to reflect Android changes, so try again.

  • Meshileya Israel

    ..thanks so much for this…really appreciates your contribution in the world of programming

  • Hello, thank you, I like your artical! And what is the price of this apps? In this way, I can use only Andriod or it can be iPhone apps? I think that a lot of modern people use more often iPhones than Android, that is why, I get this question!

  • Linto V

    Thank you so much for the tutorial

  • karan ladla

    can u include edit code also???

  • Logan Dawson

    Thanks for the tutorial, if I wanted to add subitems to the itemList how might I go about doing that? Would I have to make a custom ArrayAdapter?

  • RagaS

    how to sync the system events calendar with app

  • wajd

    Nice tutorial, although i started wondering if using a database was the best method, and whether it was too complex. currently working on replacing it with a combination of classes and methods as an alternative to SQLite, but would like to hear your reasoning behind using this.

  • lakshay5387

    I am still getting a bank white screen even after making above mentioned changes. Can you please suggest any corrections?

  • avinash

    hi! i did everything as you shown , ad it runs till first time displaying of app and in last when i completed all then it’s showing white screen and goes quit why it is so plz help me?

  • Pritam Kumar Shahi

    I am very new in android any help would be appreciated ..Where can i set the this method updateUI(){}….??

  • Agris

    Thank You very much for this turtorial!

  • Bipin Shrestha

    How do i do it in fragments… the deleting part!

  • r2k

    How do you edit an item from ToDo List? I am unable to get the ID of the item for the WHERE clause when I call db.update(). When I call db.update(), it updates all my entries in the listview.

  • Sherwyn Cardozo

    can i know how to use the deleteTask if i am using a fragment

  • Arnab Kadle

    Mate,
    Useful tutorial!! Thanks a ton!

    All said, I’ve got an issue!!
    Add two or more tasks with same name, and you choose to delete either one,
    All of it gets deleted!! Could you help me fix this?

Recommended
Sponsors
Get the latest in Mobile, once a week, for free.