0

enter image description here

I'm making a function that looks like an image.

Although not shown in the image, these items can be added or deleted through the button.

Item is composed of Header and Detail. The adapter also has HeaderAdapter and DetailAdapter respectively.

I'm using ConcatAdapter to make sure there are HeaderAdapter and DetailAdatper per group rather than the whole list.

Because I thought it would be more manageable than using multiview types in one adapter (purely my opinion).

But I have a question. HeaderAdapter.

As you can see from the image, there is one header per group. So, there must be only one HeaderItem in the HeaderAdapter of each group.

In this case, I don't think there is much reason to use the Adapter.

In my case, is it better to use a multiview type for one Adapter?

RoutineItem

sealed class RoutineItem(
    val layoutId: Int
) {
    data class Header(
        val id: String = "1",
        val workout: String = "2",
        val unit: String = "3",
    ) : RoutineItem(VIEW_TYPE) {
        companion object {
            const val VIEW_TYPE = R.layout.routine_item
        }
    }
    data class Detail(
        val id: String = UUID.randomUUID().toString(), // UUID
        val set: Int = 1,
        var weight: String ="",
        val reps: String = "1"
    ) : RoutineItem(VIEW_TYPE) {
        companion object {
            const val VIEW_TYPE = R.layout.item_routine_detail
        }
    }
}

HeaderAdapter

class HeaderAdapter(item: RoutineItem.Header) : BaseAdapter<RoutineItem.Header>(initialItem = listOf(item)) {

    override fun createViewHolder(itemView: View): GenericViewHolder<RoutineItem.Header> {
        return HeaderViewHolder(itemView)
    }

    override fun getItemCount(): Int = 1

    class HeaderViewHolder(itemView: View) : GenericViewHolder<RoutineItem.Header>(itemView)
}

DetailAdapter

class DetailAdapter(private val items: List<RoutineItem.Detail> = emptyList())
    : BaseAdapter<RoutineItem.Detail>(initialItem = items) {

    override fun createViewHolder(itemView: View): GenericViewHolder<RoutineItem.Detail> {
        return DetailViewHolder(itemView)
    }

    override fun getItemCount(): Int = items.size

    class DetailViewHolder(itemView: View) : GenericViewHolder<RoutineItem.Detail>(itemView)
}

Activity

class MainActivity : AppCompatActivity() {
    var concatAdpater: ConcatAdapter = ConcatAdapter()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val rv: RecyclerView = findViewById(R.id.rv)

        val adapterItems: ArrayList<Pair<RoutineItem.Header, List<RoutineItem.Detail>>> = arrayListOf()
        val childItems : List<RoutineItem.Detail> = listOf(
            RoutineItem.Detail(),
            RoutineItem.Detail(),
            RoutineItem.Detail(),
            RoutineItem.Detail(),
            RoutineItem.Detail()
        )

        adapterItems.add(Pair(RoutineItem.Header(), childItems))
        adapterItems.add(Pair(RoutineItem.Header(), childItems))
        adapterItems.add(Pair(RoutineItem.Header(), childItems))
        for ((header, list) in adapterItems) { // 1 adapter per group
            concatAdpater.addAdapter(HeaderAdapter(header))
            concatAdpater.addAdapter(DetailAdapter(list))
        }
        rv.adapter = concatAdpater
    }
}

Because it is a test code, there are parts that are not functionally implemented! (Ex. Dynamically adding and deleting items..)

ybybyb
  • 1,385
  • 1
  • 12
  • 33
  • 2
    I think it would make more sense to use a single adapter with multiple view heights, for simplicity *and* performance. Your RecyclerView can't do much recycling if it is composed of multiple adapters, each of which contains fewer views than will fit on a screen. The point of RecyclerView is to recycle views that scroll off one end of the screen and reuse them for the views scrolling onto the screen at the other end. But views cannot be shared between adapters in your ConcatAdapter, so almost all the performance benefit of RecyclerView is wasted. – Tenfour04 Dec 07 '21 at 19:14
  • @Tenfour04 Then, in my case, there is only one HeaderItem in HeaderAdater per group, so there are not enough Header to scroll in HeaderAdapter of each group, so RecyclerView cannot recycle in HeaderAdapter at all? – ybybyb Dec 07 '21 at 19:55
  • 1
    Correct. Every HeaderItem's ViewHolder would be a unique instance. Also, in your example picture, none of your groups of Detail items is big enough to cover the screen, so no DetailItem's ViewHolder would ever be recycled either. Even if they were enough to cover the screen, once it got to the next group, the next adapter would have to inflate new view holders for its own DetailItems. – Tenfour04 Dec 07 '21 at 20:18
  • If so, at least in my code, each group's HeaderAdapter as well as DetailAdapter are not being recycled at all. Even if there are enough DetailItems in a group to be scrollable, only the corresponding DetailAdapter in the group is recycled, and other groups are not. Can't it be recycled? – ybybyb Dec 07 '21 at 21:01
  • 1
    Each Adapter instance has its own pool of ViewHolders. They don't share with each other, so no, there is no recycling going on. You are not using ConcatAdapter for its intended purpose. It is for separate lists of completely unrelated items items to be combined into a single scrolling view. Think of a store app that shows you some special deals at the top and then a list of search results below that in the same list. ConcatAdapter would let you put these completely different things in separate adapters for code organization purposes. – Tenfour04 Dec 07 '21 at 21:27
  • thanks. I found that each adapter had its own ViewHolderPool, so it wasn't recycled. But look at the ViewHolder section of this [link](https://medium.com/androiddevelopers/merge-adapters-sequentially-with-mergeadapter-294d2942127a). Here's what you said and the solution to that. Could this be a solution? Of course I decided to use a single adapter as you described, I just want to know the answer to the solution of not recycling. – ybybyb Dec 08 '21 at 17:23
  • 1
    Oh; I didn’t realize there was an option for this. I haven’t looked deeply into it, but that might resolve it and allow sharing of the same ViewHolder classes if they are the same class. – Tenfour04 Dec 08 '21 at 18:12
  • Thanks for commenting for so long! – ybybyb Dec 09 '21 at 14:42

1 Answers1

1

It's always better to use a single adapter because item animation and state changes are way more managable with DiffUtil. Also it's easier to maintain and way more efficient (in terms of speed and resource managment).

More detailed answers:

benyuss
  • 104
  • 1
  • 9