在添加或删除的 Firebase Firestore 数据中重复 RecyclerView 项
Posted
技术标签:
【中文标题】在添加或删除的 Firebase Firestore 数据中重复 RecyclerView 项【英文标题】:Duplicate RecyclerView Item on Data Added or Deleted Firebase Firestore 【发布时间】:2019-06-05 22:24:11 【问题描述】:我正在构建一个使用 firebase firestore 作为我的数据库的应用程序,我添加了分页并且它可以工作,但问题是当从应用程序中添加或删除数据时,recycler 项目重复,有时到 3、4 个地方,有时它打乱了安排。已尝试搜索解决方案并最终覆盖了这些方法:
@Override
public long getItemId(int position)
return super.getItemId(position);
@Override
public int getItemViewType(int position)
return position;
还有添加
setHasStableIds(true);
但仍然没有运气,这是我的代码片段:
public class HomeFragment extends Fragment
private View view;
private LinearLayout mNoMedia;
private RecyclerView mMediaList;
private SwipeRefreshLayout mSwipe;
private TextView mNoInternet;
private FirebaseAuth mAuth;
private FirebaseFirestore firebaseFirestore;
private List<PostModel> postModelList;
private PostAdapter postAdapter;
private DocumentSnapshot lastVisible;
private Boolean isFirstPageFirstLoad;
private String currentUserId;
private final int NUM_COLUMNS = 2;
private StaggeredGridLayoutManager staggeredGridLayoutManager;
public HomeFragment()
// Required empty public constructor
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
// Inflate the layout for this fragment
if (view == null)
view = inflater.inflate(R.layout.fragment_home, container, false);
mAuth = FirebaseAuth.getInstance();
firebaseFirestore = FirebaseFirestore.getInstance();
mSwipe = view.findViewById(R.id.mainSwipe);
mMediaList = view.findViewById(R.id.mediaList);
mNoInternet = view.findViewById(R.id.no_internet);
postModelList = new ArrayList<>();
staggeredGridLayoutManager = new StaggeredGridLayoutManager(NUM_COLUMNS, StaggeredGridLayoutManager.VERTICAL);
postAdapter = new PostAdapter(postModelList);
postAdapter.setHasStableIds(true);
mMediaList.setLayoutManager(staggeredGridLayoutManager);
mMediaList.hasFixedSize();
mMediaList.setHasFixedSize(true);
mMediaList.setItemAnimator(null);
mMediaList.setAdapter(postAdapter);
mNoMedia = view.findViewById(R.id.mainMediaLin);
//=========================== Functions ====================================================
try
if (getActivity() != null)
if (mAuth.getCurrentUser() != null)
currentUserId = mAuth.getCurrentUser().getUid();
loadPost();
else
mNoMedia.setVisibility(View.VISIBLE);
mNoInternet.setVisibility(View.VISIBLE);
else
mNoMedia.setVisibility(View.VISIBLE);
catch (Exception e)
e.printStackTrace();
return view;
private void loadPost()
Query firstQuery = firebaseFirestore.collection("TubbePosts")
.document(currentUserId).collection("Posts")
.orderBy("utc", Query.Direction.DESCENDING).limit(8);
firstQuery.addSnapshotListener(new EventListener<QuerySnapshot>()
@Override
public void onEvent(@javax.annotation.Nullable QuerySnapshot queryDocumentSnapshots, @javax.annotation.Nullable FirebaseFirestoreException e)
if (queryDocumentSnapshots != null)
if (!queryDocumentSnapshots.isEmpty())
if (isFirstPageFirstLoad)
lastVisible = queryDocumentSnapshots.getDocuments().get(queryDocumentSnapshots.size() - 1);
postModelList.clear();
for (@NonNull DocumentChange doc : queryDocumentSnapshots.getDocumentChanges())
if (doc.getType() == DocumentChange.Type.ADDED)
mNoMedia.setVisibility(View.GONE);
String docId = doc.getDocument().getId();
final PostModel postModel = doc.getDocument().toObject(PostModel.class).withId(docId);
if (isFirstPageFirstLoad)
postModelList.add(postModel);
else
postModelList.add(0, postModel);
postAdapter.notifyDataSetChanged();
isFirstPageFirstLoad = false;
);
public void loadMorePost()
Query nextQuery = firebaseFirestore.collection("TubbePosts")
.document(currentUserId).collection("Posts")
.orderBy("utc", Query.Direction.DESCENDING)
.startAfter(lastVisible).limit(8);
nextQuery.addSnapshotListener(new EventListener<QuerySnapshot>()
@Override
public void onEvent(@javax.annotation.Nullable QuerySnapshot queryDocumentSnapshots, @javax.annotation.Nullable FirebaseFirestoreException e)
if (queryDocumentSnapshots != null)
if (!queryDocumentSnapshots.isEmpty())
lastVisible = queryDocumentSnapshots.getDocuments().get(queryDocumentSnapshots.size() - 1);
for (@NonNull DocumentChange doc : queryDocumentSnapshots.getDocumentChanges())
if (doc.getType() == DocumentChange.Type.ADDED)
String docId = doc.getDocument().getId();
@NonNull final PostModel postModel = doc.getDocument().toObject(PostModel.class).withId(docId);
postModelList.add(postModel);
postAdapter.notifyDataSetChanged();
);
我的适配器:
public class PostAdapter extends RecyclerView.Adapter<PostAdapter.ViewHolder>
private List<PostModel> postList;
private Context context;
private FirebaseFirestore firebaseFirestore;
private FirebaseAuth firebaseAuth;
private String img = null;
private RequestOptions placeholder;
private String userId, id;
private int ref;
public PostAdapter(List<PostModel> postList)
this.postList = postList;
@NonNull
@Override
public PostAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_item, parent, false);
context = parent.getContext();
firebaseFirestore = FirebaseFirestore.getInstance();
firebaseAuth = FirebaseAuth.getInstance();
return new PostAdapter.ViewHolder(view);
@Override
public void onBindViewHolder(@NonNull PostAdapter.ViewHolder holder, int position)
holder.setIsRecyclable(false);
try
final String docId = postList.get(position).DocId;
final String currentUserId = firebaseAuth.getCurrentUser().getUid();
String postId = postList.get(position).getPostId();
String posterId = postList.get(position).getPosterId();
String descText = postList.get(position).getDescText();
String mediaUrl = postList.get(position).getMediaUrl();
String format = postList.get(position).getFormat();
long utcTime = postList.get(position).getUtc().getTime();
String utc = postList.get(position).getUtc().toString();
if (format.equals("image"))
holder.setImg(mediaUrl);
else if (format.equals("video"))
holder.mPlay.setVisibility(View.VISIBLE);
holder.setVideoImg(mediaUrl);
holder.mTime.setText(getTimeAgo(utcTime, context));
holder.mDesc.setText(descText);
firebaseFirestore.collection("TubbePosts").document(posterId)
.collection("Posts")
.document(postId).addSnapshotListener(new EventListener<DocumentSnapshot>()
@Override
public void onEvent(@javax.annotation.Nullable DocumentSnapshot documentSnapshot, @javax.annotation.Nullable FirebaseFirestoreException e)
if (documentSnapshot != null)
if (!documentSnapshot.exists())
try
userId = currentUserId;
id = postId;
ref = position;
new DeleteProcess().execute();
catch (Exception f)
f.printStackTrace();
);
firebaseFirestore.collection("Users").document(posterId)
.addSnapshotListener(new EventListener<DocumentSnapshot>()
@Override
public void onEvent(@javax.annotation.Nullable DocumentSnapshot documentSnapshot,
@javax.annotation.Nullable FirebaseFirestoreException e)
try
if (documentSnapshot != null)
if (documentSnapshot.exists())
String image = documentSnapshot.getString("image");
holder.setUserImg(image);
img = image;
catch (Exception f)
f.printStackTrace();
);
firebaseFirestore.collection("TubbePosts").document(posterId)
.collection("Posts")
.document(postId).collection("Likes")
.addSnapshotListener(new EventListener<QuerySnapshot>()
@Override
public void onEvent(@Nullable QuerySnapshot queryDocumentSnapshots, @Nullable FirebaseFirestoreException e)
if (queryDocumentSnapshots != null)
if (!queryDocumentSnapshots.isEmpty())
int count = queryDocumentSnapshots.size();
if (count < 1000)
holder.mLikeCount.setText(count + "");
else
holder.mLikeCount.setText(getCount(count));
else
holder.mLikeCount.setText("0");
);
firebaseFirestore.collection("TubbePosts").document(posterId)
.collection("Posts").document(postId).collection("Likes")
.document(currentUserId).addSnapshotListener(new EventListener<DocumentSnapshot>()
@Override
public void onEvent(@Nullable DocumentSnapshot documentSnapshot, @Nullable FirebaseFirestoreException e)
if (documentSnapshot != null)
if (documentSnapshot.exists())
holder.mLikeBtn.setImageDrawable(context.getResources().getDrawable(R.mipmap.heart_selected));
else
holder.mLikeBtn.setImageDrawable(context.getResources().getDrawable(R.mipmap.heart));
);
holder.mLikeBtn.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
if (isConnected(context))
firebaseFirestore.collection("TubbePosts")
.document(posterId)
.collection("Posts")
.document(postId).collection("Likes")
.document(currentUserId).get()
.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>()
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task)
if (task.isSuccessful())
if (task.getResult().exists())
firebaseFirestore.collection("TubbePosts")
.document(posterId)
.collection("Posts")
.document(postId).collection("Likes")
.document(currentUserId).delete();
else
Date utc = new Date(System.currentTimeMillis());
Map<String, Object> likesMap = new HashMap<>();
likesMap.put("utc", utc);
firebaseFirestore.collection("TubbePosts")
.document(posterId)
.collection("Posts")
.document(postId).collection("Likes")
.document(currentUserId).set(likesMap);
);
);
firebaseFirestore.collection("TubbePosts").document(posterId)
.collection("Posts").document(postId).collection("Comments")
.addSnapshotListener(new EventListener<QuerySnapshot>()
@Override
public void onEvent(@Nullable QuerySnapshot queryDocumentSnapshots, @Nullable FirebaseFirestoreException e)
if (queryDocumentSnapshots != null)
if (!queryDocumentSnapshots.isEmpty())
int count = queryDocumentSnapshots.size();
if (count < 1000)
holder.mCommentCount.setText(count + "");
else
holder.mCommentCount.setText(getCount(count));
else
holder.mCommentCount.setText("0");
);
holder.mPosterImg.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
if (!currentUserId.equals(posterId))
Intent viewIntent = new Intent(context, ViewAccountActivity.class);
viewIntent.putExtra("userId", posterId);
context.startActivity(viewIntent);
else
Toast.makeText(context, context.getResources().getString(R.string.your_account), Toast.LENGTH_SHORT).show();
/* AccountFragment accountFragment = new AccountFragment();
HomeFragment homeFragment = new HomeFragment();
FragmentTransaction fragmentTransaction = homeFragment.getActivity()
.getSupportFragmentManager()
.beginTransaction();
fragmentTransaction.replace(R.id.main_frame, accountFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit(); */
);
holder.mCommentBtn.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
Intent commentIntent = new Intent(context, CommentActivity.class);
commentIntent.putExtra("posterId", posterId);
commentIntent.putExtra("postId", postId);
context.startActivity(commentIntent);
);
holder.mCard.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
Intent viewIntent = new Intent(context, ViewActivity.class);
viewIntent.putExtra("postId", postId);
viewIntent.putExtra("posterId", posterId);
viewIntent.putExtra("desc", descText);
viewIntent.putExtra("mediaUrl", mediaUrl);
viewIntent.putExtra("posterImgUrl", img);
viewIntent.putExtra("format", format);
viewIntent.putExtra("utc", utc);
context.startActivity(viewIntent);
);
catch (Exception e)
e.printStackTrace();
@Override
public int getItemCount()
return postList.size();
@Override
public long getItemId(int position)
return position;
@Override
public int getItemViewType(int position)
return position;
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView)
super.onAttachedToRecyclerView(recyclerView);
public class ViewHolder extends RecyclerView.ViewHolder
private View mView;
private CardView mCard;
private ImageView mPostImage, mLikeBtn, mCommentBtn, mPosterImg, mPlay;
private TextView mDesc, mLikeCount, mCommentCount, mTime;
public ViewHolder(View itemView)
super(itemView);
mView = itemView;
mCard = mView.findViewById(R.id.postCard);
mPostImage = mView.findViewById(R.id.post_img);
mPosterImg = mView.findViewById(R.id.posterImg);
mDesc = mView.findViewById(R.id.postDesc);
mLikeBtn = mView.findViewById(R.id.likeBtn);
mLikeCount = mView.findViewById(R.id.likesCount);
mCommentBtn = mView.findViewById(R.id.commentBtn);
mCommentCount = mView.findViewById(R.id.commentCount);
mTime = mView.findViewById(R.id.postTime);
mPlay = mView.findViewById(R.id.play_hint);
private void setUserImg(String downloadUrl)
if (!TextUtils.isEmpty(downloadUrl))
RequestOptions placeholderRequest = new RequestOptions();
placeholderRequest.placeholder(R.color.grey200);
Glide.with(context).setDefaultRequestOptions(placeholderRequest).load(downloadUrl).into(mPosterImg);
private void setImg(String downloadUrl)
if (!TextUtils.isEmpty(downloadUrl))
RequestOptions placeholderRequest = new RequestOptions();
placeholderRequest.placeholder(R.drawable.ic_image);
Glide.with(context).setDefaultRequestOptions(placeholderRequest).load(downloadUrl).into(mPostImage);
private void setVideoImg(String downloadUrl)
if (!TextUtils.isEmpty(downloadUrl))
long interval = getPosition() * 1000;
RequestOptions options = new RequestOptions().frame(interval);
placeholder = new RequestOptions();
placeholder.placeholder(R.drawable.ic_image);
Glide.with(context).setDefaultRequestOptions(placeholder).asBitmap()
.load(downloadUrl).apply(options).into(mPostImage);
还有我的模特:
public class PostModel extends DocId
private String descText, postId, mediaUrl, posterId, format;
private Date utc;
public PostModel ()
public PostModel(String descText, String postId, String mediaUrl, String posterId, String format, Date utc)
this.descText = descText;
this.postId = postId;
this.mediaUrl = mediaUrl;
this.posterId = posterId;
this.format = format;
this.utc = utc;
public String getDescText()
return descText;
public String getPostId()
return postId;
public String getMediaUrl()
return mediaUrl;
public String getPosterId()
return posterId;
public String getFormat()
return format;
public Date getUtc()
return utc;
感谢您的帮助。
【问题讨论】:
如果您遇到问题,最好在发布问题时创建MCVE。您为此问题发布了几乎500
行代码。这对于人们解析和尝试在线调试来说非常重要。请编辑您的问题以仅保留相关代码。
与此同时,您可以查看我在 this 帖子中的回答,我在该帖子中解释了如何通过将查询游标与 @ 组合来在 Firestore 中对查询进行分页987654330@方法..我也可以看看这个video更好的理解。
谢谢,但我需要在没有 .get() 的情况下实时检索数据
在这种情况下,您需要使用addSnapshotListener
。几乎是一样的。
【参考方案1】:
从 firestore 数据库 调用数据时,我在 recyclerView
上遇到了同样的数据重复问题。使用 Java 8,它允许通过调用 clear()
方法来使用 Collection Interface,如下所示:
listName.clear(); //clears the list
listName.add(new list(a,b)); //update the list with new data
adapter.notifyDataSetChanged(); //updates the data into a recyclerView
另外,如果要查询多个数据以显示在recyclerView
上,可以调用removeIf()
方法:
listName.removeIf(list -> list.getId() = 1); //Removes all of the elements of this collection that satisfy the given predicate.
listName.add(new list(a,b)); //update the list with new data
adapter.notifyDataSetChanged(); //updates the data into a recyclerView
上述方法clear()
和removeIf()
避免了recyclerView
中的数据重复。
【讨论】:
【参考方案2】:我使用.addSnapshotListener
的解决方案是在添加文档之前在可变列表中使用clear()
:
productosLista.clear()
for (doc in documentSnapshots!!)
val producto = doc.toObject<Productos>(Productos::class.java)
productosLista.add(producto)
【讨论】:
以上是关于在添加或删除的 Firebase Firestore 数据中重复 RecyclerView 项的主要内容,如果未能解决你的问题,请参考以下文章
添加或删除的Firebase Firestore上的重复RecyclerView项
firebase 删除值导致 tableview 重新加载元素的两倍