Ad

Can I Defer The View Binding In An Adapter Using Kotlin Android Extension

- 1 answer

Kotlin Android Extensions provide a way that using the IDs directly in Activity/fragment/Adapters. So What I want is a generic way for an adapter and leave the bind view responsibility to the user of adapter instead of adapter itself. For example :

class GenericAdapter<T>(
    @LayoutRes private val layoutId: Int,
    private var list: List<T>,
    inline private val bind: (View, T, Int) -> Unit) : RecyclerView.Adapter<InnerHolder>() {
    override fun onBindViewHolder(holder: InnerHolder?, position: Int) {
        holder?.containerView?.let {
             bind(holder.containerView, list[position], position)
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): InnerHolder =
        InnerHolder(LayoutInflater.from(parent?.context).inflate(layoutId,
            parent,
            false))

    override fun getItemCount(): Int = list.size

    class InnerHolder(
        val containerView: View?) : RecyclerView.ViewHolder(containerView)

}

which I can just pass the layout file and bind function to bind the adapter as I want without rewriting the same code for every single layout file with and adapter.

But here is the problem: ViewHolder pattern want to eliminate call "findViewById" every time an ChildView inflated. But with my implementation of adapter. It needs to call "findViewById" every time when adapter bind a view.

Edit: for the details of the "cache" problem.

Using the GenericAdapter in Activity. Like

override fun onCreated(savedInstanceState: Bundle?){
    list.adapter = GenericAdapter<Item>(items){ view,item,position ->
        view.textview.text = item.text
    }
}

this will decompiled into this:

     TextView var2 = (TextView)var10000.findViewById(id.textview);
     Intrinsics.checkExpressionValueIsNotNull(var2, "itemView.tv_text");
     var2.setText((CharSequence)String.valueOf(var1));

But they have a experimental tool with Android Extension with a LayoutContainer for example :

class InnerHolder(
    override val containerView: View?) : RecyclerView.ViewHolder(containerView), LayoutContainer {
    fun bind(int: Int) {
        textview.text = int.toString()
    }
}

And this will perfectly use the cached views:

     TextView var10000 = (TextView)this._$_findCachedViewById(id.tv_text);

But the problem is that the cached views need inside the scope of LayoutContainer. For example this implementation wont work:

class InnerHolder(
    override val containerView: View?) : RecyclerView.ViewHolder(containerView), LayoutContainer {
    fun bind(int: Int) {
        this.bindInt(int)
    }

}
// some extension function outside the ViewHolder Scope. 
fun LayoutContainer.bindInt(int: Int) {
    tv_text.text = int.toString()

}

this will still use the findViewById to find the view:

  TextView var10000 = (TextView)$receiver.getContainerView().findViewById(id.tv_text);

So my question is there a way to remain the cache in some way and still using Android Extension with my adapter but outside the scope of viewholder?

Ad

Answer

You can use like this:

innerHolder.apply {
    tv_text.text = "test"
}

it will decompiled into this:

TextView var10000 = (TextView)innerHolder._$_findCachedViewById(id.tv_text);
var10000.setText((CharSequence)"test");
Ad
source: stackoverflow.com
Ad