'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 |