How can I create a wrapper for the RecyclerView Adapter that will accept any Adapter that implements RecyclerView.Adapter<RecyclerView.ViewHolder>

127
April 18, 2022, at 3:20 PM

I know the title is a bit weird, sorry. This is a weird one that I'm not even sure I can explain it correctly, here is my attempt:

Basically I want to implement a wrapper for any adapters that extend RecyclerView.Adapter<RecyclerView.ViewHolder> , the wrapper eventually will be able to insert rows if needed and it will modify things like getItemCount() as necessary, but I'm nowhere near that part yet.

This is my class definition:

class MyRecyclerAdapterWrapper(activity: Activity,val originalAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

I pass to it the original adapter which I want to wrap.

If I have the class definition like that, and then I try to create an instance of MyRecyclerAdapterWrapper like this:

val wrapper = MyRecyclerAdapterWrapper(activity, myOriginalAdapter)

Where myOriginalAdapter is an instance of:

class MyOriginalAdapter() : RecyclerView.Adapter<MyOriginalAdapter.MyViewHolder>() {

I then get an error saying that it is expecting RecyclerView.Adapter<RecyclerView.ViewHolder> but instead got MyOriginalAdapter.

So then I tried the out keyword on the class definition like this:

class MyRecyclerAdapterWrapper(activity: Activity,val originalAdapter: RecyclerView.Adapter<out RecyclerView.ViewHolder>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

And that allows my constructor call MyRecyclerAdapterWrapper(activity, myOriginalAdapter) to stop complaining, however then my MyRecyclerAdapterWrapper class fails at implementing some of the RecyclerView.Adapter<RecyclerView.ViewHolder> overrides because originalAdapter expects Nothing instead of RecyclerView.ViewHolder.

For example inside MyRecyclerAdapterWrapper I have this piece of code:

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        originalAdapter.onBindViewHolder(holder,position)
}

The error here is Type mismatch Required: Nothing Found: RecyclerView.ViewHolder.

So what is the correct way to handle this?

Edit: just wanted to be clear that other adapters will also use this wrapper, so the wrapper must be generic.

Edit: I should have mentioned this, there is a similar wrapper that works fine that I was using but it is in Java, the class definition on that one was:

public final class MyRecyclerAdapterWrapper extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
     public MyRecyclerAdapterWrapper(@NonNull Activity activity,
        @NonNull RecyclerView.Adapter originalAdapter){

Edit: Based on the answer from @muetzenflo I was able to get most of it to work but still have a problem with some methods.

I now have this:

class MyRecyclerAdapterWrapper<T : RecyclerView.ViewHolder>(activity: Activity, val originalAdapter: RecyclerView.Adapter<T>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

And

 class MyCustomAdapter(): RecyclerView.Adapter<MyCustomAdapter.MyViewHolder>(){
        class MyViewHolder(v: View): RecyclerView.ViewHolder(v) {
        }

This works:

val myRecyclerAdapterWrapper = MyRecyclerAdapterWrapper(this, MyCustomAdapter())

But this doesn't:

genericMethod(myRecyclerAdapterWrapper)
private fun genericMethod(myRecyclerAdapterWrapper: MyRecyclerAdapterWrapper<RecyclerView.ViewHolder>) {
    }

The error is:

Type mismatch: inferred type is MainActivity.MyRecyclerAdapterWrapper<MainActivity.MyCustomAdapter.MyViewHolder> but MainActivity.MyRecyclerAdapterWrapper<RecyclerView.ViewHolder> was expected

Edit: Think I figured out how to make the generic methods:

private fun <T> genericMethod(myRecyclerAdapterWrapper: MyRecyclerAdapterWrapper<T> ) where T : RecyclerView.ViewHolder {
    }

So in the end this is how my class ended up and it all compiles and runs:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val myRecyclerAdapterWrapper = MyRecyclerAdapterWrapper(this, MyCustomAdapter())
        genericMethod(myRecyclerAdapterWrapper)
    }
    private fun <T> genericMethod(myRecyclerAdapterWrapper: MyRecyclerAdapterWrapper<T> ) where T : RecyclerView.ViewHolder {
    }
    class MyRecyclerAdapterWrapper<T : RecyclerView.ViewHolder>(activity: Activity, val originalAdapter: RecyclerView.Adapter<T>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
            return originalAdapter.onCreateViewHolder(parent,viewType)
        }
        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
            originalAdapter.onBindViewHolder(holder as T,position)
        }
        override fun getItemCount(): Int {
            return originalAdapter.itemCount
        }
    }
    class MyCustomAdapter(): RecyclerView.Adapter<MyCustomAdapter.MyViewHolder>(){
        class MyViewHolder(v: View): RecyclerView.ViewHolder(v) {
        }
        override fun getItemCount(): Int {
            TODO("Not yet implemented")
        }
        override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
            TODO("Not yet implemented")
        }
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
            TODO("Not yet implemented")
        }
    }
}
Answer 1

