user3448282
user3448282

Reputation: 2699

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?

Upvotes: 7

Views: 2842

Answers (1)

Ben P.
Ben P.

Reputation: 54214

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.

Upvotes: 8

Related Questions