First steps on Android: Creating a simple Todo App

April 26th, 2010 by

In this tutorial we are going to build a simple todo app that is able to store simple todos in a database. The user is able to add new todos or delete old ones by clicking on a todo. For this tutorial we won’t use maven to keep it simple – if maven integration is desired – take a look at this tutorial.

 

Steps

  • Create a new android project using the Android SDK and your IDE
  • Create some packages com.hascode.android.activity and com.hascode.android.persistence
  • Create the layout in res/layout/main.xml – the main elements: a listview for the todos-list, a textbox and a button to enter new data.
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
    android:id="@+id/widget31"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    xmlns:android="http://schemas.android.com/apk/res/android"
    >
    <TableRow
    android:id="@+id/row"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:layout_below="@+id/tasklist"
    android:layout_alignParentLeft="true"
    >
    <EditText
    android:id="@+id/etNewTask"
    android:layout_width="200px"
    android:layout_height="wrap_content"
    android:text=""
    android:textSize="18sp"
    >
    </EditText>
    <Button
    android:id="@+id/btNewTask"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@+string/add_button_name"
    >
    </Button>
    </TableRow>
    <ListView
    android:id="@+id/tasklist"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:layout_alignParentLeft="true"
    >
    </ListView>
    </RelativeLayout>
  • Externalize strings in the strings.xml - e.g. the button text or the application’s name
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">SimpleTodos</string>
        <string name="add_button_name">Add new todo</string>
    </resources>
  • Create a new activity in com.hascode.android.activity named SimpleTodoActivity. Logging activity is mapped at the defined Tag in APP_TAG.
    package com.hascode.android.activity;
     
    import java.util.List;
     
    import android.app.Activity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.AdapterView;
    import android.widget.ArrayAdapter;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.ListView;
    import android.widget.TextView;
    import android.widget.AdapterView.OnItemClickListener;
     
    import com.hascode.android.R;
    import com.hascode.android.persistence.TaskProvider;
     
    public class SimpleTodoActivity extends Activity {
    	public static final String APP_TAG = "com.hascode.android.simple-todos";
    	private ListView taskView;
    	private Button btNewTask;
    	private EditText etNewTask;
    	private TaskProvider provider;
     
    	/*
    	 * (non-Javadoc)
    	 *
    	 * @see android.app.Activity#onCreate(android.os.Bundle)
    	 */
    	@Override
    	public void onCreate(Bundle bundle) {
    		super.onCreate(bundle);
    		setContentView(R.layout.main);
    		provider = new TaskProvider(this);
    		taskView = (ListView) findViewById(R.id.tasklist);
    		btNewTask = (Button) findViewById(R.id.btNewTask);
    		etNewTask = (EditText) findViewById(R.id.etNewTask);
    		btNewTask.setOnClickListener(handleNewTaskEvent);
    		renderTodos();
    	}
     
    	/**
    	 * renders the task list
    	 */
    	private void renderTodos() {
    	}
    }
  • Create the event handling for the button and add the following Listener to the class:
    	private OnClickListener handleNewTaskEvent = new OnClickListener() {
    		@Override
    		public void onClick(View view) {
    			Log.d(APP_TAG, "add task click received");
    			provider.addTask(etNewTask.getText().toString());
    			renderTodos();
    		}
    	};
  • Create an adapter class for database access in com.hascode.android.persistence named TodoProvider with methods for creating/deletion of database entries .. please note that the iterator cursor.next() does not exist anymore in the Cursor class .. new api .. new luck …
    package com.hascode.android.persistence;
     
    import java.util.ArrayList;
    import java.util.List;
     
    import android.content.ContentValues;
    import android.content.Context;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    import android.util.Log;
     
    import com.hascode.android.activity.SimpleTodoActivity;
     
    public class TodoProvider {
    	private static final String DB_NAME = "tasks";
    	private static final String TABLE_NAME = "tasks";
    	private static final int DB_VERSION = 1;
    	private static final String DB_CREATE_QUERY = "CREATE TABLE " + TABLE_NAME
    			+ " (id integer primary key autoincrement, title text not null);";
     
    	private SQLiteDatabase storage;
    	private SQLiteOpenHelper helper;
     
    	public TodoProvider(Context ctx) {
    		helper = new SQLiteOpenHelper(ctx, DB_NAME, null, DB_VERSION) {
    			@Override
    			public void onUpgrade(SQLiteDatabase db, int oldVersion,
    					int newVersion) {
    				db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
    				onCreate(db);
    			}
     
    			@Override
    			public void onCreate(SQLiteDatabase db) {
    				db.execSQL(DB_CREATE_QUERY);
    			}
    		};
    		storage = helper.getWritableDatabase();
    	}
     
    	public List<String> findAll() {
    		Log.d(SimpleTodoActivity.APP_TAG, "findAll triggered");
    		List<String> tasks = new ArrayList<String>();
    		Cursor c = storage.query(TABLE_NAME, new String[] { "title" }, null,
    				null, null, null, null);
    		if (c != null) {
    			c.moveToFirst();
    			while (c.isAfterLast() == false) {
    				tasks.add(c.getString(0));
    				c.moveToNext();
    			}
    			c.close();
    		}
    		return tasks;
    	}
     
    	public void addTask(String title) {
    		ContentValues data = new ContentValues();
    		data.put("title", title);
    		storage.insert(TABLE_NAME, null, data);
    	}
     
    	public void deleteTask(String title) {
    		storage.delete(TABLE_NAME, "title='" + title + "'", null);
    	}
     
    	public void deleteTask(long id) {
    		storage.delete(TABLE_NAME, "id=" + id, null);
    	}
    }
  • Connect the activity to the database – implement renderTodos in the Activity and add an event listener for clicks on the list items. Adapters do the job of adding items to the listview. Please do not use setClickListener for the ListView – use setOnItemClickListener instead!
    	/**
    	 * renders the task list
    	 */
    	private void renderTodos() {
    		List<String> todos = provider.findAll();
    		if (!todos.isEmpty()) {
    			Log.d(APP_TAG, String.format("%d beans found", beans.size()));
    			// render the list
    			taskView.setAdapter(new ArrayAdapter<String>(this,
    					android.R.layout.simple_list_item_1, todos
    							.toArray(new String[] {})));
     
    			// dumb item deletion onclick
    			taskView.setOnItemClickListener(new OnItemClickListener() {
    				@Override
    				public void onItemClick(AdapterView<?> parent, View view,
    						int position, long id) {
    					Log.d(APP_TAG, String.format(
    							"item with id: %d and position: %d", id, position));
    					TextView v = (TextView) view;
    					provider.deleteTask(v.getText().toString());
    					renderTodos();
     
    				}
    			});
    		} else {
    			Log.d(APP_TAG, "no tasks found");
    		}
    	}
  • Sign your app as described in this article and deploy it if you wish to test it on your phone – currently I am using the new HTC Desire :) .If you’re using Eclipse and the Android Tools for Eclipse: Project > Android Tools > Export Signed Application Package

    Export and sign the Android Application

    Export and sign the Android Application

  • Finally the app looks like this on my emulator:

    Running the application on the emulator

    Running the application on the emulator

