'How to implement a custom view in a RecyclerView using a BindingAdapter

Can anyone help me to use a custom view (AAChartCore-Kotlin, ChartView) in a RecyclerView (displaying some speedcubing sessions' data) using a binding adapter?

I have tried adding the ChartView to the RecyclerView items list and implementing a BindingAdapter for it where I call aa_drawChartWithChartModel(...), which should display the data in the chart, passing the item's chartModel to it, but I have no idea if that's right.

For now, it seems like the chart is loaded in the view but it is invisible.

list_item_session.xml

<!-- ... -->

</LinearLayout>

    <!-- ... -->

    </HorizontalScrollView>

        <!-- ... -->

        </androidx.constraintlayout.widget.ConstraintLayout>

            <!-- ... -->

            <com.github.aachartmodel.aainfographics.aachartcreator.AAChartView

                android:id="@+id/sessionChart"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="@dimen/margin_big"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@+id/sessionEndDateTime"
                app:layout_constraintTop_toTopOf="parent"
                app:sessionChartView="@{session}">

            </com.github.aachartmodel.aainfographics.aachartcreator.AAChartView>

        </androidx.constraintlayout.widget.ConstraintLayout>

    </HorizontalScrollView>


</LinearLayout>

SessionAdapter.kt

class SessionAdapter(private val clickListener: SessionListener) : ListAdapter<DataItem, RecyclerView.ViewHolder>(SessionsDiffCallback()) {

    /* PROPERTIES */

    // Coroutines
    private val adapterScope = CoroutineScope(Dispatchers.Default)


    /* COMPANION OBJECT */

    companion object
    {
        private const val ITEM_VIEW_TYPE_PADDING = 0
        private const val ITEM_VIEW_TYPE_SESSION = 1
    }


    /* ADAPTER METHODS */

    fun submitListWithPadding(list: List<Session>?) {
        adapterScope.launch {
            val items = when(list) {
                null -> listOf(DataItem.PaddingItem)
                else -> list.map { DataItem.SessionItem(it) } + listOf(DataItem.PaddingItem)
            }
            withContext(Dispatchers.Main) {
                submitList(items)
            }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return when(viewType) {
            ITEM_VIEW_TYPE_PADDING -> TextViewHolder.from(parent)
            ITEM_VIEW_TYPE_SESSION -> ViewHolder.from(parent)

            else -> throw ClassCastException("Unknown viewType $viewType")
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when(holder) {
            is ViewHolder -> {
                val sessionItem = getItem(position) as DataItem.SessionItem
                holder.bind(clickListener, sessionItem.session)
            }
        }
    }

    override fun getItemViewType(position: Int): Int {
        return when(getItem(position)) {
            is DataItem.PaddingItem -> ITEM_VIEW_TYPE_PADDING
            is DataItem.SessionItem -> ITEM_VIEW_TYPE_SESSION
        }
    }


    /* VIEW HOLDER FOR SESSION ITEMS */

    class ViewHolder private constructor(val binding: ListItemSessionBinding) : RecyclerView.ViewHolder(binding.root)
    {
        fun bind(clickListener: SessionListener, item: Session) {
            binding.session = item
            binding.clickListener = clickListener
            binding.executePendingBindings()
        }

        companion object {
            fun from(parent: ViewGroup): ViewHolder {
                val layoutInflater = LayoutInflater.from(parent.context)
                val binding = ListItemSessionBinding.inflate(layoutInflater, parent, false)

                return ViewHolder(binding)
            }
        }
    }


    /* TEXT VIEW HOLDER FOR PADDING ITEM */

    class TextViewHolder(view: View) : RecyclerView.ViewHolder(view)
    {
        companion object
        {
            fun from(parent: ViewGroup): TextViewHolder {
                val layoutInflater = LayoutInflater.from(parent.context)
                val view = layoutInflater.inflate(R.layout.list_item_padding, parent, false)

                return TextViewHolder(view)
            }
        }
    }
}

BindingAdapters.kt

/* ... */

@BindingAdapter("sessionChartView")
fun AAChartView.setSessionChartView(item: Session?) {
    item?.let {
        aa_drawChartWithChartModel(it.chartModel)
    }
}

DataItem.kt

sealed class DataItem
{
    abstract val id: Long

    data class SessionItem(val session: Session) : DataItem()
    {
        override val id = session.id
    }

    object PaddingItem : DataItem()
    {
        override val id = Long.MIN_VALUE
    }
}

SessionsDiffCallback.kt

class SessionsDiffCallback : DiffUtil.ItemCallback<DataItem>()
{
    override fun areItemsTheSame(oldItem: DataItem, newItem: DataItem): Boolean {
        return oldItem.id == newItem.id
    }

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


Solution 1:[1]

You should set view as argument and call it's methods in your BindingAdapter.

@BindingAdapter("sessionChartView")
@JvmStatic
fun setSessionChartView(view: AAChartView, item: Session?) {
    item?.let {
        view.aa_drawChartWithChartModel(it.chartModel)
    }
}

Solution 2:[2]

Make sure to assign your custom view in OnCreateVeiwHolder

View view = layoutInflater.inflate(R.layout.my_custom_view,parent,false);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Sergey Melnik
Solution 2 BlackCoffee