12

I have a Cursor that contains all rows from my database. That Cursor I pass to a CursorAdapter, and display the data in a list. But I need to show one extra element in the beginning. How can I do that?

I read somewhere that maybe it can be done with a CursorWrapper, and it can inject extra values into the results. But I'm not quite sure how to do that.

If someone can show me an example (code), or have another idea how to solve this, please let me know. Thx!

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Sandra
  • 4,239
  • 10
  • 47
  • 80

8 Answers8

10

How about using a combination of MergeCursor and MatrixCursor as I've suggested in this question: How to insert extra elements into a SimpleCursorAdapter or Cursor for a Spinner?

Community
  • 1
  • 1
naktinis
  • 3,957
  • 3
  • 36
  • 52
  • I solved this adding an extra element in the database. But maybe it can be done using what you've suggested. +1 – Sandra Jan 31 '13 at 13:14
4

Override getCount like this:

@Override
public int getCount() {
    final int count = super.getCount();
    return count+1;
}

and who ever uses the adapter gets one extra row. Just remember to handle this in getView ie.

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(position == 0) {
            final View v = inflater.inflate(R.layout.special_suggestion, parent,false);
            final TextView tv = (TextView) v.findViewById(android.R.id.text1);
            tv.setText("Search for '" + this.keyword + "'");
            return v;
        }
        else {
            try {
                return super.getView(position, convertView, parent);
            }
            catch (IllegalStateException e) {
                Log.e(TAG, "IllegalStateException: " + e);
            }
        }
        return inflater.inflate(this.layout, parent,false);
    }
slott
  • 3,266
  • 1
  • 35
  • 30
  • You don't have to pass `position - 1` to `super.getView`? The adapter will know to start at the first item when you've already taken that position in the list? – ray Mar 07 '16 at 19:52
2

You can use the list view's addHeaderView() or addFooterView():

TextView label = new TextView(context);
label.setText("Something Here");

listView.addHeaderView(label);

Just be sure to do so before calling setAdapter().

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
tbm
  • 1,142
  • 12
  • 22
  • 1
    This is the first time I see an answer that really addresses a good use case and is simple and worked immediately for the [tag:android] tag. Thank you so much, and I am very surprised this answer is not upvoted. – Iharob Al Asimi Mar 07 '17 at 04:13
1

You can read data from Cursor into List<YourRow>, where YourRow is a class for data from your cursor row. Then just add new YourRow item to list and use BaseAdapter for your ListView. Read this to find how to deal with BaseAdapter.

Community
  • 1
  • 1
amukhachov
  • 5,822
  • 1
  • 41
  • 60
  • Thx for the suggestion. Still I think that it may be a little bit simpler if I can inject extra values into the result. Do you know how I can do that using CursorWrapper? I found this [https://groups.google.com/forum/?fromgroups=#!topic/android-developers/QSOGjgL8kXI ], but I have no idea how to accomplish that. – Sandra Sep 19 '12 at 12:04
1

I ended up solving this problem by adding an extra element in the database. It was the simplest solution, because I needed the extra element at the beginning. So after creating my database, I insert a row in the table that I need extra element for, with the values that I want.

I also found this discussion [ https://groups.google.com/forum/?fromgroups=#!topic/android-developers/QSOGjgL8kXI ] so if someone has similar problem, it would be a starting point. Thx!

Sandra
  • 4,239
  • 10
  • 47
  • 80
  • You can see @amukhachov answer also. – Sandra Sep 19 '12 at 12:58
  • I did the same.. It's looks like an informal best practice – Renan Franca May 13 '14 at 13:13
  • This will be a somewhat reasonable solution right up until you provide a way for users to delete/ modify database entries, or need to reset the DB for any reason. Of course, if your entries are that static you probably shouldn't be using a DB in any case. The simplest, most future-proof solution is provided in my answer above. – tbm Mar 13 '17 at 16:28
0

In the adapter you can probably show the extra data in the getView method. But it kinda depends on where the data comes from, what you want to do with it (just display it?), etc.

manavo
  • 1,839
  • 2
  • 14
  • 17
  • I tried that, but if let's say I have 6 items in the cursor, getView gets called 6 times, so when I add the extra item, not all the items from the cursor are shown. There is always one item that is never shown.. That's why I wanted to add extra item in the cursor – Sandra Sep 19 '12 at 11:18
  • Yes, but when the hidden item comes into view, `getView` will be called on the adapter, will it not? Or I'm really confused! – manavo Sep 19 '12 at 11:25
  • I tried this: getView gets called regularly for the items in the cursor, and if (position == 0) I add the extra item that i need. So afterwards getView gets called for the remaining items in the cursor, and along with the extra first one, there is always one item that is never shown. i tried calling getView one more time when I add the extra item, but it is not working properly. Maybe because I don't know what to put in convertView, parent variables from the function -> getView(int position, View convertView, ViewGroup parent) – Sandra Sep 19 '12 at 11:32
  • Ah sorry! I completely misread what you wanted. I thought you wanted to add an extra column to your dataset, but it seems you want to add another row (a heading of some kind? Or just another normal row?). Correct? – manavo Sep 19 '12 at 11:37
  • Yes I have a list, for which i pull data from my database and I want the first item to be: let's say "All items". But header does not work for me, I need it to be an item from the list. – Sandra Sep 19 '12 at 11:45
  • Not sure if there is another option other than customizing the adapter. You'd need to inflate the counter by 1, then override the getItem function to get the current item+1, and if the requested item is 0, return your extra row. – manavo Sep 19 '12 at 12:07
  • 1
    I ended up inserting another row in the database. It was the simplest solution at this time. But thx anyway. – Sandra Sep 19 '12 at 12:26
0

Similar to adding an extra field to a database, you could add an additional field to the projection from the database. This saves you a little bit of space in your db and reduces information redundancy. It does mean that the field is recomputed each access instead of just stored.

Here is an example of a projection I have used to format a cost based off of the a price of an item (stored in cents) and an associated unit.

public static final String CONCATE_COST
        = "'$' || CASE WHEN SUBSTR(ROUND("+COLUMN_PRICE
            +"/100.0, 2), LENGTH(ROUND("+COLUMN_PRICE
            +"/100.0, 2))-1, 1)='.' THEN ROUND("+COLUMN_PRICE
            +"/100.0, 2) || '0' else ROUND("+COLUMN_PRICE
            +"/100.0, 2) end || "+COLUMN_UNIT;

Use that as a column to pass to your listview and you can pair it with a field in your view directly.

CoatedMoose
  • 3,624
  • 1
  • 20
  • 36
0

In those cases my solution is to execute an UNION query with static values. EX:

(Select -1 as id, "HERE GOES THE NAME" as name, "HERE GOES THE ADDRESS" as address) UNION (Select id, name, address from contacts order by name)

(doublecheck syntax, please, I'm doing by memory)

This way, you get a first row with -1|HERE GOES THE NAME|HERE GOES THE ADDRESS plus the rest of rows

So, you are not "polluting" the database and is easy to switch the first row on/off with conditions and playing with concatenations.