Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Manually created view in ViewHolder created in onCreateViewHolder

I would like to make generic adapter which let me reuse it. That's why I decided to not to create view by inflating, but pass custom, previously created view to ViewHolder in ListAdapter (RecyclerView) in onCreateViewHolder. According to its documentation it should be possible, because it says:

You can either create a new View manually or inflate it from an XML layout file.

This is my adapter implementation:

class BaseAdapter<T, V : View>(
        private val adapterConfig: AdapterConfig<T, V>,
        private val onClick: (T) -> (Unit)
) : ListAdapter<T, BaseViewHolder<V>>(adapterConfig.diffCallback) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<V> {
        return adapterConfig.viewHolder.apply {
            itemView.setOnClickListener { onClick(getItem(adapterPosition)) }
        }
    }

    override fun onBindViewHolder(holder: BaseViewHolder<V>, position: Int) {
        adapterConfig.bind(getItem(position), holder, position)
    }
}

AdapterConfig implementation class is reposnible for creating ViewHolder and it's looks like this:

override val viewHolder: BaseViewHolder<ScorerView> = BaseViewHolder(ScorerView(context))

BaseViewHolder class:

class BaseViewHolder<out V : View>(val v: V) : RecyclerView.ViewHolder(v)

(Nothing interesting until now)

My ScorerView is:

class ScorerView @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {

    init {
        LayoutInflater.from(context).inflate(R.layout.item_ranking, this, false)
    }

    fun configure(id: String, player: String, team: String, matchSet: String, points: String, pointsAvg: String) {
        idTextView.text = id
        playerTextView.text = player
        teamTextView.text = team
        matchSetTextView.text = matchSet
        pointsTextView.text = points
        pointsAvgTextView.text = pointsAvg
    }
}

The problem is that when I pass false to inflate function (which is ok, because RecyclerView require to item view wasn't attached to root view) then my widget instances (idTextView for example) is null. Otherwise when I manually attach view to root either by passing true or by calling addView(view) I'm getting a crash saying that view can't be attached when created:

ViewHolder views must not be attached when created. Ensure that you are not passing 'true' to the attachToRoot parameter of LayoutInflater.inflate(..., boolean attachToRoot)

My question is: is it even possible? If not, why official documentation saying that it's possible to manually create view?

like image 839
user3448282 Avatar asked Mar 29 '18 17:03

user3448282


People also ask

What is the difference between onCreateViewHolder and onBindViewHolder?

This method internally calls onBindViewHolder to update the ViewHolder contents with the item at the given position and also sets up some private fields to be used by RecyclerView. This method calls onCreateViewHolder to create a new ViewHolder and initializes some private fields to be used by RecyclerView.

How many times is called onCreateViewHolder?

By default it have 5. you can increase as per your need. Save this answer.

What is Oncreate view holder?

onCreateViewHolder is called when you need a new View. If there is an available Recycled View that can be provided and be bound with new data, then onBindViewHolder is called :) 96. 96. 96.

What is ViewHolder in RecyclerView?

A ViewHolder describes an item view and metadata about its place within the RecyclerView. Adapter implementations should subclass ViewHolder and add fields for caching potentially expensive findViewById results. While LayoutParams belong to the LayoutManager , ViewHolders belong to the adapter.


1 Answers

The problem is that you're returning the same view over and over again. It is an explicit requirement that onCreateViewHolder() creates a new view every time it is invoked.

The documentation says that you can create the View manually, but that still means that you manually create a different view each time onCreateViewHolder() runs.

So, because you're re-using the same view every time, the second time your app calls onCreateViewHolder(), the view is already attached to the parent.

like image 88
Ben P. Avatar answered Sep 27 '22 22:09

Ben P.