Question

I'm still wrapping my head around ContentProviders, Loaders and SimpleCursorAdapters (I found it easier with the old startManagingCursor method). Essentially, I have a ListView activity, that when a list item is clicked, would open a new activity that had all the fields populated with the data from the DB. the user could then delete the record, or change the fields and save the changes. I'll apologise in advance for the length of this post, but I'm not certain what may/may not be relevant.

I'll provide you the OLD CODE (which was functioning) first:

public class FuncLogbook extends DashMenuActivity implements OnItemClickListener {

private ListView list_LogbookResults;
private ListAdapter list_LogbookAdap;
private ArrayList<DBDetails> LogbookArrayList;

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

    list_LogbookResults = (ListView) findViewById(R.id.list_logbook);
    list_LogbookResults.setOnItemClickListener(this);

    LogbookArrayList = new ArrayList<DBDetails>();
    list_LogbookAdap = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, populateList());
    list_LogbookResults.setAdapter(list_LogbookAdap);
}

@SuppressWarnings("deprecation")
public List<String> populateList() {
    List<String> BGLResultsList = new ArrayList<String>();

    DBProvider mDBLogbook = new DBProvider();
    AddDBHelper logbook = new AddDBHelper(this);
    logbook.open();
    Cursor cur = logbook.getEntries();
    Cursor cur = mDBLogbook.query(DBProvider.CONTENT_URI, null, null, null, null);
    startManagingCursor(cur);

    while (cur.moveToNext()) {
        String RowID = cur.getString(0);
        String Time = cur.getString(1);
        String Date = cur.getString(2);
        String BGL = cur.getString(5);

        DBDetails detailsClass = new DBDetails();
        detailsClass.setuRow(RowID);
        detailsClass.setuTime(Time);
        detailsClass.setuDate(Date);
        detailsClass.setuBGL(BGL);          

        LogbookArrayList.add(detailsClass);
        BGLResultsList.add("BGL: " + BGL + "\nRow ID: " + RowID
                +"\nDate: " + Date +"\nTime: " + Time);
    }
    return BGLResultsList;
}

@Override
protected void onResume() {
    super.onResume();
    LogbookArrayList = new ArrayList<DBDetails>();
    list_LogbookAdap = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, populateList());
    list_LogbookResults.setAdapter(list_LogbookAdap);
}

@Override
protected void onStart() {
    super.onStart();
    LogbookArrayList = new ArrayList<DBDetails>();
    list_LogbookAdap = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, populateList());
    list_LogbookResults.setAdapter(list_LogbookAdap);
}

public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {      
    Intent UpdateDBDetails = new Intent(this, UpdateDBDetails.class);

    DBDetails clickedObject = LogbookArrayList.get(arg2);
    Bundle dataBundle =  new Bundle();
    dataBundle.putString("clickeduRowId", clickedObject.getuRow());
    dataBundle.putString("clickeduTime", clickedObject.getuTime());
    dataBundle.putString("clickeduDate", clickedObject.getuDate());
    dataBundle.putString("clickeduBGL", clickedObject.getuBGL());

    UpdateDBDetails.putExtras(dataBundle);
    startActivity(UpdateDBDetails);
}

NEW CODE:

public class FuncLogbook extends ListActivity implements
LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener {

private SimpleCursorAdapter adapter;
private ArrayList<DBDetails> LogbookArrayList;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView (R.layout.dash_logbook);     
    this.getListView().setDividerHeight(2);
    fillData(); 

    ListView list = getListView();
    list.setOnItemClickListener(this); }

protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
}

private void fillData() {
    String[] from = new String[] { AddDBHelper.KEY_BGL, AddDBHelper.KEY_ROWID,
            AddDBHelper.KEY_CATEG, AddDBHelper.KEY_TIME };
    int[] to = new int[] {R.id.logBGL, R.id.logRowID, R.id.logCateg, R.id.logTime };

    getLoaderManager().initLoader(0, null, this);
    adapter = new SimpleCursorAdapter(this, R.layout.logbook_item, null, from, to, 0);

    setListAdapter(adapter);        
}

public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    String[] projection = { AddDBHelper.KEY_ROWID, AddDBHelper.KEY_BGL,
            AddDBHelper.KEY_CATEG, AddDBHelper.KEY_TIME };
    CursorLoader cl = new CursorLoader(this, DBProvider.CONTENT_URI,
            projection, null, null, null);
    return cl;
}

public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    adapter.swapCursor(data);
}

public void onLoaderReset(Loader<Cursor> loader) {
    adapter.swapCursor(null);
}

