RecyclerView的ChatAdapter触发IndexOutOfBoundsException问题求助
解决RecyclerView聊天适配器键盘输入崩溃问题
我开发的聊天界面RecyclerView适配器ChatAdapter包含发送消息、接收消息、输入指示器三种视图类型。应用正常运行无异常,但打开键盘输入内容时崩溃,抛出java.lang.IndexOutOfBoundsException: Index 8 out of bounds for length 8错误。尝试修改getItemViewType和getItemCount逻辑及相关方案后仍未解决,原适配器代码如下:
class ChatAdapter(private val currentUserId: Int) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { private val messages = mutableListOf<Message>() private var isTypingIndicatorVisible = false fun updateMessages(newMessages: List<Message>) { messages.addAll(newMessages) notifyDataSetChanged() } private val MESSAGE_SENT = 1 private val MESSAGE_RECEIVED = 2 private val TYPING_INDICATOR = 3 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { MESSAGE_SENT -> { val binding = ItemMessageSentBinding.inflate(LayoutInflater.from(parent.context), parent, false) SentMessageViewHolder(binding) } MESSAGE_RECEIVED -> { val binding = ItemMessageReceivedBinding.inflate(LayoutInflater.from(parent.context), parent, false) ReceivedMessageViewHolder(binding) } TYPING_INDICATOR -> { val binding = ItemTypingIndicatorBinding.inflate(LayoutInflater.from(parent.context), parent, false) TypingIndicatorViewHolder(binding) } else -> throw IllegalArgumentException("Invalid view type") } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val message = messages[position] when (holder.itemViewType) { MESSAGE_SENT -> { (holder as SentMessageViewHolder).bind(message) } MESSAGE_RECEIVED -> { (holder as ReceivedMessageViewHolder).bind(message) } TYPING_INDICATOR -> { (holder as TypingIndicatorViewHolder).bind(message) } } } override fun getItemCount(): Int { return messages.size + if (isTypingIndicatorVisible) 1 else 0 } override fun getItemViewType(position: Int): Int { val message = messages[position] return if (isTypingIndicatorVisible && position == messages.size) { TYPING_INDICATOR } else if (message.senderEmail == currentUserId.toString()) { MESSAGE_SENT } else { MESSAGE_RECEIVED } } fun showTypingIndicator(isVisible: Boolean) { isTypingIndicatorVisible = isVisible notifyDataSetChanged() } inner class TypingIndicatorViewHolder(private val binding: ItemTypingIndicatorBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(message: Message) { binding.tvTyping.visibility = View.VISIBLE } } inner class SentMessageViewHolder(private val binding: ItemMessageSentBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(message: Message) { binding.tvMsgSent.text = message.content val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) val date = dateFormat.parse(message.date) binding.tvDateTime.text = SimpleDateFormat("hh:mm a", Locale.getDefault()).format(date) } } inner class ReceivedMessageViewHolder(private val binding: ItemMessageReceivedBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(message: Message) { binding.tvMsgReceived.text = message.content val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) val date = dateFormat.parse(message.date) binding.tvDateTime.text = SimpleDateFormat("hh:mm a", Locale.getDefault()).format(date) } } }
问题根源
崩溃的核心是输入指示器显示时,适配器的位置处理逻辑越界:
getItemCount返回messages.size + 1,但getItemViewType会先通过messages[position]获取消息,当position等于messages.size(输入指示器的位置)时,这个索引超出了messages的最大范围(列表索引从0到messages.size-1)。onBindViewHolder同样在所有位置都尝试获取messages[position],输入指示器位置会直接触发数组越界异常。- 输入指示器的
bind方法不需要Message参数,传递无效对象属于冗余逻辑。
修复方案
对三个关键方法进行修正:
1. 修正getItemViewType逻辑
先判断当前位置是否是输入指示器,再处理消息类型:
override fun getItemViewType(position: Int): Int { return if (isTypingIndicatorVisible && position == messages.size) { TYPING_INDICATOR } else { val message = messages[position] if (message.senderEmail == currentUserId.toString()) { MESSAGE_SENT } else { MESSAGE_RECEIVED } } }
2. 修正onBindViewHolder逻辑
仅在消息类型的位置访问messages,输入指示器位置跳过消息获取:
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder.itemViewType) { MESSAGE_SENT -> { (holder as SentMessageViewHolder).bind(messages[position]) } MESSAGE_RECEIVED -> { (holder as ReceivedMessageViewHolder).bind(messages[position]) } TYPING_INDICATOR -> { (holder as TypingIndicatorViewHolder).bind() } } }
3. 简化TypingIndicatorViewHolder的bind方法
移除无用的Message参数:
inner class TypingIndicatorViewHolder(private val binding: ItemTypingIndicatorBinding) : RecyclerView.ViewHolder(binding.root) { fun bind() { binding.tvTyping.visibility = View.VISIBLE } }
修复后完整代码
class ChatAdapter(private val currentUserId: Int) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { private val messages = mutableListOf<Message>() private var isTypingIndicatorVisible = false fun updateMessages(newMessages: List<Message>) { messages.addAll(newMessages) notifyDataSetChanged() } private val MESSAGE_SENT = 1 private val MESSAGE_RECEIVED = 2 private val TYPING_INDICATOR = 3 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { MESSAGE_SENT -> { val binding = ItemMessageSentBinding.inflate(LayoutInflater.from(parent.context), parent, false) SentMessageViewHolder(binding) } MESSAGE_RECEIVED -> { val binding = ItemMessageReceivedBinding.inflate(LayoutInflater.from(parent.context), parent, false) ReceivedMessageViewHolder(binding) } TYPING_INDICATOR -> { val binding = ItemTypingIndicatorBinding.inflate(LayoutInflater.from(parent.context), parent, false) TypingIndicatorViewHolder(binding) } else -> throw IllegalArgumentException("Invalid view type") } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder.itemViewType) { MESSAGE_SENT -> { (holder as SentMessageViewHolder).bind(messages[position]) } MESSAGE_RECEIVED -> { (holder as ReceivedMessageViewHolder).bind(messages[position]) } TYPING_INDICATOR -> { (holder as TypingIndicatorViewHolder).bind() } } } override fun getItemCount(): Int { return messages.size + if (isTypingIndicatorVisible) 1 else 0 } override fun getItemViewType(position: Int): Int { return if (isTypingIndicatorVisible && position == messages.size) { TYPING_INDICATOR } else { val message = messages[position] if (message.senderEmail == currentUserId.toString()) { MESSAGE_SENT } else { MESSAGE_RECEIVED } } } fun showTypingIndicator(isVisible: Boolean) { isTypingIndicatorVisible = isVisible notifyDataSetChanged() } inner class TypingIndicatorViewHolder(private val binding: ItemTypingIndicatorBinding) : RecyclerView.ViewHolder(binding.root) { fun bind() { binding.tvTyping.visibility = View.VISIBLE } } inner class SentMessageViewHolder(private val binding: ItemMessageSentBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(message: Message) { binding.tvMsgSent.text = message.content val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) val date = dateFormat.parse(message.date) binding.tvDateTime.text = SimpleDateFormat("hh:mm a", Locale.getDefault()).format(date) } } inner class ReceivedMessageViewHolder(private val binding: ItemMessageReceivedBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(message: Message) { binding.tvMsgReceived.text = message.content val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) val date = dateFormat.parse(message.date) binding.tvDateTime.text = SimpleDateFormat("hh:mm a", Locale.getDefault()).format(date) } } }
内容的提问来源于stack exchange,提问作者Sahar Batool
相关产品推荐
相关产品推荐

