4

I'm trying to make a ListView with the first item displayed in a different layout, and others in a common layout. Both layouts have the same elements which in pairs have the same names. When I do like this:

public View getView(int position, View convertView, ViewGroup parent) {        
    if(convertView == null) {
        if(position == 0){
            convertView = inflater.inflate(R.layout.article_list_top_item, parent, false);
            Log.d("ALA", "pos = " + position + ", inflated top");
        }
        else {
            convertView = inflater.inflate(R.layout.article_list_item, parent, false);
            Log.d("ALA", "pos = " + position + ", inflated normal");
        }
    }
    // setText, setBitmap etc here
    return convertView;
}

it didn't work. From the log I could say, inflater.inflate was triggered 6 times, inflated top 1 time and inflated normal 5 times.

What displayed is, article[0] was in layout article_list_top_item and article[1] ~ article[5] was in article_list_item.

Up until here it's ok, but the pattern repeated, which means article[6], article[12], [18],... were all in the layout article_list_top_item which is not what I want.

What can I do to make ONLY the first article to appear in article_list_top_item??

P.S. I tried renaming elements in article_list_top_item.xml and if-branched the setText setImage process, didn't help.

I tried adding else return convertView; before //setText lines, it became a mess.

I thought of making a dedicated layout element for the first item only, but that's not what I want because the whole list lays under a SwipeRefreshLayout

Please help.

Mr. Kro
  • 138
  • 9
  • 1
    have a look here: http://stackoverflow.com/questions/17566512/best-way-to-build-and-handle-list-view-with-differing-rows – Blackbelt Jul 22 '15 at 08:33
  • Thanks @Blackbelt, this solved the problem. Not that I haven't searched before I post, but I was too distracted by all those posts about ViewHolders – Mr. Kro Jul 22 '15 at 17:03

3 Answers3

6

You should use different itew view type. Your adapter reuse the view already inflated, so if you want to different kind you should tell him :

Create a holder for each of your view type

private class FirstHolder {
    //add a field for each subview in view type 1
}
private class SecondHolder {
    //add a field for each subview in view type 2
}

Override the getViewTypeCount() method

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

Override the getItemViewType(int position) method. This method tells which type of view is used for each position.

@Override
public int getItemViewType(int position) {
    if (position == 0) {
        return 0;
    } else {
        return 1;
    }
}

Build and setup the view :

public View getView(int position, View convertView, ViewGroup parent) {
    if (position == 0) {
        FirstHolder holder;
        if (convertView == null) {
            holder = new FirstHolder();
            convertView = inflater.inflate(R.layout.whatever_firstlayout, parent, false);
            //for each field of holder find the subview
            convertView.setTag(holder);
        } else {
            holder = (FirstHolder) convertView.getTag();
        }
        //set the data in subview with holder fields
    } else {
        SecondHolder holder;
        if (convertView == null) {
            holder = new SecondHolder();
            convertView = inflater.inflate(R.layout.whatever_secondlayout, parent, false);
            //for each field of holder find the subview
            convertView.setTag(holder);
        } else {
            holder = (SecondHolder) convertView.getTag();
        }
        //set the data in subview with holder fields
    }
    return convertView;
}
Ben
  • 757
  • 1
  • 7
  • 15
sonic
  • 1,894
  • 1
  • 18
  • 22
  • Great answer, with one addition: please implement the ViewHolder pattern. ListViews should NOT even exist without it. – DDsix Jul 22 '15 at 08:56
  • In the case of having multiple view types, you can make the same number of ViewHolder model classes – DDsix Jul 22 '15 at 08:56
  • Thanks, but I don't see the point of having a holder here. It only helps assigning data to layout elements, which is not what I'm having trouble upon. I'm still having the correct data assigned to the correct element in my layout, just that I need only the first item to appear differently. – Mr. Kro Jul 22 '15 at 16:39
  • Thanks @sonic, I overrided getViewTypeCount only – Mr. Kro Jul 22 '15 at 17:04
1

You can use addHeaderView. For example;

LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View head = inflater.inflate(R.layout.headerlayout, listview, false);
listview.addHeaderView(head);
Selim YILDIZ
  • 378
  • 3
  • 15
0

I found the problem. Exactly like @sonic said, I want to use different kinds of views for the same ListView so I have to tell him so, by overriding getViewTypeCount() and getItemViewType(int position). Working code:

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

@Override
public int getItemViewType(int pos){
    return pos > 0 ? 1 : 0;
}

public View getView(int position, View listItem, ViewGroup parent) {
    if(listItem == null) 
        listItem = inflater.inflate(getItemViewType(position) == 0 ?
                   R.layout.article_list_top_item : R.layout.article_list_item
                   , parent, false);
    // setText setBitmap etc. here
}

Comments: It works even if I don't use getViewTypeCount() and getItemViewType(int position) anywhere, but overriding them matters. Doubting getItemViewType(int position) would do anything, I tried not overriding it - and the result is, I had to override both to make it work.

I wonder how. Maybe everytime the view is inflated with a layout that's never used before, then the system assign that layout with a number from 0 to getViewTypeCount() - 1? Otherwise, where have it been used (in parent classes' code) to make it matter?

I'll leave this here to think later.

Mr. Kro
  • 138
  • 9