滚动后未正确应用聊天 2 不同持有人的 FirebaseUI Recycler 视图
FirebaseUI Recycler View for Chat 2 Different Holder Not Applied Correctly AFTER Scrolling
我正在制作一个聊天应用程序。有 2 个视图相同的 viewholder。
问题是,当我滚动到顶部时,图像、名称没有按假设正确插入。
我正在为实时数据库使用 Firebase Ui Recycler View。
firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<AdminMessage, AdminChatViewHolder>(firebaseRecyclerOptions) {
@Override
protected void onBindViewHolder(@NonNull final AdminChatViewHolder adminChatViewHolder, int i, @NonNull AdminMessage adminMessage) {
adminChatViewHolder.getTvMessage().setText(adminMessage.getMessage());
//We retrieve the userUid to get real time name
FirebaseDatabase.getInstance().getReference().child("admin").child(adminMessage.getSenderUid()).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
Admin admin = dataSnapshot.getValue(Admin.class);
if (admin != null) {
final String nameAdmin = admin.getFullName();
final String profileUrl = admin.getProfileUrl();
//And then display
adminChatViewHolder.getTvName().setText(nameAdmin);
//Check for image if null or not (profileUrl)
if (profileUrl != null) {
Glide.with(getApplicationContext())
.load(profileUrl)
.into(adminChatViewHolder.getImageView());
}
}
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
@NonNull
@Override
public AdminChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view;
if (viewType == VT_SENDER) {
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_admin_message_sender, parent, false);
} else {
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_admin_message_receiver, parent, false);
}
return new AdminChatViewHolder(view);
}
@Override
public void onDataChanged() {
progressBar.setVisibility(View.GONE);
}
@Override
public int getItemViewType(int position) {
if (getItem(position).getSenderUid().equals(userUid)) {
return VT_SENDER;
}
return VT_RECEIVER;
}
};
@NonNull
@Override
public AdminChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view;
if (viewType == VT_SENDER) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_admin_message_sender, parent, false);
return new AdminChatViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_admin_message_receiver, parent, false);
return new AdminChatViewHolder(view);
}
}
如果您的查询是正确的并且您的检索数据是正确的,那么您需要在每次显示时都需要新的视图
我认为问题可能出在来自 Firebase 数据的异步查询上。您将更新视图逻辑放在 onDataChange
中。但是,如果您快速滚动到顶部,则该行已经被回收和重复使用。在这种情况下,如果在回收行后触发 onDataChange
,内容可能会设置到错误的目标视图。
您可以做的是通过设置适当的查询来检索 Admin
对象或使用传递给 AdminMessage
的 AdminMessage
将数据检索逻辑移回 FirebaseRecyclerOptions
=15=]函数。
你可以参考这个tutorial中的第12步,看看onBindViewHolder应该做什么。
好的,我记得我以前遇到过同样的问题。我会尽我所能记住并更新这个答案。
当没有图像或url失败时总是清空图像视图或将其隐藏:
if (profileUrl != null) {
Glide.with(getApplicationContext())
.load(profileUrl)
.into(adminChatViewHolder.getImageView());
}
else
{
//TODO: set an image place holder or hide adminChatViewHolder.getImageView()
}
我喜欢在等待新图像加载时清空图像视图
并为所有视图做同样的事情,而不仅仅是 imageView(即 textview)
如果 admin 为空怎么办?会发生什么?
别忘了打电话super.onDataChanged();
您的代码非常简单,我看到您正在尝试获取您提供的 ID 的一些详细信息。我确实在加载图片时创建了一个可用的聊天应用 here and something similar in it. I encountered this issue. The issue is related to the image caching
Glide
uses. Make use of Glide Cache. Also, you should use placeholder and error。
最初绑定时重置您的 VH,并确保您使用 Glide 取消请求:)
这是因为您在 onBindViewHolder
中附加了 firebase 侦听器 - 当您滚动时,每次调用该方法时都会附加新的侦听器,如果您快速滚动,这可能会导致项目中的数据损坏,因为旧的异步结果可能会进入新的侦听器。这是非常常见的生命周期错误,如果异步逻辑处理有缺陷,它会直接出现在列表、回收器视图甚至片段和活动中。
要在您的情况下解决此问题,请在 RecyclerViewAdapter
- onViewRecycled
中使用专门为此类情况设计的方法
它应该是这样的。
首先,您需要像这样在适配器或查看器中将侦听器和数据库引用定义为单独的全局变量class
DatabaseRefference ref;
ValueEventListener listener;
然后在 onBindViewHolder
@Override
protected void onBindViewHolder(...){
...
listener = = new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
Admin admin = dataSnapshot.getValue(Admin.class);
if (admin != null) {
final String nameAdmin = admin.getFullName();
final String profileUrl = admin.getProfileUrl();
//And then display
adminChatViewHolder.getTvName().setText(nameAdmin);
//Check for image if null or not (profileUrl)
if (profileUrl != null) {
Glide.with(getApplicationContext())
.load(profileUrl)
.into(adminChatViewHolder.getImageView());
}
}
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
};
ref = FirebaseDatabase.getInstance().getReference().child("admin").child(adminMessage.getSenderUid());
ref.addListenerForSingleValueEvent(listener);
...
}
然后在视图回收事件上分离侦听器
override fun onViewRecycled(holder: StopsViewHolder) {
super.onViewRecycled(holder)
if (ref != null && listener != null)
ref.removeEventListener(listener)
}
它可能需要一些额外的工作,因为我还没有在 IDE 中测试它。希望对你有帮助。
我正在制作一个聊天应用程序。有 2 个视图相同的 viewholder。 问题是,当我滚动到顶部时,图像、名称没有按假设正确插入。 我正在为实时数据库使用 Firebase Ui Recycler View。
firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<AdminMessage, AdminChatViewHolder>(firebaseRecyclerOptions) {
@Override
protected void onBindViewHolder(@NonNull final AdminChatViewHolder adminChatViewHolder, int i, @NonNull AdminMessage adminMessage) {
adminChatViewHolder.getTvMessage().setText(adminMessage.getMessage());
//We retrieve the userUid to get real time name
FirebaseDatabase.getInstance().getReference().child("admin").child(adminMessage.getSenderUid()).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
Admin admin = dataSnapshot.getValue(Admin.class);
if (admin != null) {
final String nameAdmin = admin.getFullName();
final String profileUrl = admin.getProfileUrl();
//And then display
adminChatViewHolder.getTvName().setText(nameAdmin);
//Check for image if null or not (profileUrl)
if (profileUrl != null) {
Glide.with(getApplicationContext())
.load(profileUrl)
.into(adminChatViewHolder.getImageView());
}
}
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
@NonNull
@Override
public AdminChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view;
if (viewType == VT_SENDER) {
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_admin_message_sender, parent, false);
} else {
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_admin_message_receiver, parent, false);
}
return new AdminChatViewHolder(view);
}
@Override
public void onDataChanged() {
progressBar.setVisibility(View.GONE);
}
@Override
public int getItemViewType(int position) {
if (getItem(position).getSenderUid().equals(userUid)) {
return VT_SENDER;
}
return VT_RECEIVER;
}
};
@NonNull
@Override
public AdminChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view;
if (viewType == VT_SENDER) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_admin_message_sender, parent, false);
return new AdminChatViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_admin_message_receiver, parent, false);
return new AdminChatViewHolder(view);
}
}
如果您的查询是正确的并且您的检索数据是正确的,那么您需要在每次显示时都需要新的视图
我认为问题可能出在来自 Firebase 数据的异步查询上。您将更新视图逻辑放在 onDataChange
中。但是,如果您快速滚动到顶部,则该行已经被回收和重复使用。在这种情况下,如果在回收行后触发 onDataChange
,内容可能会设置到错误的目标视图。
您可以做的是通过设置适当的查询来检索 Admin
对象或使用传递给 AdminMessage
的 AdminMessage
将数据检索逻辑移回 FirebaseRecyclerOptions
=15=]函数。
你可以参考这个tutorial中的第12步,看看onBindViewHolder应该做什么。
好的,我记得我以前遇到过同样的问题。我会尽我所能记住并更新这个答案。
当没有图像或url失败时总是清空图像视图或将其隐藏:
if (profileUrl != null) {
Glide.with(getApplicationContext())
.load(profileUrl)
.into(adminChatViewHolder.getImageView());
}
else
{
//TODO: set an image place holder or hide adminChatViewHolder.getImageView()
}
我喜欢在等待新图像加载时清空图像视图 并为所有视图做同样的事情,而不仅仅是 imageView(即 textview) 如果 admin 为空怎么办?会发生什么?
别忘了打电话super.onDataChanged();
您的代码非常简单,我看到您正在尝试获取您提供的 ID 的一些详细信息。我确实在加载图片时创建了一个可用的聊天应用 here and something similar in it. I encountered this issue. The issue is related to the image caching
Glide
uses. Make use of Glide Cache. Also, you should use placeholder and error。
最初绑定时重置您的 VH,并确保您使用 Glide 取消请求:)
这是因为您在 onBindViewHolder
中附加了 firebase 侦听器 - 当您滚动时,每次调用该方法时都会附加新的侦听器,如果您快速滚动,这可能会导致项目中的数据损坏,因为旧的异步结果可能会进入新的侦听器。这是非常常见的生命周期错误,如果异步逻辑处理有缺陷,它会直接出现在列表、回收器视图甚至片段和活动中。
要在您的情况下解决此问题,请在 RecyclerViewAdapter
- onViewRecycled
它应该是这样的。
首先,您需要像这样在适配器或查看器中将侦听器和数据库引用定义为单独的全局变量class
DatabaseRefference ref;
ValueEventListener listener;
然后在 onBindViewHolder
@Override
protected void onBindViewHolder(...){
...
listener = = new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
Admin admin = dataSnapshot.getValue(Admin.class);
if (admin != null) {
final String nameAdmin = admin.getFullName();
final String profileUrl = admin.getProfileUrl();
//And then display
adminChatViewHolder.getTvName().setText(nameAdmin);
//Check for image if null or not (profileUrl)
if (profileUrl != null) {
Glide.with(getApplicationContext())
.load(profileUrl)
.into(adminChatViewHolder.getImageView());
}
}
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
};
ref = FirebaseDatabase.getInstance().getReference().child("admin").child(adminMessage.getSenderUid());
ref.addListenerForSingleValueEvent(listener);
...
}
然后在视图回收事件上分离侦听器
override fun onViewRecycled(holder: StopsViewHolder) {
super.onViewRecycled(holder)
if (ref != null && listener != null)
ref.removeEventListener(listener)
}
它可能需要一些额外的工作,因为我还没有在 IDE 中测试它。希望对你有帮助。