Source download

  • You’re able to fetch the source code from this tutorial from Bitbucket
  • Alternatively if you have got Mercurial installed, just clone the repository using the following command
    hg clone http://bitbucket.org/hascode/android-simple-tasks

Troubleshooting

  • Examining the database with the Android Debugging Bridge – first list your connected devices:
    >> adb devices
    List of devices attached
    emulator-5554	device
  • Connect to your device:
    adb -s emulator-5554 shell
  • Connect to the database .. the path is /data/data/<your-app-package-name>/<your-database-name>.db
    # sqlite3 /data/data/com.hascode.android/databases/tasks.db
    SQLite version 3.5.9
    Enter ".help" for instructions
  • Look what you’ve got: Use the commands .databases and .tables to receive information about existing databases and tables
    sqlite> .databases
    seq  name             file
    ---  ---------------  ----------------------------------------------------------
    0    main             /data/data/com.hascode.android/databases/tasks.db
  • View detailed log information via logcat in the ADB console – you should define a filter to avoid information overflow
    # logcat
    W/ActivityManager(   51): Launch timeout has expired, giving up wake lock!
    W/ActivityManager(   51): Activity idle timeout for HistoryRecord{43d60f08 com.hascode.android/.activity.SimpleTaskActivity}
    D/dalvikvm(   51): GC freed 12535 objects / 605968 bytes in 128ms
    I/Process (  204): Sending signal. PID: 204 SIG: 9
    I/ActivityManager(   51): Process com.hascode.android (pid 204) has died.
    I/UsageStats(   51): Unexpected resume of com.android.launcher while already resumed in com.hascode.android
    D/com.hascode.android.simple-task(  182): item with id: 3 and position: 3 clicked
    D/com.hascode.android.simple-task(  182): item with id: 2 and position: 2 clicked
    D/com.hascode.android.simple-task(  182): item with id: 1 and position: 1 clicked
    D/com.hascode.android.simple-task(  182): item with id: 1 and position: 1 clicked

Resources

Tags: , , , , , , , ,

7 Responses to “First steps on Android: Creating a simple Todo App”

  1. rajeev Says:

    i am gettin error in SimpleTodoActivity class can u brief me thge complete code r can u just gimme the complete code

  2. micha kops Says:

    Sure! I have put my source code on bitbucket.org – just download from https://bitbucket.org/hascode/hascode-tutorials/src/tip/SimpleTasks/ or clone the repository using hg clone http://bitbucket.org/hascode/hascode-tutorials

    Hope this helps :)

  3. Agnes Says:

    Hi, i am getting error in SimpleTodoActivity class, i found the error in this line

    “import com.hascode.android.R;”

    the error sounds :

    The import com.hascode.android.R cannot be resolved

    can u explain me, how to fix the error?
    Thank you :D

  4. agnes Says:

    Hi, I am getting error in SimpleTodoActivity class

    - The import com.hascode.android.R cannot be resolved

    Would you like to give some advices? Thank you for your help and response. =)

  5. micha kops Says:

    Hi,

    this file is automatically generated by one of the ADT builders .. please check if you’ve got a valid installation of the Android Eclipse ADT plugin.

  6. ajay patel Says:

    nice tutorial

  7. micha kops Says:

    new repository location: https://bitbucket.org/hascode/android-simple-tasks

Search
Categories