androidandroid-listviewandroid-arrayadaptercustom-arrayadapter

Listview's getView() isn't working properly when using 2 different kinds of listitem


I have a custom Array adapter for a Listview, i am using it for contacts, since i wanted the listview to be more organized i wanted to add a header for the 1st letter of the Contact's name This is my current progress:

@NonNull
    @Override
    public View getView(final int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        View listItem = convertView;
        LinearLayout header = null;
        String preLabel = " ";
        char firstChar = ' ';
        final Contact c = Contacts.get(position);
        String label = c.name;
        if(position != 0) {//OOb prevention
            preLabel = Contacts.get(position - 1).name;
            firstChar = label.toUpperCase().charAt(0);
        }



        char preFirstChar = preLabel.toUpperCase().charAt(0);
        if (listItem == null) {
            //If its the 1st position or the 1st character of the name is different inflate the layout with a header, else inflate the other layout.
            if(position==0 || firstChar != preFirstChar) {

                listItem = LayoutInflater.from(mContext).inflate(R.layout.contacts_list_item, parent, false);
                header  = (LinearLayout) listItem.findViewById(R.id.section);
                setSection(header, label);
            }else{
                listItem = LayoutInflater.from(mContext).inflate(R.layout.contacts_list2, parent, false);
            }
        }//Etc etc

I don't think its relevant to add any more code than this and even in the case that my logic to determine when to put a header is wrong this is having a weird behaviour since when i scroll down to the point where the view is not visible or destroyed when i scroll back up the position 0 has no header.

If i keep doing the same suddenly it fixes itself and now the 1st position again has a header, scroll again and now it doesn't why is this happening? Is there another method that the adapter uses to create the views? Does it try to predict which layout it will use for it to be faster?

Visual reference of the error:

Desc

As you can see the position 0 (for the sake of simplicity i put the position number in the textview instead of the contact's name) has a header at the start and after scrolling a bit it just, disappears, and then it reappears.


Solution

  • When you scroll and views get out of visible area, the ListView will re-use the same inflated Views for other locations in the list.

    So if you have logic in your getView method that may sometimes inflate R.layout.a and sometimes inflate R.layout.b that can lead to trouble, because then you might get a convertView that you need to re-use that is of type a but you need a view of type b for this specific position in the list.

    The solution is to use ViewTypes, which is how you tell the ListView that you have two different types of layouts, and then it knows when it can recycle a certain type and with which View.

    basically you should return override getViewTypeCount() and return 2 (you have two types of layouts), and move the logic of checking if we need a header or not to getItemViewType(int position).

    See here: Android ListView with different layouts for each row