Android Populating ListView from SQLite database using CursorLoader, LoaderManager and Content Provider

February 13, 2013
By

In this article, we will develop an Android application which fetches data from SQLite database using CursorLoader and displays it in a ListView. The data is fetched asynchronously using LoaderManager.

On executing first time, the application creates a database namely “sqllistviewdemo” and a table “cust_master” with some records in it. These records will be later fetched using CursorLoader and populates a ListView with CursorLoader and ContentProvider.

This application is developed in Eclipse(4.2.1) with ADT plugin(21.0.0) and Android SDK(21.0.0).


1. Create a new Androd Application project

Create a new Android application project

Figure 1 : Create a new Android application project


2. Configure the project

Configure the project

Figure 2 : Configure the project


3. Design application launcher icon

Design application launcher icon

Figure 3 : Design application launcher icon


4. Create a blank activity

Create a blank activity

Figure 4 : Create a blank activity


5. Enter MainActivity details

Enter MainActivity Details

Figure 5 : Enter MainActivity Details


6. Add Android Support library to this project

By default, Android support library (android-support-v4.jar ) is added to this project by Eclipse IDE to the directory libs. If it is not added, we can do it manually by doing the following steps :

  • Open Project Explorer by Clicking “Window -> Show View -> Project Explorer”
  • Right click this project
  • Then from popup window, Click “Android Tools -> Add Support Library “

7. Update the file res/values/strings.xml


<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">Populate ListView from Sqlite</string>
    <string name="hello_world">Hello world!</string>
    <string name="menu_settings">Settings</string>

</resources>


8. Create a new layout file res/layout/listview_item_layout.xml with the given below code


<?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:orientation="horizontal"
    android:padding="10dp" >

    <TextView
        android:id="@+id/code"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true" />

    <TextView
        android:id="@+id/name"
        android:layout_width="150dp"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/code"
        android:layout_centerVertical="true" />

    <TextView
        android:id="@+id/phone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/name"
        android:layout_centerVertical="true" />

</RelativeLayout>


9. Update the layout file res/layout/activity_main.xml


<ListView 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:id="@+id/listview"
    tools:context=".MainActivity"  />


10. Create a class file src/in/wptrafficanalyzer/sqllistviewdemo/CustomerDB.java with the given below code


package in.wptrafficanalyzer.sqllistviewdemo;

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

public class CustomerDB extends SQLiteOpenHelper{

    /** Database name */
    private static String DBNAME = "sqllistviewdemo";

    /** Version number of the database */
    private static int VERSION = 1;

    /** Field 1 of the table cust_master, which is the primary key */
    public static final String KEY_ROW_ID = "_id";

    /** Field 2 of the table cust_master, stores the customer code */
    public static final String KEY_CODE = "cust_code";

    /** Field 3 of the table cust_master, stores the customer name */
    public static final String KEY_NAME = "cust_name";

    /** Field 4 of the table cust_master, stores the phone number of the customer */
    public static final String KEY_PHONE = "cust_phone";

    /** A constant, stores the the table name */
    private static final String DATABASE_TABLE = "cust_master";

    /** An instance variable for SQLiteDatabase */
    private SQLiteDatabase mDB;

    /** Constructor */
    public CustomerDB(Context context) {
        super(context, DBNAME, null, VERSION);
        this.mDB = getWritableDatabase();
    }

    /** This is a callback method, invoked when the method
    * getReadableDatabase() / getWritableDatabase() is called
    * provided the database does not exists
    * */
    @Override
    public void onCreate(SQLiteDatabase db) {
        String sql =     "create table "+ DATABASE_TABLE + " ( "
                        + KEY_ROW_ID + " integer primary key autoincrement , "
                        + KEY_CODE + " text  , "
                        + KEY_NAME + "  text  , "
                        + KEY_PHONE + "  text  ) " ;

        db.execSQL(sql);

        sql = "insert into " + DATABASE_TABLE + " ( " + KEY_CODE + "," + KEY_NAME + "," + KEY_PHONE + " ) "
                + " values ( 'C01', 'Aji','1234567890' )";
        db.execSQL(sql);

        sql = "insert into " + DATABASE_TABLE + " ( " + KEY_CODE + "," + KEY_NAME + "," + KEY_PHONE + " ) "
                + " values ( 'C02', 'Ajith','0123456789' )";
        db.execSQL(sql);

        sql = "insert into " + DATABASE_TABLE + " ( " + KEY_CODE + "," + KEY_NAME + "," + KEY_PHONE + " ) "
                + " values ( 'C03', 'James','2013456789' )";
        db.execSQL(sql);

        sql = "insert into " + DATABASE_TABLE + " ( " + KEY_CODE + "," + KEY_NAME + "," + KEY_PHONE + " ) "
                + " values ( 'C04', 'Mohammed' , '9012345678' )";
        db.execSQL(sql);

    }

    /** Returns all the customers in the table */
    public Cursor getAllCustomers(){
        return mDB.query(DATABASE_TABLE, new String[] { KEY_ROW_ID,  KEY_CODE , KEY_NAME, KEY_PHONE } ,
                            null, null, null, null,
                            KEY_NAME + " asc ");
    }