There are my 2 classes that I use in all of my projects for making generalized use of databinding in RecyclerView items:

Usage

import androidx.recyclerview.widget.DiffUtil
import de.bvb09.android.R
import de.bvb09.android.data.model.news.NewsComponent
import de.bvb09.android.recyclerview.DataBindingAdapter
import de.bvb09.android.recyclerview.DataBindingViewHolder
/**
 * This adapter is responsible for the optional horizontal image gallery
 */
class ZoomImageAdapter : DataBindingAdapter<String>(DiffCallback) {
    override fun getItemViewType(position: Int): Int {
        return R.layout.item_zoom_image
    }
    /**
     * The DiffCallback is used by the [DataBindingAdapter] to check which items are
     * completely new and which just changed its content. When defining the Boolean checks, be
     * as precise as possible to avoid strange behaviour of the RecyclerView items.
     */
    object DiffCallback : 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
        }
    }
}
The ListAdapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
/**
 * This generic Adapter can be used to use any data class T as data provider for recyclerView list items.
 * To send a list of items to this adapter, use the submitList(newList) method. The DiffCallback is then called to
 * detect which items are completely new or have changed content. Animations are created accordingly and automatically.
 */
abstract class DataBindingAdapter<T>(diffCallback: DiffUtil.ItemCallback<T>) : ListAdapter<T, DataBindingViewHolder<T>>(diffCallback) {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DataBindingViewHolder<T> {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = DataBindingUtil.inflate<ViewDataBinding>(layoutInflater, viewType, parent, false)
        return DataBindingViewHolder(binding)
    }
    override fun onBindViewHolder(holder: DataBindingViewHolder<T>, position: Int) {
        val item = getItem(position)
        holder.bind(item)
    }
}
The ViewHolder
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView
import your.package.name.BR
/**
 * This ViewHolder is an addition to the [DataBindingAdapter]. From this, it receives a ViewDataBinding which
 * allows us to connect the data class T with the data-bindings defined in the item layout.
 */
class DataBindingViewHolder<T>(private val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) {
    var item: T? = null
        private set
    fun rebind() {
        binding.invalidateAll()
        binding.executePendingBindings()
    }
    fun bind(item: T) {
        this.item = item
        binding.setVariable(BR.item, item)
        binding.executePendingBindings()
    }
}
Answer 2

parameter require Nothing because you declared out for generic parameter

try to change RecyclerView.ViewHolder to MyOriginalAdapter.MyViewHolder


class MyRecyclerAdapterWrapper(activity: Activity,val originalAdapter: RecyclerView.Adapter<MyOriginalAdapter.MyViewHolder>) : RecyclerView.Adapter<MyOriginalAdapter.MyViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyOriginalAdapter.MyViewHolder {
        TODO("Not yet implemented")
    }
    override fun onBindViewHolder(holder: MyOriginalAdapter.MyViewHolder, position: Int) {
        originalAdapter.onBindViewHolder(holder, position)
    }
    override fun getItemCount(): Int {
        TODO("Not yet implemented")
    }
}
Answer 3

If I understand what you are trying to do, this should solve your issue in a generic matter.

Your RecyclerViewWrapper can use generic types for both the Adapter and for the ViewHolder

