How to insert extra elements into a SimpleCursorAdapter or Cursor for a Spinner?
Asked Answered
L

3

30

I have a Spinner which is to show a list of data fetched from database. The data is returned to a cursor from query, and the cursor gets passed to spinner's SimpleCursorAdapter. It is working fine as such, but I want to insert another item on top of this data. For example, the spinner is already showing a list of user created templates saved in DB, but I want to insert "New Template" and "Empty Template" on top of the list of templates, and it needs to be inserted into Cursor/SimpleCursorAdapter somehow.

I have considered using an arraylist and populating the arraylist from cursor, but cursor is better solution for me since it contains other related rows of data too. I searched internet for other solutions and found some answers asking to use CursorWrapper for this purpose, but I could not find a concrete example how to use CursorWrapper to accomplish what I want. How can I insert some rows in cursor or can someone please give a easy to follow CursorWrapper example!! Thanks in advance.

Lehmann answered 19/7, 2011 at 22:39 Comment(0)
C
91

You can use a combination of MergeCursor and MatrixCursor with your DB cursor like this:

MatrixCursor extras = new MatrixCursor(new String[] { "_id", "title" });
extras.addRow(new String[] { "-1", "New Template" });
extras.addRow(new String[] { "-2", "Empty Template" });
Cursor[] cursors = { extras, cursor };
Cursor extendedCursor = new MergeCursor(cursors);
Clare answered 31/1, 2013 at 9:50 Comment(2)
Awesome. This is much better than loading cursor into an arraylist like others have suggested. thanks.Inspect
Is it necessary to close the extras and cursor Cursors?Opportina
M
2

This is the method I tried.

MatrixCursor m = new MatrixCursor(c.getColumnNames());
Cursor c = DBHelper.rawQuery("Select values from your_table");
MatrixCursor m = new MatrixCursor(c.getColumnNames());

//Use MatrixCursor#addRow here to add before the original cursor

while (c.moveToNext()) {
    //Use MatrixCursor#addRow here to add before the original row
    DBHelper.insertRow(c, m);
    //Use MatrixCursor#addRow here to add after the original row

}
//Use MatrixCursor#addRow here to add after the original cursor

m.addRow(new String[]{col1Val, col2Val, col3Val,..., //to match the number of columns needed});

DBHelper.insertRow()

public final static void insertRow(Cursor from, MatrixCursor to) {
final String columns[] = from.getColumnNames(), values[] = new String[columns.length];
    final int size = columns.length;

    for (int i = 0; i < size; i++) {
        values[i] = getStringFromColumn(from, columns[i]);
    }
    to.addRow(values);
}

With this method, you can add any amount of rows anywhere in your cursor. Even though it is not making use of CursorWrapper, it can be used with CursorAdapters or SimpleCursorAdapters.

Mixedup answered 14/1, 2014 at 9:27 Comment(0)
D
2

I tried the solution provided by @naktinis, but the result wasn't what I expected. What I myself wanted to achieve as an adapter in which new elements can be added at the top (index 0). However, with the solution given, new elements were indeed added at the top but only to the END of the MatrixCursor. In other words, when I added rows dynamically to the "extras" MatrixCursor, I got something like this:

  • "extras" row 1
  • "extras" row 2
  • "extras" row 3
  • "cursor" row 1
  • "cursor" row 2
  • "cursor" row 3.

However, what I really wanted to achieve was something like this:

  • "extras" row 3
  • "extras" row 2
  • "extras" row 1
  • "cursor" row 1
  • "cursor" row 2
  • "cursor" row 3.

In other words, most recent elements enter at the top (index 0).

I was able to achieve this manually by doing the follow. Note that I did not include any logic to handle dynamically removing elements from the adapter.

private class CollectionAdapter extends ArrayAdapter<String> {

  /**
   * This is the position which getItem uses to decide whether to fetch data from the
   * DB cursor or directly from the Adapter's underlying array. Specifically, any item
   * at a position lower than this offset has been added to the top of the adapter
   * dynamically.
   */
  private int mCursorOffset;

  /**
   * This is a SQLite cursor returned by a call to db.query(...).
   */
  private Cursor mCursor;

  /**
   * This stores the initial result returned by cursor.getCount().
   */
  private int mCachedCursorCount;

  public Adapter(Context context, Cursor cursor) {
    super(context, R.layout.collection_item);
    mCursor = cursor;
    mCursorOffset = 0;
    mCachedCursorCount = -1;
  }

  public void add(String item) {
    insert(item, 0);
    mCursorOffset = mCursorOffset + 1;
    notifyDataSetChanged();
  }

  @Override
  public String getItem(int position) {
    // return the item directly from underlying array if it was added dynamically.
    if (position < mCursorOffset) {
      return super.getItem(position);
    }

    // try to load a row from the cursor.
    if (!mCursor.moveToPosition(position - mCursorOffset)) {
      Log.d(TAG, "Failed to move cursor to position " + (position - mCursorOffset));
      return null; // this shouldn't happen.
    }
    return mCursor.getString(INDEX_COLLECTION_DATA);   
  }

  @Override
  public int getCount() {
    if (mCachedCursorCount == -1) {
      mCachedCursorCount = mCursor.getCount();
    }
    return mCursorOffset + mCachedCursorCount;
  }
}
Daron answered 30/9, 2014 at 17:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.