Latest:
I updated the code and solved the issues regarding running on lower API android.
I have tested on android version 2.1 (api level 7)
How to use:
1st screen is an empty list view.
Click on Enter Data button on first screen
2nd screen is a form view. add data to the text boxes and click add button
this data will be saved to database and take you to the first screen
Key Points
Cursor adapter is specialized adapter for a list view, when the data comes from database.
res/layout/main.xml
it has a list view, currently empty.
this list view will be populated with data coming from database
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <? xml version = "1.0" encoding = "utf-8" ?> android:layout_width = "fill_parent" android:layout_height = "fill_parent" android:orientation = "vertical" > < ListView android:id = "@+id/list_data" android:layout_height = "match_parent" android:layout_width = "match_parent" android:layout_weight = "1" /> < Button android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_weight = "0" android:layout_marginTop = "5dp" android:text = "Enter Data" android:onClick = "onClickEnterData" /> </ LinearLayout > |
res/layout/enter_data.xml
two text boxes for entering data and the data will be saved in database
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <? xml version = "1.0" encoding = "utf-8" ?> android:orientation = "vertical" android:layout_width = "fill_parent" android:layout_height = "fill_parent" > < EditText android:id = "@+id/et_person_name" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_marginTop = "5dip" android:singleLine = "true" android:hint = "Enter Name" android:inputType = "textPersonName" android:imeOptions = "actionNext" /> < EditText android:id = "@+id/et_person_pin" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_marginTop = "5dip" android:singleLine = "true" android:hint = "Enter PIN" android:inputType = "numberDecimal" android:imeOptions = "actionDone" /> < Button android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_marginTop = "5dp" android:text = "Add" android:onClick = "onClickAdd" /> </ LinearLayout > |
res/layout/single_row_item.xml
this xml is responsible for how each item in list view will be looked
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <? xml version = "1.0" encoding = "utf-8" ?> android:layout_width = "fill_parent" android:layout_height = "fill_parent" android:orientation = "vertical" > < TextView android:id = "@+id/tv_person_name" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:text = "Sample Data" android:textSize = "15sp" android:paddingBottom = "5dp" /> < TextView android:id = "@+id/tv_person_pin" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:text = "Sample Data 2" android:textSize = "15sp" android:paddingBottom = "5dp" /> </ LinearLayout > |
CustomCursorAdapter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | package com.example; import android.content.Context; import android.database.Cursor; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CursorAdapter; import android.widget.TextView; public class CustomCursorAdapter extends CursorAdapter { public CustomCursorAdapter(Context context, Cursor c) { super (context, c); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { // when the view will be created for first time, // we need to tell the adapters, how each item will look LayoutInflater inflater = LayoutInflater.from(parent.getContext()); View retView = inflater.inflate(R.layout.single_row_item, parent, false ); return retView; } @Override public void bindView(View view, Context context, Cursor cursor) { // here we are setting our data // that means, take the data from the cursor and put it in views TextView textViewPersonName = (TextView) view.findViewById(R.id.tv_person_name); textViewPersonName.setText(cursor.getString(cursor.getColumnIndex(cursor.getColumnName( 1 )))); TextView textViewPersonPIN = (TextView) view.findViewById(R.id.tv_person_pin); textViewPersonPIN.setText(cursor.getString(cursor.getColumnIndex(cursor.getColumnName( 2 )))); } } |
EnterDataActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | package com.example; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.EditText; public class EnterDataActivity extends Activity { EditText editTextPersonName; EditText editTextPersionPIN; /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.enter_data); editTextPersonName = (EditText) findViewById(R.id.et_person_name); editTextPersionPIN = (EditText) findViewById(R.id.et_person_pin); } public void onClickAdd (View btnAdd) { String personName = editTextPersonName.getText().toString(); String personPIN = editTextPersionPIN.getText().toString(); if ( personName.length() != 0 && personPIN.length() != 0 ) { Intent newIntent = getIntent(); newIntent.putExtra( "tag_person_name" , personName); newIntent.putExtra( "tag_person_pin" , personPIN); this .setResult(RESULT_OK, newIntent); finish(); } } } |
MyActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | package com.example; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; public class MyActivity extends Activity { private CustomCursorAdapter customAdapter; private PersonDatabaseHelper databaseHelper; private static final int ENTER_DATA_REQUEST_CODE = 1 ; private ListView listView; private static final String TAG = MyActivity. class .getSimpleName(); /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.main); databaseHelper = new PersonDatabaseHelper( this ); listView = (ListView) findViewById(R.id.list_data); listView.setOnItemClickListener( new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Log.d(TAG, "clicked on item: " + position); } }); // Database query can be a time consuming task .. // so its safe to call database query in another thread // Handler, will handle this stuff for you <img src="http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif?m=1129645325g" alt=":)" class="wp-smiley"> new Handler().post( new Runnable() { @Override public void run() { customAdapter = new CustomCursorAdapter(MyActivity. this , databaseHelper.getAllData()); listView.setAdapter(customAdapter); } }); } public void onClickEnterData(View btnAdd) { startActivityForResult( new Intent( this , EnterDataActivity. class ), ENTER_DATA_REQUEST_CODE); } @Override protected void onActivityResult( int requestCode, int resultCode, Intent data) { super .onActivityResult(requestCode, resultCode, data); if (requestCode == ENTER_DATA_REQUEST_CODE && resultCode == RESULT_OK) { databaseHelper.insertData(data.getExtras().getString( "tag_person_name" ), data.getExtras().getString( "tag_person_pin" )); customAdapter.changeCursor(databaseHelper.getAllData()); } } } |
PersonDatabaseHelper.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | package com.example; 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; public class PersonDatabaseHelper { private static final String TAG = PersonDatabaseHelper. class .getSimpleName(); // database configuration // if you want the onUpgrade to run then change the database_version private static final int DATABASE_VERSION = 1 ; private static final String DATABASE_NAME = "mydatabase.db" ; // table configuration private static final String TABLE_NAME = "person_table" ; // Table name private static final String PERSON_TABLE_COLUMN_ID = "_id" ; // a column named "_id" is required for cursor private static final String PERSON_TABLE_COLUMN_NAME = "person_name" ; private static final String PERSON_TABLE_COLUMN_PIN = "person_pin" ; private DatabaseOpenHelper openHelper; private SQLiteDatabase database; // this is a wrapper class. that means, from outside world, anyone will communicate with PersonDatabaseHelper, // but under the hood actually DatabaseOpenHelper class will perform database CRUD operations public PersonDatabaseHelper(Context aContext) { openHelper = new DatabaseOpenHelper(aContext); database = openHelper.getWritableDatabase(); } public void insertData (String aPersonName, String aPersonPin) { // we are using ContentValues to avoid sql format errors ContentValues contentValues = new ContentValues(); contentValues.put(PERSON_TABLE_COLUMN_NAME, aPersonName); contentValues.put(PERSON_TABLE_COLUMN_PIN, aPersonPin); database.insert(TABLE_NAME, null , contentValues); } public Cursor getAllData () { String buildSQL = "SELECT * FROM " + TABLE_NAME; Log.d(TAG, "getAllData SQL: " + buildSQL); return database.rawQuery(buildSQL, null ); } // this DatabaseOpenHelper class will actually be used to perform database related operation private class DatabaseOpenHelper extends SQLiteOpenHelper { public DatabaseOpenHelper(Context aContext) { super (aContext, DATABASE_NAME, null , DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { // Create your tables here String buildSQL = "CREATE TABLE " + TABLE_NAME + "( " + PERSON_TABLE_COLUMN_ID + " INTEGER PRIMARY KEY, " + PERSON_TABLE_COLUMN_NAME + " TEXT, " + PERSON_TABLE_COLUMN_PIN + " TEXT )" ; Log.d(TAG, "onCreate SQL: " + buildSQL); sqLiteDatabase.execSQL(buildSQL); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { // Database schema upgrade code goes here String buildSQL = "DROP TABLE IF EXISTS " + TABLE_NAME; Log.d(TAG, "onUpgrade SQL: " + buildSQL); sqLiteDatabase.execSQL(buildSQL); // drop previous table onCreate(sqLiteDatabase); // create the table from the beginning } } } |
AndroidManifest.xml
there is nothing special in this AndroidManifest file. still i am including this one, in case you need to see something
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <? xml version = "1.0" encoding = "utf-8" ?> package = "com.example" android:versionCode = "1" android:versionName = "1.0" > < uses-sdk android:minSdkVersion = "7" android:targetSdkVersion = "17" /> < application android:label = "@string/app_name" android:icon = "@drawable/ic_launcher" > < activity android:name = "MyActivity" android:label = "@string/app_name" > < intent-filter > < action android:name = "android.intent.action.MAIN" /> < category android:name = "android.intent.category.LAUNCHER" /> </ intent-filter > </ activity > < activity android:name = ".EnterDataActivity" /> </ application > </ manifest > |