Is there any way to use multiple LayoutManagers with MergeAdapter in a RecyclerView?

Viewed 1705

I'm working with MergeAdapter (A.K.A ConcatAdapter)

https://medium.com/androiddevelopers/merge-adapters-sequentially-with-mergeadapter-294d2942127a

But what I need to do is that the first Adapter should show a gridlayout and the second one a linearlayout, so , if I do it like this

 val headerAdapter = HeaderAdapter(requireContext(), Header("TestHeader"))
        val dividerViewAdapter = DividerViewAdapter(requireContext())
        val animalAdapter = DividerViewAdapter(requireContext(),DataSource.animalList) // -> this should be shown as a GridLayout
        val mergeAdapter = MergeAdapter(headerAdapter,dividerViewAdapter,animalAdapter)
        test_rv.layoutManager = LinearLayoutManager(requireContext())
        test_rv.adapter = mergeAdapter

Now this code works fine, but since it has a linearlayoutmanager all the content will be shown as a LinearLayout in which my animalAdapter will not display as I want.

Is there anyway to setup different layoutmanagers for the different adapters to show the data in the way I want ?

3 Answers

Actually there is here's how i solved it :

    class HorizontalContainerAdapter(private val lifecycleOwner: LifecycleOwner, val adapter : ListAdapter<*, *>) :
ListAdapter<String, HorizontalContainerAdapter.Holder>(diffCallback) {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
    val inflater = LayoutInflater.from(parent.context)
    return Holder(DataBindingUtil.inflate(inflater, R.layout.item_horizontal_container, parent, false), adapter)
}

override fun onBindViewHolder(holder: Holder, position: Int) {
    holder.binding.lifecycleOwner = lifecycleOwner
}

override fun getItemViewType(position: Int): Int {
    return R.layout.item_horizontal_container
}

override fun getItemCount(): Int {
     return 1
}

class Holder(val binding: ItemHorizontalContainerBinding, adapter: ListAdapter<*, *>) : RecyclerView.ViewHolder(binding.root) {

    init {
        binding.recyclerView.layoutManager = LinearLayoutManager(binding.recyclerView.context, LinearLayoutManager.HORIZONTAL, false)
        binding.recyclerView.adapter = adapter
    }
}

companion object {
    val diffCallback by lazy {
        object :
            DiffUtil.ItemCallback<String>() {

            override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
                return oldItem == newItem
            }

            override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
                return oldItem == newItem
            }
        }
    }
}

Mergeadapter / ConcatAdapter with Horizontal and Vertical Layouts

Best Of luck

No, there's not a way to have multiple LayoutManagers associated with one RecyclerView. The LayoutManager is for the entire RecyclerView, and is not associated with the Adapter. To put it another way: the adapter doesn't have a layout manager; instead the RecyclerView has a layout manager, and it also has an adapter.

If this were not the case, the behavior would get very strange if, say, it switched from a GridLayoutManager to a LinearLayoutManager in the middle of a row.

For your particular scenario, a row (for a vertical list) or column (for a horizontal list) in a linear layout is the same as an item in a grid with a span count equal to the number of spans, so you can simply use a GridLayoutManager for the entire RecyclerView if you set the span counts correctly.

GridLayoutManager.setSpanSizeLookup is very useful for similar situations. Instead of using the multipe layout managers, you can use GridLayoutManager.

For example, when I add 1 vertical, 3 horizontal and then 4 vertical objects one after the other, the code will be as follows:

ConcatAdapter concatAdapter = new ConcatAdapter(
        verticalItemAdapter, // contains  1 vertical item
        horizontalItemAdapter, // contains 3 horizontal items
        verticalItemAdapter, // contains 4 vertical items
);
int verticalSpanCount = 1;
int horizontalSpanCount = 3;

GridLayoutManager layoutManager = new GridLayoutManager(context, horizontalSpanCount);
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
    @Override
    public int getSpanSize(int position) {
        return concatAdapter.getWrappedAdapterAndPosition(position).first instanceof HorizontalItemAdapter
                ? verticalSpanCount 
                : horizontalSpanCount;
    }
});
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(concatAdapter);
Related