简体   繁体   中英

lateinit property binding has not been initialized

I know that there are similar questions to this, but I just cant find something that is similar, I've been studying new things to learn, and while converting kotlin synthetics to viewvbinding mode, I've encountered this error

kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized at com.codepalace.chatbot.ui.MessagingAdapter.onBindViewHolder(MessagingAdapter.kt:60) at com.codepalace.chatbot.ui.MessagingAdapter.onBindViewHolder(MessagingAdapter.kt:17)

It says that I have to initialize the binding, but I dont know where to put it.

This is the code.

class MessagingAdapter: RecyclerView.Adapter<MessagingAdapter.MessageViewHolder>() {

var messagesList = mutableListOf<Message>()
private lateinit var binding: MessageItemBinding


inner class MessageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    init {
        itemView.setOnClickListener {

            //Remove message on the item clicked
            messagesList.removeAt(adapterPosition)
            notifyItemRemoved(adapterPosition)
        }
    }
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageViewHolder {

    return MessageViewHolder(
        LayoutInflater.from(parent.context).inflate(R.layout.message_item, parent, false)
    )
}

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

@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: MessageViewHolder, position: Int) {
    val currentMessage = messagesList[position]


    when (currentMessage.id) {
        SEND_ID -> {
            holder.itemView.findViewById<View>(R.id.tv_message).apply {
                binding.tvMessage.text = currentMessage.message
                visibility = View.VISIBLE
            }
            holder.itemView.findViewById<View>(R.id.tv_bot_message).visibility = View.GONE
        }
        RECEIVE_ID -> {
            holder.itemView.findViewById<View>(R.id.tv_bot_message).apply {
                binding.tvBotMessage.text = currentMessage.message
                visibility = View.VISIBLE
            }
            holder.itemView.findViewById<View>(R.id.tv_message).visibility = View.GONE
        }
    }
}

fun insertMessage(message: Message) {
    this.messagesList.add(message)
    notifyItemInserted(messagesList.size)
}

private lateinit var binding: MessageItemBinding

You didn't initialize the binding object, as you defined it as lateinit, then you should define it.

This is typically in onCreateViewHolder

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackedActivityHolder {
    val inflater = LayoutInflater.from(parent.context)
    binding = DataBindingUtil.inflate<MessageItemBinding>(inflater, R.layout.message_item, parent, false)
    return MessageViewHolder(
        binding
    )
}

UPDATE

You need to accept MessageItemBinding type instead of View in the MessageViewHolder constructor, and use itemView.root to get the root view of the list item.

inner class MessageViewHolder(itemView: MessageItemBinding) : RecyclerView.ViewHolder(itemView.root) {
    init {
        itemView.root.setOnClickListener {

            //Remove message on the item clicked
            messagesList.removeAt(adapterPosition)
            notifyItemRemoved(adapterPosition)
        }
    }
}

The reason why You get this error is that MessageItemBinding should not be in Adapter but in ViewHolder class. You can make RecyclerView like this:

object MessageDiffCallback : DiffUtil.ItemCallback<Message>()
{
    override fun areItemsTheSame(
        oldItem: Message,
        newItem: Message
    ): Boolean =
            oldItem.id == newItem.id

    override fun areContentsTheSame(
        oldItem: Message,
        newItem: Message
    ): Boolean =
            oldItem == newItem
}

I assume that Message is a data class. You have to create this MessageDiffCallback and override these 2 methods in a similar way that I did it.

Now Create ViewHolder class:

class MessageViewHolder private constructor(
    private val binding: MessageItemBinding
) : RecyclerView.ViewHolder(binding.root)
{
    companion object
    {
        fun create(parent: ViewGroup): MessageViewHolder 
        {
            val layoutInflater = LayoutInflater.from(parent.context)
            val binding = MessageItemBinding.inflate(layoutInflater, parent, false)
            return MessageViewHolder(
                binding
            )
        }
    }

    fun bind(
        message: Messaage
    )
    {
        // Here You can do everything. 
        // You pass the message and based on this You can set up a view and use binding
    }
}

And now Adapter class

class MessageAdapter : ListAdapter<Message, MessageViewHolder>(MessageDiffCallback)
{
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageViewHolder =
            MessageViewHolder.create(parent)


    override fun onBindViewHolder(holder: MessageViewHolder, position: Int) =
            holder.bind(
                message = getItem(position),
            )
}

Now You should have a working adapter. To put data in it use messageAdapter.submitList(messages) in Your Fragment/Activity


Maybe not the best answer because it changes Your code and uses a little different logic but it should work better. You can check google sample code here or take codelab here . It is free after signing up

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM