Home > Software design >  Android onBindViewHolder holder
Android onBindViewHolder holder

Time:10-25

I'm trying to learn how to code for android devices. I'm following a tutorial video to make a simple todo app. The code I have written thus far (that is relevant to this issue) is an xml file called item_todo.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:paddingStart="8dp"
    android:paddingEnd="8dp">

    <TextView
        android:id="@ id/tvTodoTitle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Example"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@ id/cbDone"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <CheckBox
        android:id="@ id/cbDone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

and the todoadapter.kt file as follows:

package com.example.todo

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup

import androidx.recyclerview.widget.RecyclerView


class TodoAdapter (
    private val todos: MutableList<Todo>
) : RecyclerView.Adapter<TodoAdapter.TodoViewHolder>(){

    class TodoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
        return TodoViewHolder(
            LayoutInflater.from(parent.context).inflate(
                R.layout.item_todo,
                parent,
                false
            )
        )
    }

    override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
        val curTodo = todos[position]
        holder.itemView.apply {
            tvTodoTitle.text = curTodo.title
        }
    }

    override fun getItemCount(): Int {
        return todo.size
    }


}

the only issue I've had thus far is that when I call:

holder.itemView.apply {
            tvTodoTitle.text = curTodo.title
        }

the holder doesn't seem to find the xml file info. Is the video I'm watching just so old the command is depreciated, or have I done something wrong?

CodePudding user response:

This is the same issue as in this question, except that the answers there don't talk about how to get view references in a ViewHolder (they only mention Fragment or Activity). So you can read the answers there for the explanation of why this isn't working any more according to the outdated tutorial instructions.

For working with a ViewHolder, here are two ways to do it without synthetic references.

Without view binding

Add properties to your view holder class for each View you want to be able to work with, and use findViewById on the itemView to initialize them:

class TodoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val tvTodoTitle: TextView = itemView.findViewById(R.id.tvTodoTitle)
}

Then in onBindViewHolder, you can use it like this (notice we don't work with itemView):

    override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
        val curTodo = todos[position]
        holder.apply {
            tvTodoTitle.text = curTodo.title
        }
    }

With view binding

This is preferable, especially if you have more than one view to work with in your layout, because you don't have to manually create properties for each of them and hook them up correctly to the right view IDs.

Set up view binding in your project according to the directions. Change your ViewHolder class to have a binding property like this:

class TodoViewHolder(val binding: ItemTodoBinding): RecyclerView.ViewHolder(binding.root)

And change onCreateViewHolder() to use inflate the binding like this:

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
        return TodoViewHolder(
            ItemTodoBinding.inflate(
                LayoutInflater.from(parent.context),
                R.layout.item_todo,
                parent,
                false
            )
        )
    }

And then you can use it in onBindViewHolder like this:

    override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
        val curTodo = todos[position]
        holder.binding.apply {
            tvTodoTitle.text = curTodo.title
        }
    }
  • Related