RecyclerView 滚动时弄乱视图 - 带图片
RecyclerView Messing Up Views When Scrolling - With Picture
我正在开发一个聊天应用程序。我有聊天 activity,两个用户可以在其中发送 WhatsApp 之类的消息,但我遇到了问题。
如图所示 (https://ibb.co/3cyYX01),滚动时视图乱七八糟,我想我知道为什么了。
查看这些帖子后:
,
我认为问题可能出在函数 onBindViewHolder
中的回收器视图适配器中,因为我在某些视图(VIEW.GONE 和 VIEW.VISIBLE)上使用了可见性选项,并且我认为这些视图正在以错误的可见性重绘。
此外,我在onBindViewHolder
中使用了holder.setIsRecyclable(false)
,以检查是否是回收部分导致了问题,当我使用它时,它运行良好。
这是 RecyclerView 适配器:
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
谢谢!
因为视图持有者正在被回收和重复使用,导致您的视图处于错误状态。
在 SendViewHolder
class 中,您仅通过将可见性设置为可见来处理 if 块中 sentImage
的状态。因此,您还需要将其可见性设置为进入 else 块。
或者您可以先重置视图可见性,然后再显示它,如下所示。
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
}
}
recyleView
的工作基于此,它回收 视图以显示列表。当您滚动时,超出屏幕的 views
不会被销毁,而是会再次被重新使用以显示新的列表项。因此,如果您更改视图的 visibility
或任何其他 属性 并且不在 onBindViewHolder
中再次重置它,那么它将显示在它被回收之前设置的所有属性.
fun bind(data: Data) {
val textView = itemView.findViewById<TextView>(R.id.tvText)
if(data.text.isEmpty()) {
textView.visibility = View.GONE
}
}
在上面的方法中,当文本为empty
时,我们隐藏了textView
,但我们没有在else
条件中设置任何内容。因此,当我们将可见性设置为 gone
的 views
将被回收时,它们永远不会显示 textView
,因为它正在重用 view
。为了解决这个问题,我们必须分别为 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
和 ReceivedViewHolder
中,您将 ImageView
的可见性设置为 visible
sentImage.visibility = View.VISIBLE
receiveImage.visibility = View.VISIBLE
但您永远不会将其设置为 gone
。
在(message.message.equals("Sent you an image") && !message.url.equals(""))
的else条件中,将ImageView
的visibility
设为GONE
。对所有其他视图也执行相同的操作,这样您就不会得到意外的 UI.
我正在开发一个聊天应用程序。我有聊天 activity,两个用户可以在其中发送 WhatsApp 之类的消息,但我遇到了问题。
如图所示 (https://ibb.co/3cyYX01),滚动时视图乱七八糟,我想我知道为什么了。
查看这些帖子后:
我认为问题可能出在函数 onBindViewHolder
中的回收器视图适配器中,因为我在某些视图(VIEW.GONE 和 VIEW.VISIBLE)上使用了可见性选项,并且我认为这些视图正在以错误的可见性重绘。
此外,我在onBindViewHolder
中使用了holder.setIsRecyclable(false)
,以检查是否是回收部分导致了问题,当我使用它时,它运行良好。
这是 RecyclerView 适配器:
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
谢谢!
因为视图持有者正在被回收和重复使用,导致您的视图处于错误状态。
在 SendViewHolder
class 中,您仅通过将可见性设置为可见来处理 if 块中 sentImage
的状态。因此,您还需要将其可见性设置为进入 else 块。
或者您可以先重置视图可见性,然后再显示它,如下所示。
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
}
}
recyleView
的工作基于此,它回收 视图以显示列表。当您滚动时,超出屏幕的 views
不会被销毁,而是会再次被重新使用以显示新的列表项。因此,如果您更改视图的 visibility
或任何其他 属性 并且不在 onBindViewHolder
中再次重置它,那么它将显示在它被回收之前设置的所有属性.
fun bind(data: Data) {
val textView = itemView.findViewById<TextView>(R.id.tvText)
if(data.text.isEmpty()) {
textView.visibility = View.GONE
}
}
在上面的方法中,当文本为empty
时,我们隐藏了textView
,但我们没有在else
条件中设置任何内容。因此,当我们将可见性设置为 gone
的 views
将被回收时,它们永远不会显示 textView
,因为它正在重用 view
。为了解决这个问题,我们必须分别为 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
和 ReceivedViewHolder
中,您将 ImageView
的可见性设置为 visible
sentImage.visibility = View.VISIBLE
receiveImage.visibility = View.VISIBLE
但您永远不会将其设置为 gone
。
在(message.message.equals("Sent you an image") && !message.url.equals(""))
的else条件中,将ImageView
的visibility
设为GONE
。对所有其他视图也执行相同的操作,这样您就不会得到意外的 UI.