    @Override
    public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
        // TODO Auto-generated method stub
    }
}


11. Create a ContentProvider class in the file src/in/wptrafficanalyzer/sqllistviewdemo/Customer.java with the given below code


package in.wptrafficanalyzer.sqllistviewdemo;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;

/** A custom Content Provider to do the database operations */
public class Customer extends ContentProvider{

    public static final String PROVIDER_NAME = "in.wptrafficanalyzer.sqllistviewdemo.customer";

    /** A uri to do operations on cust_master table. A content provider is identified by its uri */
    public static final Uri CONTENT_URI = Uri.parse("content://" + PROVIDER_NAME + "/customers" );

    /** Constants to identify the requested operation */
    private static final int CUSTOMERS = 1;

    private static final UriMatcher uriMatcher ;
    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(PROVIDER_NAME, "customers", CUSTOMERS);
    }

    /** This content provider does the database operations by this object */
    CustomerDB mCustomerDB;

    /** A callback method which is invoked when the content provider is starting up */
    @Override
    public boolean onCreate() {
        mCustomerDB = new CustomerDB(getContext());
        return true;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    /** A callback method which is by the default content uri */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

        if(uriMatcher.match(uri)==CUSTOMERS){
            return mCustomerDB.getAllCustomers();
        }else{
            return null;
        }
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
        String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }
}

12. Update the file src/in/wptrafficanalyzer/sqllistviewdemo/MainActivity.java


package in.wptrafficanalyzer.sqllistviewdemo;

import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.Menu;
import android.widget.ListView;

public class MainActivity extends FragmentActivity implements LoaderCallbacks<Cursor> {

    SimpleCursorAdapter mAdapter;
    ListView mListView;

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

        mListView = (ListView) findViewById(R.id.listview);

        mAdapter = new SimpleCursorAdapter(getBaseContext(),
                R.layout.listview_item_layout,
                null,
                new String[] { CustomerDB.KEY_CODE, CustomerDB.KEY_NAME, CustomerDB.KEY_PHONE},
                new int[] { R.id.code , R.id.name, R.id.phone }, 0);

        mListView.setAdapter(mAdapter);

        /** Creating a loader for populating listview from sqlite database */
        /** This statement, invokes the method onCreatedLoader() */
        getSupportLoaderManager().initLoader(0, null, this);

    }

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

    /** A callback method invoked by the loader when initLoader() is called */
    @Override
    public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
        Uri uri = Customer.CONTENT_URI;
        return new CursorLoader(this, uri, null, null, null, null);
    }

    /** A callback method, invoked after the requested content provider returned all the data */
    @Override
    public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) {
        mAdapter.swapCursor(arg1);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> arg0) {
        mAdapter.swapCursor(null);
    }
}


13. Update the AndroidManfiest.xml file


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="in.wptrafficanalyzer.sqllistviewdemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="4"
        android:targetSdkVersion="16" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <activity
            android:name="in.wptrafficanalyzer.sqllistviewdemo.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <provider
            android:name="Customer"
            android:authorities="in.wptrafficanalyzer.sqllistviewdemo.customer"
            android:exported="false" />

    </application>

</manifest>



14. Screenshot of the application

Screenshot of the application in action

Figure 6 : Screenshot of the application in action


15. Download source code


How to hire me?

I am George Mathew, working as software architect and Android app developer at wptrafficanalyzer.in

You can hire me on hourly basis or on project basis for Android applications development.

For hiring me, please mail your requirements to info@wptrafficanalyzer.in.

My other blogs
store4js.blogspot.com


Android Knowledge Quiz

Ready to test your knowledge in Android? Take this quiz :



Tags: , , , ,

