滚动时的回收范围弄乱视图 - 带有图片

发布于 2025-01-24 12:22:28 字数 8176 浏览 0 评论 0 原文

我正在使用聊天应用程序。我有聊天活动,两个用户可以在其中发送诸如WhatsApp之类的消息,但是我有问题。

就像您可以在图片中看到的那样( https://ibb.co/3cyyx01 ),视图在滚动时,我想我知道为什么。

研究这些帖子之后: recyClerView在滚动时会弄乱, =“ https://stackoverflow.com/questions/29702357/android-recyclerview-content-content-messed-messed-up-after-scrolling/34217561#34217561”

OnBindViewHolder ,因为我在某些视图上使用可见性选项(view.gone and view.ible.ible),我认为这些视图正在与此视图相关。可见性错误。

此外,我在 holder.setisrecyclable(false) in onBindViewHolder 中使用了,以便检查是否是导致问题的回收部分,当我使用问题时,它可以很好地工作。

这是循环系统适配器:

    private const val SEND_LAYOUT = 0
private const val RECEIVED_LAYOUT = 1

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

    private lateinit var receiverUserPic: String
    private lateinit var messageList: List<Message>
    private lateinit var currentUserPic: String
    private lateinit var currentUserUID: String
    private lateinit var targetUID: String

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {

        val viewHolder: RecyclerView.ViewHolder
        val view: View

        viewHolder = if (viewType == SEND_LAYOUT) {

            view = LayoutInflater.from(parent.context)
                .inflate(R.layout.sent_message_row, parent, false)
            SentViewHolder(view)

        } else {

            view = LayoutInflater.from(parent.context)
                .inflate(R.layout.recieved_message_row, parent, false)
            ReceivedViewHolder(view)

        }

        return viewHolder

    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {

        //holder.setIsRecyclable(false)
        val currentMessage = messageList[position]

        if (holder.itemViewType == SEND_LAYOUT) {

            holder as SentViewHolder
            holder.bindSentRow(currentMessage)

        } else {

            holder as ReceivedViewHolder
            holder.bindReceivedRow(currentMessage)

        }

    }

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

    override fun getItemViewType(position: Int): Int {

        val currentMessage = messageList[position]

        return if (FirebaseAuth.getInstance().currentUser?.uid.equals(currentMessage.sender))
            SEND_LAYOUT
        else
            RECEIVED_LAYOUT

    }

    inner class SentViewHolder(private val itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun bindSentRow(message: Message) {

            val sentMessageTextView =
                itemView.findViewById<TextView>(R.id.sentMessage)
            val sentImage = itemView.findViewById<ImageView>(R.id.sentImage)
            val profileImage =
                itemView.findViewById<ImageView>(R.id.sentMessageProfilePicture)
            val sentIsSeenImageTextView =
                itemView.findViewById<TextView>(R.id.sentIsSeenImageTextView)
            val sentIsSeenTextView =
                itemView.findViewById<TextView>(R.id.sentIsSeenTextView)


            profileImage.setOnClickListener {

                val visitProfileIntent = Intent(it.context, VisitProfileActivity::class.java)
                visitProfileIntent.putExtra("targetUID", currentUserUID)
                it.context.startActivity(visitProfileIntent)

            }

            if (message.message.equals("Sent you an image") && !message.url.equals("")) {

                sentMessageTextView.visibility = View.GONE
                sentIsSeenImageTextView.visibility = View.VISIBLE
                sentIsSeenTextView.visibility = View.GONE
                sentImage.visibility = View.VISIBLE


                Glide.with(itemView.rootView).load(message.url)
                    .override(SIZE_ORIGINAL, SIZE_ORIGINAL)
                    .error(R.drawable.error_icon)
                    .placeholder(R.drawable.loading_icon)
                    .listener(object : RequestListener<Drawable?> {
                        override fun onLoadFailed(
                            @Nullable e: GlideException?,
                            model: Any,
                            target: Target<Drawable?>,
                            isFirstResource: Boolean
                        ): Boolean {
                            return false
                        }

                        override fun onResourceReady(
                            resource: Drawable?,
                            model: Any?,
                            target: Target<Drawable?>?,
                            dataSource: DataSource?,
                            isFirstResource: Boolean
                        ): Boolean {
                            return false
                        }
                    }).into(sentImage)



                if (adapterPosition == messageList.size - 1) {
                    sentIsSeenImageTextView.visibility = View.VISIBLE
                    sentIsSeenTextView.visibility = View.GONE

                    if (message.seen == true) {
                        sentIsSeenImageTextView.text = "Seen"
                    } else {
                        sentIsSeenImageTextView.text = "Sent"
                    }

                } else {
                    sentIsSeenImageTextView.visibility = View.GONE
                }

                

        } else {

            sentMessageTextView.visibility = View.VISIBLE
            sentMessageTextView.text = message.message
            sentIsSeenImageTextView.visibility = View.GONE

            if (adapterPosition == messageList.size - 1) {
                sentIsSeenTextView.visibility = View.VISIBLE
                sentIsSeenImageTextView.visibility = View.GONE

                if (message.seen == true) {
                    sentIsSeenTextView.text = "Seen"
                } else {
                    sentIsSeenTextView.text = "Sent"
                }

            }

        }


        Glide.with(itemView.rootView).load(currentUserPic).into(profileImage)


    }

}


inner class ReceivedViewHolder(private val itemView: View) : RecyclerView.ViewHolder(itemView) {

    fun bindReceivedRow(message: Message) {

        val receiveMessageTextView =
            itemView.findViewById<TextView>(R.id.receivedMessage)
        val receiveImage =
            itemView.findViewById<ImageView>(R.id.receivedImage)
        val receiveProfileImage =
            itemView.findViewById<ImageView>(R.id.receivedMessageProfileImage)

        receiveProfileImage.setOnClickListener {

            val visitProfileIntent = Intent(it.context, VisitProfileActivity::class.java)
            visitProfileIntent.putExtra("targetUID", targetUID)
            it.context.startActivity(visitProfileIntent)

        }

        if (message.message.equals("Sent you an image") && !message.url.equals("")) {

            receiveMessageTextView.visibility = View.GONE
            receiveImage.visibility = View.VISIBLE

            Glide.with(itemView.rootView).load(message.url).into(receiveImage)


        } else {

            receiveMessageTextView.visibility = View.VISIBLE
            receiveMessageTextView.text = message.message

        }

        Glide.with(itemView.rootView).load(receiverUserPic).into(receiveProfileImage)


    }

}

fun getMessageList(): List<Message> {
    return messageList
}

fun setMessagesList(
    newList: List<Message>,
    userProfilePic: String,
    userProfilePic1: String,
    currentUID: String,
    receiverUID: String
) {
    messageList = newList
    currentUserPic = userProfilePic
    receiverUserPic = userProfilePic1
    currentUserUID = currentUID
    targetUID = receiverUID
    notifyDataSetChanged()
}


}

pastebin链接: https://pastebin.com/ri5puadk

谢谢!

I am working on a chat app. I have the chat activity where the two users can send messages like WhatsApp, but I have a problem.

Like you can see in the picture (https://ibb.co/3cyYX01), the views are messing up when scrolling, and I think I know why.

After looking into those posts:
RecyclerView messes up when scrolling ,
Android: RecyclerView content messed up after scrolling

I assume the problem may be in the recycler view adapter in the function onBindViewHolder, because I am using the visibility option on some views(VIEW.GONE and VIEW.VISIBLE) and I think that these views are getting redrawn with wrong visibility.

In addition, I used holder.setIsRecyclable(false) in onBindViewHolder in order to check if it's the recycling part that cause the problem and when I used it, it worked perfectly.

This is the RecyclerView Adapter:

    private const val SEND_LAYOUT = 0
private const val RECEIVED_LAYOUT = 1

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

    private lateinit var receiverUserPic: String
    private lateinit var messageList: List<Message>
    private lateinit var currentUserPic: String
    private lateinit var currentUserUID: String
    private lateinit var targetUID: String

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {

        val viewHolder: RecyclerView.ViewHolder
        val view: View

        viewHolder = if (viewType == SEND_LAYOUT) {

            view = LayoutInflater.from(parent.context)
                .inflate(R.layout.sent_message_row, parent, false)
            SentViewHolder(view)

        } else {

            view = LayoutInflater.from(parent.context)
                .inflate(R.layout.recieved_message_row, parent, false)
            ReceivedViewHolder(view)

        }

        return viewHolder

    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {

        //holder.setIsRecyclable(false)
        val currentMessage = messageList[position]

        if (holder.itemViewType == SEND_LAYOUT) {

            holder as SentViewHolder
            holder.bindSentRow(currentMessage)

        } else {

            holder as ReceivedViewHolder
            holder.bindReceivedRow(currentMessage)

        }

    }

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

    override fun getItemViewType(position: Int): Int {

        val currentMessage = messageList[position]

        return if (FirebaseAuth.getInstance().currentUser?.uid.equals(currentMessage.sender))
            SEND_LAYOUT
        else
            RECEIVED_LAYOUT

    }

    inner class SentViewHolder(private val itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun bindSentRow(message: Message) {

            val sentMessageTextView =
                itemView.findViewById<TextView>(R.id.sentMessage)
            val sentImage = itemView.findViewById<ImageView>(R.id.sentImage)
            val profileImage =
                itemView.findViewById<ImageView>(R.id.sentMessageProfilePicture)
            val sentIsSeenImageTextView =
                itemView.findViewById<TextView>(R.id.sentIsSeenImageTextView)
            val sentIsSeenTextView =
                itemView.findViewById<TextView>(R.id.sentIsSeenTextView)


            profileImage.setOnClickListener {

                val visitProfileIntent = Intent(it.context, VisitProfileActivity::class.java)
                visitProfileIntent.putExtra("targetUID", currentUserUID)
                it.context.startActivity(visitProfileIntent)

            }

            if (message.message.equals("Sent you an image") && !message.url.equals("")) {

                sentMessageTextView.visibility = View.GONE
                sentIsSeenImageTextView.visibility = View.VISIBLE
                sentIsSeenTextView.visibility = View.GONE
                sentImage.visibility = View.VISIBLE


                Glide.with(itemView.rootView).load(message.url)
                    .override(SIZE_ORIGINAL, SIZE_ORIGINAL)
                    .error(R.drawable.error_icon)
                    .placeholder(R.drawable.loading_icon)
                    .listener(object : RequestListener<Drawable?> {
                        override fun onLoadFailed(
                            @Nullable e: GlideException?,
                            model: Any,
                            target: Target<Drawable?>,
                            isFirstResource: Boolean
                        ): Boolean {
                            return false
                        }

                        override fun onResourceReady(
                            resource: Drawable?,
                            model: Any?,
                            target: Target<Drawable?>?,
                            dataSource: DataSource?,
                            isFirstResource: Boolean
                        ): Boolean {
                            return false
                        }
                    }).into(sentImage)



                if (adapterPosition == messageList.size - 1) {
                    sentIsSeenImageTextView.visibility = View.VISIBLE
                    sentIsSeenTextView.visibility = View.GONE

                    if (message.seen == true) {
                        sentIsSeenImageTextView.text = "Seen"
                    } else {
                        sentIsSeenImageTextView.text = "Sent"
                    }

                } else {
                    sentIsSeenImageTextView.visibility = View.GONE
                }

                

        } else {

            sentMessageTextView.visibility = View.VISIBLE
            sentMessageTextView.text = message.message
            sentIsSeenImageTextView.visibility = View.GONE

            if (adapterPosition == messageList.size - 1) {
                sentIsSeenTextView.visibility = View.VISIBLE
                sentIsSeenImageTextView.visibility = View.GONE

                if (message.seen == true) {
                    sentIsSeenTextView.text = "Seen"
                } else {
                    sentIsSeenTextView.text = "Sent"
                }

            }

        }


        Glide.with(itemView.rootView).load(currentUserPic).into(profileImage)


    }

}


inner class ReceivedViewHolder(private val itemView: View) : RecyclerView.ViewHolder(itemView) {

    fun bindReceivedRow(message: Message) {

        val receiveMessageTextView =
            itemView.findViewById<TextView>(R.id.receivedMessage)
        val receiveImage =
            itemView.findViewById<ImageView>(R.id.receivedImage)
        val receiveProfileImage =
            itemView.findViewById<ImageView>(R.id.receivedMessageProfileImage)

        receiveProfileImage.setOnClickListener {

            val visitProfileIntent = Intent(it.context, VisitProfileActivity::class.java)
            visitProfileIntent.putExtra("targetUID", targetUID)
            it.context.startActivity(visitProfileIntent)

        }

        if (message.message.equals("Sent you an image") && !message.url.equals("")) {

            receiveMessageTextView.visibility = View.GONE
            receiveImage.visibility = View.VISIBLE

            Glide.with(itemView.rootView).load(message.url).into(receiveImage)


        } else {

            receiveMessageTextView.visibility = View.VISIBLE
            receiveMessageTextView.text = message.message

        }

        Glide.with(itemView.rootView).load(receiverUserPic).into(receiveProfileImage)


    }

}

fun getMessageList(): List<Message> {
    return messageList
}

fun setMessagesList(
    newList: List<Message>,
    userProfilePic: String,
    userProfilePic1: String,
    currentUID: String,
    receiverUID: String
) {
    messageList = newList
    currentUserPic = userProfilePic
    receiverUserPic = userProfilePic1
    currentUserUID = currentUID
    targetUID = receiverUID
    notifyDataSetChanged()
}


}

Pastebin Link:
https://pastebin.com/Ri5pUAdk

Thank you !

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

云醉月微眠 2025-01-31 12:22:28

recyLeview 的工作是基于 recycles 的视图以显示列表。当您滚动时,视图从屏幕上出来的不会被破坏,但再次被重复使用以显示新列表项目。因此,如果您更改可见性或视图的任何其他属性,并且不要在 onBindViewHolder 中再次重置它,则它将显示所有属性被回收了。

fun bind(data: Data) {
    val textView = itemView.findViewById<TextView>(R.id.tvText)
    if(data.text.isEmpty()) {
        textView.visibility = View.GONE
    }
}

在上面的方法中,我们隐藏 textView 当文本为时,我们没有在 else> else 条件中设置任何内容。因此,当视图 为此,我们将可见性被回收时,他们永远不会显示 textView ,因为它正在重复使用查看。为了解决这个问题,我们必须为 true false 条件设置视图的属性。

fun bind(data: Data) {
    val textView = itemView.findViewById<TextView>(R.id.tvText)
    if(data.text.isEmpty()) {
        textView.visibility = View.GONE
    } else {
        textView.visibility = View.VISIBLE
    }
}

sentViewHolder readeviewholder 中,您将 ImageView 的可见性设置为可见>可见

sentImage.visibility = View.VISIBLE

receiveImage.visibility = View.VISIBLE

但您永远不会将其设置为走了

的其他条件下(Message.Message.Equals(“发送给您的映像”) 的代码> ImageView to gone 。也为所有其他视图做同样的事情,因此您不会得到意外的UI。

Working of the recyleView is based on that, it recycles views to show a list. When you scroll, views which go out of the screen, are not destroyed but are reused again to show the new list item. So, if you change visibility or any other property of a view and don't reset it again inside onBindViewHolder, then it would show all the properties which were set earlier before it got recycled.

fun bind(data: Data) {
    val textView = itemView.findViewById<TextView>(R.id.tvText)
    if(data.text.isEmpty()) {
        textView.visibility = View.GONE
    }
}

In the above method, we are hiding textView when text is empty, but we are not setting anything in the else condition. So when views, for which we've set visibility to gone would be recycled, they'd never show the textView as it is reusing the view. To deal with this, we've to set the properties of the views for true and false conditions each.

fun bind(data: Data) {
    val textView = itemView.findViewById<TextView>(R.id.tvText)
    if(data.text.isEmpty()) {
        textView.visibility = View.GONE
    } else {
        textView.visibility = View.VISIBLE
    }
}

In SentViewHolder and ReceivedViewHolder, you are setting the visibility of the ImageView to visible

sentImage.visibility = View.VISIBLE

receiveImage.visibility = View.VISIBLE

but you are never setting it to gone.

In the else condition of (message.message.equals("Sent you an image") && !message.url.equals("")), set the visibility of ImageView to GONE. Do the same for all other views too, so you don't get an unexpected UI.

抠脚大汉 2025-01-31 12:22:28

因为视图持有人被回收并重复使用,这会导致您的观点处于错误状态。

sendviewholder 类中,您仅通过设置可见的可见性来处理 Sindimage 的状态。因此,您还需要将其可见性设置为进入其他块。

或者,您可以首先重置视图可见度,然后如下所示。

fun bindSendRow(message: Message) {
    sentMessageTextView.visibility = View.GONE
    sentImage.visibilty = View.GONE
    if(shouldShowImage){
        sentImage.visibility = View.VISIBLE
    } else if(shouldShowText){
        sentMessageTextView.visibility = View.VISIBLE
    }
}

Because the view holder is being recycled and reused that cause your views to be in the wrong state.

In SendViewHolder class you only handle the state of sentImage in if block by setting the visibility to visible. Therefore you also need to set its visibility to gone in else block.

Or you can reset the view visibility first then show it like below.

fun bindSendRow(message: Message) {
    sentMessageTextView.visibility = View.GONE
    sentImage.visibilty = View.GONE
    if(shouldShowImage){
        sentImage.visibility = View.VISIBLE
    } else if(shouldShowText){
        sentMessageTextView.visibility = View.VISIBLE
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文