You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RecyclerView onCreateViewHolder中bindingAdapterPosition返回-1

RecyclerView中bindingAdapterPosition返回-1问题排查

问题现象

实现RecyclerView评论列表功能时,在onCreateViewHolder方法内调用holder.bindingAdapterPosition始终返回-1,无法获取当前条目位置。日志校验显示AsyncListDiffer持有的列表数据已正常加载,但位置取值持续失败。

相关实现代码

Adapter与ViewHolder核心代码

class CommentAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    private var likeClick: LikeClick? = null
    private var disLikeClick: DisLikeClick? = null

    inner class CommentHolder(view: View) : RecyclerView.ViewHolder(view) {
        val nicknameTextView: TextView = itemView.findViewById(R.id.nickname_text_view)
        val commentTextView: TextView = itemView.findViewById(R.id.comment_text_view)
        val likeCount: TextView = itemView.findViewById(R.id.like_count_text_view)
        val disLikeCount: TextView = itemView.findViewById(R.id.dislike_count_text_view)
        val likeBtn: ImageButton = itemView.findViewById(R.id.like_image_btn)
        val disLikeBtn: ImageButton = itemView.findViewById(R.id.disLike_Image_btn)
    }

    fun setLikeClickListener(clickListener: LikeClick) {
        likeClick = clickListener
    }
    fun setDisLikeClickListener(clickListener: DisLikeClick) {
        disLikeClick = clickListener
    }

    private val diffCallback = object : DiffUtil.ItemCallback<Comment>() {
        override fun areItemsTheSame(oldItem: Comment, newItem: Comment): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(oldItem: Comment, newItem: Comment): Boolean {
            return oldItem.id == newItem.id
        }
    }

    private val diff = AsyncListDiffer(this, diffCallback)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item_comment, parent, false)
        val holder: RecyclerView.ViewHolder = CommentHolder(view)

        //set on click listener for like and dislike
        Log.d("TESTLOG",diff.currentList.toString())
        // 问题代码位置
        val commentItem = diff.currentList[holder.bindingAdapterPosition]
        val likeBtn=view.findViewById<ImageButton>(R.id.like_image_btn)
        val dislikeBtn=view.findViewById<ImageButton>(R.id.disLike_Image_btn)

        likeBtn.setOnClickListener {
            likeClick?.onLikeClick(commentItem.id,likeBtn)
        }

        dislikeBtn.setOnClickListener {
            disLikeClick?.onDisLikeClick(commentItem.id,dislikeBtn)
        }

        return holder
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val commentItem = diff.currentList[position]
        val videoHolder = holder as CommentHolder
        videoHolder.apply {
            commentTextView.text = commentItem.comment
            nicknameTextView.text = commentItem.nickname
            likeCount.text = commentItem.commentlike.toString()
            disLikeCount.text = commentItem.dislike.toString()

            //check if user already liked/disliked this comment and change icon based on that
            Utils.likes.forEach {
                if(it == commentItem.id){
                    likeBtn.isSelected=true
                }
            }

            Utils.disLikes.forEach {
                if(it==commentItem.id){
                    disLikeBtn.isSelected=true
                }
            }
        }
    }

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

    fun submitList(list: List<Comment>) {
        diff.submitList(list)
    }

    interface LikeClick {
        fun onLikeClick(commentId: Int, likeBtn:ImageButton)
    }
    interface DisLikeClick {
        fun onDisLikeClick(commentId: Int, disLikeBtn:ImageButton)
    }
}

日志打印结果

onCreateViewHolder中打印的列表数据正常:

D/TESTLOG: [Comment(comment=firstcomment
    , commentlike=0, createdAt=2022-07-06T04:35:43.000Z, dislike=0, id=4, nickname=Alpac, videoId=170c3d0c-331f-4da9-974b-39703940f819)]

Adapter绑定逻辑

页面层提交数据、绑定Adapter的代码如下:

ChikiCommentsFetcher().fetchCommentsOfAVideo(videoId).observe(viewLifecycleOwner){
    commentsRecyclerView.apply {
        commentAdapter.submitList(it)
        commentAdapter.setLikeClickListener(this@CommentsFragment)
        commentAdapter.setDisLikeClickListener(this@CommentsFragment)
        adapter=commentAdapter
    }
}

问题根因

bindingAdapterPosition返回-1是API设计的正常表现:

  • 该方法的返回值只有当ViewHolder完成绑定、被附加到RecyclerView并分配了确定的列表位置时才是有效索引,未绑定状态下固定返回RecyclerView.NO_POSITION,也就是常量值-1
  • onCreateViewHolder的执行时机是RecyclerView需要创建新的ViewHolder实例时,此时刚实例化的ViewHolder还未和任何数据位置绑定,也没有附加到RecyclerView布局中,自然拿不到有效位置
  • 原有写法还存在隐藏bug:在onCreateViewHolder中提前获取commentItem并存在点击监听的闭包里,后续ViewHolder滑动复用时,点击事件会一直持有创建时的旧数据,导致点赞点踩传错评论ID
  • 额外问题:DiffUtil的areContentsTheSame实现仅对比了评论ID,没有对比点赞数、评论内容等实际展示字段,会导致内容变化时列表不会自动刷新UI

修复方案

  • 不要在onCreateViewHolder中尝试获取位置、读取对应条目数据,这里仅做视图初始化、点击监听器设置即可
  • 点击事件触发时,实时调用holder.bindingAdapterPosition获取当前位置,同时校验位置有效性,避免NO_POSITION导致的数组越界崩溃
  • 所有和数据相关的绑定逻辑,全部放在onBindViewHolder中执行
  • 补全areContentsTheSame的字段对比逻辑

修复后的onCreateViewHolder代码参考:

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item_comment, parent, false)
    val holder = CommentHolder(view)
    val likeBtn=view.findViewById<ImageButton>(R.id.like_image_btn)
    val dislikeBtn=view.findViewById<ImageButton>(R.id.disLike_Image_btn)

    likeBtn.setOnClickListener {
        // 点击时实时获取最新位置
        val currentPos = holder.bindingAdapterPosition
        if (currentPos != RecyclerView.NO_POSITION) {
            val commentItem = diff.currentList[currentPos]
            likeClick?.onLikeClick(commentItem.id,likeBtn)
        }
    }

    dislikeBtn.setOnClickListener {
        val currentPos = holder.bindingAdapterPosition
        if (currentPos != RecyclerView.NO_POSITION) {
            val commentItem = diff.currentList[currentPos]
            disLikeClick?.onDisLikeClick(commentItem.id,dislikeBtn)
        }
    }

    return holder
}

修复后的diffCallback参考:

private val diffCallback = object : DiffUtil.ItemCallback<Comment>() {
    override fun areItemsTheSame(oldItem: Comment, newItem: Comment): Boolean {
        return oldItem.id == newItem.id
    }

    override fun areContentsTheSame(oldItem: Comment, newItem: Comment): Boolean {
        // 对比所有会影响UI展示的字段
        return oldItem.id == newItem.id 
                && oldItem.comment == newItem.comment
                && oldItem.nickname == newItem.nickname
                && oldItem.commentlike == newItem.commentlike
                && oldItem.dislike == newItem.dislike
    }
}

内容的提问来源于stack exchange,提问作者Abed Sako

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 13:15:41