3

In my custom ListAdapter, the first time that GetView() is called, convertView is passed in as NULL, but the second time it is passed in as the view that was created the first time. My ListView has 4 rows, and all 4 are on the screen at the same time. From the documentation, it seems that convertView should be a view that was already created and has now been scrolled off the screen. I expected convertView to be null all 4 times, so that it would create / inflate 4 separate views. Am I supposed to have a convertView after the first call to getView? Thanks.

In OnCreate():

    Cursor questions = db.loadQuestions(b.getLong("categoryId"), inputLanguage.getLanguageId(), outputLanguage.getLanguageId());
    startManagingCursor(questions);

    ListAdapter adapter = new QuestionsListAdapter(this, questions);

    ListView list = (ListView)findViewById(R.id.list1);
    setListAdapter(adapter);

Adapter class

private class QuestionsListAdapter extends BaseAdapter implements  ListAdapter{

    private Cursor c;
    private Context context;

    public QuestionsListAdapter(Context context, Cursor c) {
        this.c = c;
        this.context = context;
    }

    public Object getItem(int position) {
        c.moveToPosition(position);
        return new Question(c);
    }

    public long getItemId(int position) {
        c.moveToPosition(position);
        return new Question(c).get_id();
    }

    @Override
    public int getItemViewType(int position) {

        Question currentQuestion = (Question)this.getItem(position);
        if (currentQuestion.getType().equalsIgnoreCase("text"))
            return 0;
        else if (currentQuestion.getType().equalsIgnoreCase("range"))
            return 0;
        else if (currentQuestion.getType().equalsIgnoreCase("yesNo"))
            return 2;
        else if (currentQuestion.getType().equalsIgnoreCase("picker"))
            return 0;
        else if (currentQuestion.getType().equalsIgnoreCase("command"))
            return 0;
        else if (currentQuestion.getType().equalsIgnoreCase("datePicker"))
            return 0;
        else if (currentQuestion.getType().equalsIgnoreCase("diagram"))
            return 0;
        else
            return -1;
    }

    @Override
    public int getViewTypeCount() {
        return 7;
    }

    public int getCount() {
        return c.getCount();
    }

    public View getView(int position, View convertView, ViewGroup viewGroup) {

        Question currentQuestion = (Question)this.getItem(position);

        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.question_row_text, null);
        }
        //setup cell

        return convertView;
    } 
}
GendoIkari
  • 11,134
  • 6
  • 58
  • 100
  • Your analysis of the documentation is correct. Are you sure that all four views fit on the screen? Can you post some code/XML? – Gyan aka Gary Buyn Jun 07 '11 at 22:23
  • I edited with code... I hadn't added it because the behavior is noticeable as soon as GetView() is called... so I figured it can't be related to my GetView() code... – GendoIkari Jun 07 '11 at 22:33
  • Also, the list basically loads fine... and I see all 4 rows completely on the screen. – GendoIkari Jun 07 '11 at 22:34
  • 3
    I'm posting this as a comment, not an answer, because I'm going solely from memory here, but: I think this is consistent with the behavior I've seen. Have you let the code run to completion? If I'm remembering right, GetView will be called twice for each row that's to be displayed. I *think* that the first set of calls is done for layout purposes, and the second set returns the views that will actually be displayed. Your code should do the same thing in either case (the same thing being, just use the ConvertView that's passed in.) – Dan Breslau Jun 07 '11 at 23:08
  • @Dan - I think you've got it actually... It looks like it runs through all 4 once, with the latter 3 passing in the convertView that was created in the first one. But then, it runs through all 4 again... and this time, it does NOT pass in a convertView for the latter 3. So this is when the latter 3 views are created. Thanks a bunch! – GendoIkari Jun 07 '11 at 23:21
  • @Dan you're right, it was mentioned in a [google I/O presentation](http://www.youtube.com/watch?v=wDBM6wVEO70), @ minute 41:30. – dmon Jun 08 '11 at 03:45
  • @Dan if you post that as an answer; I'll accept it. – GendoIkari Jun 15 '11 at 20:16

3 Answers3

3

If I remember right, GetView will be called twice for each row that's to be displayed. I think that the first set of calls is done for layout purposes, and the second set returns the views that will actually be displayed. It makes sense that the second set of calls should return the same Views that were returned in the first set of calls.

In any case, your code should not care whether it's being called for layout or display. In almost all situations, if the convertView is non-null, then normally that convertView should be returned; otherwise, you should return a new View.

Dan Breslau
  • 11,114
  • 2
  • 33
  • 42
  • 1
    `getView()` isn't normally called more than once per row displayed UNLESS something in `getView()` invalidates your layout. One very common way for this to happen is if you call `TextView.setText()` on a TextView that has a `layout_width='wrap_content'` or if the TextView's height will change as a result of the `setText()` – Steve Prentice Jun 15 '11 at 21:13
  • Just watched the video linked into the question's comments and it appears this happens in a normal scenario too for the first 3 list items. Not sure I totally understand that yet though. – Steve Prentice Jun 15 '11 at 21:16
0

Fast forward to 2014 and I was having the same problem... None of the solutions in this post worked for me. As I was watching the Google I/O video referenced in above answers and comments, the answer (for my particular case) became immediately obvious at about the 19 minute mark...

THE NUMBER RETURNED FROM GETVIEWTYPECOUNT() MUST REMAIN CONSTANT THROUGHOUT THE LIFE OF THE ADAPTER!!!!

I had created an adapter that could dynamically have any number of types of views/data... and my getViewTypeCount() method returned the current number of view types... so, if I ever added a new data type to the adapter that return value would change.

Making it a constant number fixed my problem. Hopefully this will help other people in the future as well.

Justin
  • 6,344
  • 6
  • 35
  • 34
0

To be honest I can't see much wrong with your code. You might want to try extending CursorAdapter and just overriding a few methods so that it can manage the cursor for you.

Gyan aka Gary Buyn
  • 11,664
  • 2
  • 21
  • 26