Adapter with multiple items using getItemViewType

61 Views Asked by At

I have a contact list that are separated by group header. So I have used getItemViewType in my adapter :

    @Override
    public int getItemViewType(int position) {
        ContactItem contactItem = mContacts.get(position);
        if (contactItem.getContactSeparator() == null) {
            if (contactItem.getContact().getContactType() == ContactType.SINGLE) {
                if (mShowSeparator) {
                    return TYPE_CONTACT;
                }
                return TYPE_SEARCH_CONTACT;
            }
            if (mShowSeparator) {
                return TYPE_CONTACT_MULTIPLE;
            }
            return TYPE_SEARCH_CONTACT_MULTIPLE;
        }
        return TYPE_SEPARATOR;
    }

As you see I check if Contact Separator is null or not. So here is my model class :

@Parcelize
class ContactItem @JvmOverloads constructor(
        var contact: Contact? = null,
        var contactSeparator: String? = null
) : Parcelable

Is there any cleaner solution for my model class when we have multiple items in our adapter?

For instance for Contact type I have a enum :

enum class ContactType {
    SINGLE,
    MULTIPLE
}

And set it up in background thread :

if (numbers.size() > 1) {
       contact.setContactType(ContactType.MULTIPLE);
}

But this solution seems pretty much the same as 1st one. What is your suggestion?

Source code can be found here : https://github.com/alirezaeiii/Rebtel-Contacts-Clone/tree/master

2

There are 2 best solutions below

1
Muhammad Umar On

Does something like this solve your problem? I have separated the "separator" implementation

@Override
public int getItemViewType(int position) {
    ContactItem contactItem = mContacts.get(position);
    ContactType contactType = (contactItem.contact != null) ? contactItem.contact.getContactType() : ContactType.SINGLE;
    boolean hasSeparator = mShowSeparator && contactItem.contactSeparator == null;

    if (contactItem.contactSeparator != null) {
        return TYPE_SEPARATOR;
    } else if (contactType == ContactType.SINGLE && mShowSeparator) {
        return TYPE_CONTACT;
    } else if (contactType == ContactType.SINGLE) {
        return TYPE_SEARCH_CONTACT;
    } else if (!mShowSeparator && contactType != ContactType.SINGLE) {
        return TYPE_SEARCH_CONTACT_MULTIPLE;
    } else {
        return TYPE_CONTACT_MULTIPLE;
    }
}
1
Rakib Hasan On

If you want a cleaner solution, here's one idea. Personally, I often prefer using sealed classes because they make the code more structured and easier to manage.

sealed class ContactItemViewType {
    object Separator : ContactItemViewType()
    data class ContactTypeView(val type: ContactType, val showSeparator: Boolean) : ContactItemViewType()
}

In your adapter:

override fun getItemViewType(position: Int): Int {
    val contactItem = mContacts[position]
    return when (val viewType = determineViewType(contactItem)) {
        is ContactItemViewType.Separator -> TYPE_SEPARATOR
        is ContactItemViewType.ContactTypeView -> {
            val contactType = viewType.type
            val showSeparator = viewType.showSeparator
            getContactTypeViewType(contactType, showSeparator)
        }
    }
}

private fun determineViewType(contactItem: ContactItem): ContactItemViewType {
    return if (contactItem.contactSeparator == null) {
        ContactItemViewType.ContactTypeView(contactItem.contact!!.contactType, mShowSeparator)
    } else {
        ContactItemViewType.Separator
    }
}

This approach makes use of Kotlin's sealed classes to represent different view types in a more structured way.

Hope it helps!