class RecyclerViewWrapper<VH : RecyclerView.ViewHolder, A : RecyclerView.Adapter<VH>>(
    activity: Activity,
    val originalAdapter: A
) : RecyclerView.Adapter<VH>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
        TODO("Not yet implemented")
    }
    override fun onBindViewHolder(holder: VH, position: Int) {
        TODO("Not yet implemented")
    }
    override fun getItemCount(): Int {
        TODO("Not yet implemented")
    }
}

Your OriginalAdapter can be whatever adapter you want

class OriginalAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        TODO("Not yet implemented")
    }
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        TODO("Not yet implemented")
    }
    override fun getItemCount(): Int {
        TODO("Not yet implemented")
    }
}

And then to use it you can just initiate it as such

fun example(activity: Activity) {
    val wrapper = RecyclerViewWrapper(activity, OriginalAdapter())
}

I think this will be able to help you (as long as I understood correctly), you may need to make minor changes to fit your needs but nothing breaking

Answer 4

You can use below wrapper class of recycler view adapter,

public class WrapperRecyclerAdapter extends RecyclerView.Adapter {
    private final RecyclerView.Adapter mAdapter;
    public WrapperRecyclerAdapter(RecyclerView.Adapter adapter) {
        super.setHasStableIds(adapter.hasStableIds());
        mAdapter = adapter;
        mAdapter.registerAdapterDataObserver(new ForwardingDataSetObserver());
    }
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return mAdapter.onCreateViewHolder(parent, viewType);
    }
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        mAdapter.onBindViewHolder(holder, position);
    }
    @Override
    public int getItemViewType(int position) {
        return mAdapter.getItemViewType(position);
    }
    @Override
    public void onViewRecycled(RecyclerView.ViewHolder holder) {
        mAdapter.onViewRecycled(holder);
    }
    @Override
    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
        mAdapter.onViewAttachedToWindow(holder);
    }
    @Override
    public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
        mAdapter.onViewDetachedFromWindow(holder);
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        mAdapter.onAttachedToRecyclerView(recyclerView);
    }
    @Override
    public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
        mAdapter.onDetachedFromRecyclerView(recyclerView);
    }
    @Override
    public long getItemId(int position) {
        return mAdapter.getItemId(position);
    }
    @Override
    public int getItemCount() {
        return mAdapter.getItemCount();
    }
    private class ForwardingDataSetObserver extends RecyclerView.AdapterDataObserver {
        @Override public void onChanged() {
            notifyDataSetChanged();
        }
        @Override public void onItemRangeChanged(int positionStart, int itemCount) {
            notifyItemRangeChanged(positionStart, itemCount);
        }
        @Override public void onItemRangeInserted(int positionStart, int itemCount) {
            notifyItemRangeInserted(positionStart, itemCount);
        }
        @Override public void onItemRangeRemoved(int positionStart, int itemCount) {
            notifyItemRangeRemoved(positionStart, itemCount);
        }
        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
           notifyDataSetChanged(); //TODO
        }
    }
}
Rent Charter Buses Company
READ ALSO
Why in the fragment inside the bottomNavigation, the request is not sent to the server from the second time onwards

Why in the fragment inside the bottomNavigation, the request is not sent to the server from the second time onwards

I have a bottom navigation and when switch between its items In two cases, two different things happen : First : When the program is first run -> Everything works finethat's mean: enter image description here

80
user = discord.utils.get(client.users, name=&quot;name&quot;, discriminator=&quot;0000&quot;) comes back with none

user = discord.utils.get(client.users, name="name", discriminator="0000") comes back with none

So i want to be able to mention a user when I input there name and discriminator but I cant get it to work as it keeps return none

105
What is the logic behind this fstring in python?

What is the logic behind this fstring in python?

From this question: Converting an integer to signed 2's complement binary string

124
How to switch to predict mode in Trax after training a model?

How to switch to predict mode in Trax after training a model?

I'm familiarizing myself with the Trax library for building deep learning models and one question that I can't find an answer to is how to switch from "train" mode to "eval" mode after model training is complete

106