29 Responses to Android Populating ListView from SQLite database using CursorLoader, LoaderManager and Content Provider

  1. sjpm on February 13, 2013 at 3:56 pm

    hi
    thanks for tutorial
    have one problem to my project . i use library sherlock in my project and in tutorial use fragmentactivity but i use SherlockActivity in project . now what i do ?

    • george on February 13, 2013 at 5:07 pm

      Hi,
      We can use the class SherlockFragmentActivity instead of SherlockActivity in the MainActivity class.

      • sjpm on February 13, 2013 at 7:23 pm

        thanks you
        but dont work listview my project :( when open activity project force close !

        • george on February 13, 2013 at 7:58 pm

          Please check the LogCat window to get any clue on error that forces the project be closed :(

          • sjpm on February 13, 2013 at 8:21 pm

            error LogCat window :

            02-12 05:58:05.658: E/AndroidRuntime(7715): FATAL EXCEPTION: main
            02-12 05:58:05.658: E/AndroidRuntime(7715): java.lang.IllegalArgumentException: column ‘_id’ does not exist
            02-12 05:58:05.658: E/AndroidRuntime(7715): at android.database.AbstractCursor.getColumnIndexOrThrow(AbstractCursor.java:314)
            02-12 05:58:05.658: E/AndroidRuntime(7715): at android.database.CursorWrapper.getColumnIndexOrThrow(CursorWrapper.java:99)

            and i use database sqlite own from assets .
            :( thanks you for answer

          • george on February 13, 2013 at 8:39 pm

            Please ensure that, your table is having the field _id which is needed for the cursor adapter to work

  2. sjpm on February 13, 2013 at 10:51 pm

    :d Thank you
    Excellent work :x

  3. sjpm on February 15, 2013 at 6:55 pm

    hi again
    have a question how change color textview item ?
    i don’t want to use change color from file xml .
    you can help me ?
    thank you

  4. erigobeli on February 26, 2013 at 7:02 pm

    Congratulations, great job!

    Is there any way to use CursorLoader and LoaderManager without content provider? Or … I must use content providers, even if I don’t want to share my datas?

    Thank you very much

  5. Shanthi on May 10, 2013 at 11:43 pm

    Awesome work :)

  6. jorge o. jr on May 17, 2013 at 5:23 pm

    Amazing work! Helped me imensely to understand the topic.

    Greeting from Brazil

  7. serg on July 4, 2013 at 6:50 pm

    Hi, thanks, nice tutorial. But “SimpleCursorAdapter” is depreciated and it’s not advised to use it. How this example can be changed to avoid using SimpleCursorAdapter? Thanks again

    • george on July 4, 2013 at 7:15 pm

      Hi Serg,

      Thank you.

      As per the documentation of the class “android.support.v4.widget.SimpleCursorAdapter”, only one of its constructor is deprecated. In this application we are not using the deprecated constructor, instead we are using the standard constructor. Please correct me, if i am wrong.

  8. serg on July 4, 2013 at 7:51 pm

    I see, thanks!

  9. Yeung on August 15, 2013 at 1:17 pm

    HI. Is it a must for a CursorLoader work with contentProvider? I read this
    http://stackoverflow.com/questions/12869373/android-cursorloader-sqlite-without-content-provider

    It seems really in this way. Although there is library loader without contentProvider, it present some drawback.

    Is it really the best way if I use CursorLoader work with contentProvider?

  10. Tony on August 28, 2014 at 1:23 am

    Can you please show me how to implement the same using AsyncTask

  11. Aman on October 18, 2014 at 1:33 pm

    Hi, I am getting an error in MainActivity class that is “R cannot be resolved”
    Please Help

  12. narcisse on November 9, 2014 at 12:35 pm

    HI,
    Thanks a lot.
    It is so clear and elegant code.
    for the first time I see such a clear contentprovider class.
    it worked well.
    Good luck for your job.

  13. rohit on November 26, 2014 at 3:42 pm

    nice tutorial.

  14. Remi on November 28, 2014 at 9:39 pm

    Hi George,

    Love your website it helped me a lot in understanding of the Android Dev. In my app that I am developing I list longitude and latitude of locations stored in SQLlite DB. I would like to click on one of the entries that consists the coordinates and display it with a marker on a map in a new activity. Could you tell me how I can pass those two fields (longitude and latitude) to a maps activity.
    Thanks you
    Marcin

  15. rohit on November 30, 2014 at 5:52 pm

    i have to modify data after fetching it from database.where do it write that method to modify data before populating in listview…????
    waiting for your reply sir.

  16. TASSEMBEDO on January 7, 2015 at 9:56 pm

    Thanks for this helpful tutorial. it’s work for me.
    How to add a textview before the listview?

  17. Nitesh Kumar on March 24, 2015 at 6:31 pm

    Hi, Nice piece of code. I have some questions.

    1. If I use content provider, my data will be public means accessible by other apps. How can I avoid it? I don’t want my data to be accessible by other apps.

    2. How to update the list if the data is changed in the database?

  18. ashwini hegade on April 16, 2015 at 4:34 pm

    Hello Mr,George
    I hav changed the Phone attibute as Place and their respective values in table inserting column an also changed the nmes but after running giving the same output as before…wat to do please tel me..

  19. Prakhar Sharma on June 30, 2015 at 5:17 pm

    Sir I am struck with something I need your Help! In your above Code only I want to add another column in the database and populate it in list view on Upgrading the version of database? So when database version changes list view will get another entry in its 5 row with extra column along with the previous data. Can you tell me how to do that.

  20. mjpy on September 13, 2015 at 10:22 am

    hi, very nice work. it works fine in my app. but here is my problem, i have a button to show another activity every time i click it, it take too long for loading the activity that has the listview. can you help me about this? Thanks in advance :)

  21. Ajay on March 15, 2016 at 12:03 am

    This is a helpful tutorial. Can you confirm this code is correct? I get a error with menu_main.
    public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
    }

  22. Ajay on March 15, 2016 at 4:09 am

    Thanks for the nice tutorial. I faced minor issue but got it working.

  23. Rajmohan on July 5, 2016 at 12:03 pm

    Hi ,George Can you help me for getting Contact name and contact number from the Phone list itself and to add those to sqlite database? your current tutorial was very beneficial..

Leave a Reply

Your email address will not be published. Required fields are marked *

Be friend at g+

Subscribe for Lastest Updates

FBFPowered by ®Google Feedburner