public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {       
    Intent UpdateDBDetails = new Intent(this, UpdateDBDetails.class);   

    /*DBDetails clickedObject = LogbookArrayList.get(pos);
    Bundle dataBundle =  new Bundle();
    dataBundle.putString("clickeduRowId", clickedObject.getuRow());
    dataBundle.putString("clickeduTime", clickedObject.getuTime());
    dataBundle.putString("clickeduDate", clickedObject.getuDate());
    dataBundle.putString("clickeduBGL", clickedObject.getuBGL());

    UpdateDBDetails.putExtras(dataBundle);*/
    startActivity(UpdateDBDetails);
}

When running it, I'm getting a NullPointerException on the line that reads DBDetails clickedObject = LogbookArrayList.get(pos); I know having the ArrayList is wrong, but the only tutorials I can find either show how to use an ArrayList with the onItemClick, or show how to use the selected id in a Toast message, but I can't seem to place the info into a Bundle to pass to the Activity. Any help is appreciated.

UPDATE

Based on Luksprog's answer, I've amended to

public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {       
    Intent UpdateDBDetails = new Intent(this, UpdateDBDetails.class);

    Cursor c = (Cursor) adapter.getItem(pos);
    Toast.makeText(getApplicationContext(), "Clicked Record Number " + id + " at " + pos,
            Toast.LENGTH_SHORT).show(); 

    Bundle dataBundle =  new Bundle();
    dataBundle.putString("clickeduRowId", c.getString(c.getColumnIndex(AddDBHelper.KEY_ROWID)));
    dataBundle.putString("clickeduTime", c.getString(c.getColumnIndex(AddDBHelper.KEY_TIME)));
    dataBundle.putString("clickeduDate", c.getString(c.getColumnIndex(AddDBHelper.KEY_DATE)));
    dataBundle.putString("clickeduBGL", c.getString(c.getColumnIndex(AddDBHelper.KEY_BGL)));

    UpdateDBDetails.putExtras(dataBundle);
    startActivity(UpdateDBDetails);
}

It was throwing an error if I didn't add a cast to the cursor, hence the Cursor c = (Cursor) adapter.getItem(pos);

This is still not working. Logcat shows:

10-21 23:24:02.167: E/CursorWindow(2663): Failed to read row 2, column -1 from a CursorWindow which has 8 rows, 4 columns.
10-21 23:24:02.178: E/AndroidRuntime(2663): FATAL EXCEPTION: main
10-21 23:24:02.178: E/AndroidRuntime(2663): java.lang.IllegalStateException: Couldn't read row 2, col -1 from CursorWindow.  Make sure the Cursor is initialized correctly before accessing data from it.
10-21 23:24:02.178: E/AndroidRuntime(2663):     at android.database.CursorWindow.nativeGetString(Native Method)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at android.database.CursorWindow.getString(CursorWindow.java:434)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:51)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at android.database.CursorWrapper.getString(CursorWrapper.java:114)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at com.rone.glucometer.FuncLogbook.onItemClick(FuncLogbook.java:227)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at android.widget.AdapterView.performItemClick(AdapterView.java:298)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at android.widget.AbsListView.performItemClick(AbsListView.java:1086)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at android.widget.AbsListView$PerformClick.run(AbsListView.java:2855)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at android.widget.AbsListView$1.run(AbsListView.java:3529)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at android.os.Handler.handleCallback(Handler.java:615)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at android.os.Handler.dispatchMessage(Handler.java:92)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at android.os.Looper.loop(Looper.java:137)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at android.app.ActivityThread.main(ActivityThread.java:4745)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at java.lang.reflect.Method.invokeNative(Native Method)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at java.lang.reflect.Method.invoke(Method.java:511)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
10-21 23:24:02.178: E/AndroidRuntime(2663):     at dalvik.system.NativeStart.main(Native Method)

UPDATE I realised the above logcat error was due to me not listing all the columns within the onCreateLoader; I had only listed 4 in this example for simplicity and to reduce size but I had closer to 15 columns. Thanks, your code is working perfectly Luksprog

Was it helpful?

Solution

Your second attempt doesn't work because you use the same ArrayList which now isn't initialized, as you use a Cursor based adapter. Instead you should get the Cursor from the adapter(using the getItem(position) method you'll get the Cursor position at the correct row) and get the data from it:

public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {       
    Intent UpdateDBDetails = new Intent(this, UpdateDBDetails.class);   
    Cursor c = adapter.getItem(position);
    Bundle dataBundle =  new Bundle();
    dataBundle.putString("clickeduRowId", c.getString(c.getColumnIndex(the_index_for_this_column_data))); // One of the fields from AddDBHelper.KEY_BGL, AddDBHelper.KEY_ROWID, AddDBHelper.KEY_CATEG, AddDBHelper.KEY_TIME
    // the same for the other fields. 

    UpdateDBDetails.putExtras(dataBundle);
    startActivity(UpdateDBDetails);
}

Also, to be on the safe side call the LoaderManager after you instantiate the adapter.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top