#java #android #android-adapter #android-viewholder
#java #Android #android-адаптер #android-viewholder
Вопрос:
У меня есть функция комментариев в моем приложении, где пользователи могут комментировать сообщения друг друга, а также им могут нравиться или не нравиться комментарии друг друга. Кроме того, если они недовольны своим комментарием, они могут его удалить. Все работает нормально, пока я не перейду к удалению части комментариев, потому что тогда я вызываю notifyDataSetChanged();
или notifyItemRangeChanged();
, и все перепутывается, потому что, когда список комментариев обновляется, и я пытаюсь поставить лайк какому-то комментарию в списке, иногда лайкают больше одного, или если я нажимаю «Не нравится», иногда больше одного не нравится, а иногда выбираются как «нравится», так и «не нравится».
Я буквально перепробовал тысячу способов исправить это, и ничего не сработало. Ниже я покажу вам, ребята, несколько примеров того, что я пробовал… В onChildRemoved(); Я пробовал это так:
CommentsActivity
private void readComments() {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Comments").child(mPostId);
ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
Comment comment = dataSnapshot.getValue(Comment.class);
comment.setComment(comment.getComment());
mCommentList.add(comment);
keysList.add(dataSnapshot.getKey());
mCommentAdapter.notifyItemInserted(mCommentList.size() - 1);
}
@Override
public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
}
@Override
public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
mCommentAdapter.removeComment(dataSnapshot.getKey());
mRecyclerView.setAdapter(mCommentAdapter);
mCommentAdapter.notifyDataSetChanged();
}
@Override
public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
};
reference.addChildEventListener(childEventListener);
}
@Override
public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
int index = keysList.indexOf(dataSnapshot.getKey());
mCommentList.remove(index);
mCommentAdapter.notifyItemChanged(index);
keysList.remove(index);
mCommentAdapter.notifyItemRangeChanged(index, mCommentList.size());
}
@Override
public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
index = keysList.indexOf(dataSnapshot.getKey());
keysList.remove(index);
mCommentList.remove(index);
mCommentAdapter.notifyDataSetChanged();
}
И список продолжается. Я перепробовал так много их вариантов, и все равно после удаления комментария, когда я пытаюсь понравиться или не понравиться комментарию в списке, более одного получают лайк или не лайк, а иногда выбираются как лайк, так и не лайк.
Идеи о том, что может быть причиной этого?
CommentAdapter (Как мне нравятся и не нравятся комментарии)
public class CommentAdapter extends RecyclerView.Adapter<CommentAdapter.ViewHolder> {
private Context mContext;
private List<Comment> mCommentList;
private String mPostId;
private String mNotificationId;
private FirebaseUser mFirebaseUser;
public CommentAdapter(Context mContext, List<Comment> mComment, String mPostId) {
this.mContext = mContext;
this.mCommentList = mComment;
this.mPostId = mPostId;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.comment_item, parent, false);
return new CommentAdapter.ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
mFirebaseUser = FirebaseAuth.getInstance().getCurrentUser();
final Comment comment = mCommentList.get(position);
holder.comment.setText(comment.getComment());
commentLike(comment.getCommentid(), holder.commentLike);
commentDislike(comment.getCommentid(), holder.commentDislike);
getUserInfo(holder.image_profile, holder.username, comment.getPublisher());
commentLikesNumber(holder.commentLikesNumber, comment.getCommentid());
commentDislikesNumber(holder.commentDislikesNumber, comment.getCommentid());
commentResponseNumber(holder.commentResponseNumber, comment.getCommentid());
holder.iconComment.setOnClickListener(v -> {
Intent intent = new Intent(mContext, ResponseActivity.class);
intent.putExtra("commentid", comment.getCommentid());
intent.putExtra("publisherid", comment.getPublisher());
intent.putExtra("postid", mPostId);
mContext.startActivity(intent);
});
holder.commentLike.setOnClickListener(v -> {
if (holder.commentLike.getTag().equals("like") amp;amp; holder.commentDislike.getTag().equals("dislike")) {
likeComment(comment.getCommentid(), mPostId, comment.getPublisher());
} else if (holder.commentLike.getTag().equals("like") amp;amp; holder.commentDislike.getTag().equals("disliked")) {
likeComment(comment.getCommentid(), mPostId, comment.getPublisher());
removeDislike(comment.getCommentid());
} else {
removeLike(comment.getPublisher(), comment.getCommentid());
}
});
holder.commentDislike.setOnClickListener(v -> {
if (holder.commentDislike.getTag().equals("dislike") amp;amp; holder.commentLike.getTag().equals("like")) {
dislikeComment(comment.getCommentid());
Toast.makeText(mContext, "Don't be mean", Toast.LENGTH_SHORT).show();
} else if (holder.commentDislike.getTag().equals("dislike") amp;amp; holder.commentLike.getTag().equals("liked")) {
dislikeComment(comment.getCommentid());
removeLike(comment.getPublisher(), comment.getCommentid());
Toast.makeText(mContext, "Don't be mean", Toast.LENGTH_SHORT).show();
} else {
removeDislike(comment.getCommentid());
}
});
holder.image_profile.setOnClickListener(v -> {
Intent intent = new Intent(mContext, MainActivity.class);
intent.putExtra("publisherid", comment.getPublisher());
mContext.startActivity(intent);
});
//Timestamp to Date
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
Date date = new Date(timestamp.getTime());
//Current timestamp
Calendar calendar = Calendar.getInstance();
long ts = calendar.getTimeInMillis();
//Getting comment timestamp from database
long timestampOfComment = comment.getTimestamp();
//SECOND WAY
long duration = ts - timestampOfComment;
long diffInSeconds = TimeUnit.MILLISECONDS.toSeconds(duration);
long diffInMinutes = TimeUnit.MILLISECONDS.toMinutes(duration);
long diffInHours = TimeUnit.MILLISECONDS.toHours(duration);
long diffInDays = TimeUnit.MILLISECONDS.toDays(duration);
if (diffInSeconds < 10) {
holder.timestamp.setText(" u25CF " "Just now");
} else if (diffInSeconds > 10 amp;amp; diffInSeconds < 60) {
holder.timestamp.setText(" u25CF " diffInSeconds " seconds ago");
} else if (diffInSeconds > 60 amp;amp; diffInMinutes == 1) {
holder.timestamp.setText(" u25CF " diffInMinutes " minute ago");
} else if (diffInSeconds > 60 amp;amp; diffInMinutes < 60) {
holder.timestamp.setText(" u25CF " diffInMinutes " minutes ago");
} else if (diffInSeconds > 60 amp;amp; diffInMinutes > 60 amp;amp; diffInHours == 1) {
holder.timestamp.setText(" u25CF " diffInHours " hour ago");
} else if (diffInSeconds > 60 amp;amp; diffInMinutes > 60 amp;amp; diffInHours > 1 amp;amp; diffInHours < 24) {
holder.timestamp.setText(" u25CF " diffInHours " hours ago");
} else if (diffInSeconds > 60 amp;amp; diffInMinutes > 60 amp;amp; diffInHours > 24 amp;amp; diffInDays == 1) {
holder.timestamp.setText(" u25CF " diffInDays " day ago");
} else if (diffInSeconds > 60 amp;amp; diffInMinutes > 60 amp;amp; diffInHours > 24 amp;amp; diffInDays > 1) {
holder.timestamp.setText(" u25CF " diffInDays " days ago");
}
holder.itemView.setOnLongClickListener(v -> {
if (comment.getPublisher().equals(mFirebaseUser.getUid())) {
AlertDialog alertDialog = new AlertDialog.Builder(mContext).create();
alertDialog.setTitle("Would you like to delete this comment?");
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "No", (dialog, which) -> dialog.dismiss());
alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "Yes", (dialog, which) -> {
deleteCommentNotification(mPostId, comment.getCommentid());
FirebaseDatabase.getInstance().getReference("Comments Liked").child(comment.getCommentid()).setValue(null);
FirebaseDatabase.getInstance().getReference("Comments Disliked").child(comment.getCommentid()).setValue(null);
FirebaseDatabase.getInstance().getReference("Responses").child(comment.getCommentid()).setValue(null);
Toast.makeText(mContext, "Your comment has been deleted.", Toast.LENGTH_SHORT).show();
dialog.dismiss();
});
alertDialog.show();
}
return true;
});
}
@Override
public int getItemCount() {
if (mCommentList != null) {
return mCommentList.size();
} else {
return 0;
}
}
static class ViewHolder extends RecyclerView.ViewHolder {
CircleImageView image_profile;
TextView username, comment, commentLikesNumber, commentDislikesNumber, commentResponseNumber, timestamp;
ImageView commentLike, commentDislike, iconComment;
ViewHolder(@NonNull View itemView) {
super(itemView);
image_profile = itemView.findViewById(R.id.image_profile);
comment = itemView.findViewById(R.id.comment);
username = itemView.findViewById(R.id.username);
commentLike = itemView.findViewById(R.id.icon_thumb_up_grey);
commentDislike = itemView.findViewById(R.id.icon_thumb_down_grey);
commentLikesNumber = itemView.findViewById(R.id.comment_likes_number);
commentDislikesNumber = itemView.findViewById(R.id.comment_dislikes_number);
iconComment = itemView.findViewById(R.id.icon_comment_grey);
commentResponseNumber = itemView.findViewById(R.id.comment_response_number);
timestamp = itemView.findViewById(R.id.timestamp);
}
}
private void deleteCommentNotification(String postid, String commentid) {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Comments").child(postid).child(commentid);
reference.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
String notificationId = dataSnapshot.child("notificationId").getValue(String.class);
String user = dataSnapshot.child("for").getValue(String.class);
if (notificationId != null amp;amp; user != null) {
FirebaseDatabase.getInstance().getReference("Notifications").child(user).child(notificationId).removeValue();
reference.removeValue();
}
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
private void getUserInfo(final ImageView prof_image, final TextView username, String publisherid) {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Users").child(publisherid);
reference.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
if (user != null) {
Glide.with(mContext.getApplicationContext()).load(user.getImageurl()).into(prof_image);
username.setText(user.getUsername());
} else {
prof_image.setImageResource(R.drawable.profile_placeholder);
username.setVisibility(View.GONE);
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
public void removeComment(final String key) {
for (int i = 0; i < mCommentList.size(); i ) {
if (mCommentList.get(i).getCommentid().equals(key)) {
mCommentList.remove(i);
break;
}
}
}
private void commentLike(final String commentid, final ImageView imageView) {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Comments Liked").child(commentid);
reference.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
if (mFirebaseUser != null)
if (dataSnapshot.child(mFirebaseUser.getUid()).exists()) {
imageView.setImageResource(R.drawable.ic_thumb_up_blue);
imageView.setTag("liked");
} else {
imageView.setImageResource(R.drawable.ic_thumb_up_grey);
imageView.setTag("like");
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
private void commentDislike(final String commentid, final ImageView imageView) {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Comments Disliked").child(commentid);
reference.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
if (mFirebaseUser != null)
if (dataSnapshot.child(mFirebaseUser.getUid()).exists()) {
imageView.setImageResource(R.drawable.ic_thumb_down_blue);
imageView.setTag("disliked");
} else {
imageView.setImageResource(R.drawable.ic_thumb_down_grey);
imageView.setTag("dislike");
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
private void commentLikesNumber(TextView commentLikesNumber, String commentid) {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Comments Liked").child(commentid);
reference.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
long commentLikes = dataSnapshot.getChildrenCount();
if (commentLikes == 0) {
commentLikesNumber.setVisibility(View.INVISIBLE);
} else {
commentLikesNumber.setVisibility(View.VISIBLE);
commentLikesNumber.setText(String.valueOf(commentLikes));
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
private void commentDislikesNumber(TextView commentDislikesNumber, String commentid) {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Comments Disliked").child(commentid);
reference.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
long commentDislikes = dataSnapshot.getChildrenCount();
if (commentDislikes == 0) {
commentDislikesNumber.setVisibility(View.INVISIBLE);
} else {
commentDislikesNumber.setVisibility(View.VISIBLE);
commentDislikesNumber.setText(String.valueOf(commentDislikes));
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
private void commentResponseNumber(TextView commentResponseNumber, String commentid) {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Responses").child(commentid);
reference.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
long commentResponses = dataSnapshot.getChildrenCount();
if (commentResponses == 0) {
commentResponseNumber.setVisibility(View.INVISIBLE);
} else {
commentResponseNumber.setVisibility(View.VISIBLE);
commentResponseNumber.setText(String.valueOf(commentResponses));
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
private void likeComment(String commentId, String postId, String userId) {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Comments Liked").child(commentId).child(mFirebaseUser.getUid());
mNotificationId = reference.push().getKey();
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("notificationId", mNotificationId);
hashMap.put("commentid", commentId);
hashMap.put("userid", mFirebaseUser.getUid());
reference.setValue(hashMap);
HashMap<String, Object> hashMap1 = new HashMap<>();
hashMap1.put("userid", mFirebaseUser.getUid());
hashMap1.put("comment", "liked your comment");
hashMap1.put("postid", postId);
hashMap1.put("ispost", true);
hashMap1.put("notificationId", mNotificationId);
hashMap1.put("commentid", commentId);
hashMap1.put("seen", false);
hashMap1.put("type", "");
if (!mFirebaseUser.getUid().equals(userId)) {
FirebaseDatabase.getInstance().getReference("Notifications").child(userId).child(mNotificationId).setValue(hashMap1);
}
}
private void removeLike(String userId, String commentId) {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Comments Liked").child(commentId).child(mFirebaseUser.getUid());
reference.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
String notificationId = dataSnapshot.child("notificationId").getValue(String.class);
if (notificationId != null) {
FirebaseDatabase.getInstance().getReference("Notifications").child(userId).child(notificationId).removeValue();
reference.removeValue();
}
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
private void dislikeComment(String commentId) {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Comments Disliked").child(commentId).child(mFirebaseUser.getUid());
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("commentid", commentId);
hashMap.put("notificationId", mNotificationId);
hashMap.put("userid", mFirebaseUser.getUid());
reference.setValue(hashMap);
}
private void removeDislike(String commentId) {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Comments Disliked").child(commentId).child(mFirebaseUser.getUid());
reference.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
reference.removeValue();
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
}
Комментарии:
1. Как вам нравится и не нравится сообщение? может быть, эта функциональность создает проблемный почтовый код
2. @chandmohd в моем классе адаптера мне нравятся и не нравятся комментарии на основе их тегов. Я только что опубликовал код, чтобы вы могли его увидеть.
Ответ №1:
Причиной странного поведения являются ValueEventListener
объекты, которые вы регистрируете при каждом вызове onBindViewHolder
. onBindViewHolder
Метод вызывается каждый раз, когда новый элемент прокручивается в поле зрения или изменяются его данные. Поскольку вы никогда не отменяете регистрацию слушателей, они продолжают обновлять представления, даже если они уже могут быть привязаны к другим элементам.
Не регистрируйте никаких слушателей в onBindViewHolder
. Настройте ValueEventListener
ы с вашим адаптером и отмените их регистрацию, когда адаптер больше не используется. В прослушивателе вызываются методы уведомления адаптера вместо непосредственного обновления представлений. Переместите регистрацию onClickListeners в ваш ViewHolder и сделайте их общими, чтобы они не были привязаны к определенному элементу. onBindViewHolder
Следует просто обновить ваше представление.
Все изменения должны устранить вашу проблему и сделать прокрутку еще более плавной 😉
CommentAdapter
public class CommentAdapter extends RecyclerView.Adapter<CommentAdapter.ViewHolder> {
private Context mContext;
private List<Comment> mCommentList;
private String mPostId;
private String mNotificationId;
private FirebaseUser mFirebaseUser;
public CommentAdapter(Context mContext, List<Item> items, String mPostId) {
this.items = items;
this.mPostId = mPostId;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.comment_item, parent, false);
return new CommentAdapter.ViewHolder(mContext, mPostId, view);
}
@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
holder.update(items.get(position));
}
ViewHolder
public static class ViewHolder extends RecyclerView.ViewHolder {
private Item item;
public ViewHolder(Context context, String mPostId, View itemView) {
super(itemView);
// ...
// Register OnClickListeners here once for the view. Example:
icon.setOnClickListener(v -> {
if (item != null) {
Comment comment = item.getComment();
Intent intent = new Intent(mContext, ResponseActivity.class);
intent.putExtra("commentid", comment.getCommentid());
intent.putExtra("publisherid", comment.getPublisher());
intent.putExtra("postid", mPostId);
mContext.startActivity(intent);
}
});
// ...
}
public void update(Item item) {
this.item = item;
update(item.getComment());
update(item.getLikeStatus());
}
public void update(Comment comment) {
textControl.setText(comment.getText());
// ...
}
public void update(LikeStatus status) {
if (status.likes == 0) {
likeNumbersText.setVisibility(View.INVISIBLE);
} else {
likeNumbersText.setVisibility(View.VISIBLE);
likeNumbersText.setText(String.valueOf(likes));
}
if (status.dislikes == 0) {
dislikeNumbersText.setVisibility(View.INVISIBLE);
} else {
dislikeNumbersText.setVisibility(View.VISIBLE);
dislikesNumbersText.setText(String.valueOf(likes));
}
}
// ...
}
Активность
DatabaseReference comments = FirebaseDatabase.getInstance().getReference("Comments").child(mPostId);
ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
Comment comment = dataSnapshot.getValue(Comment.class);
Item item = getItemByCommentId(dataSnapshot.getKey());
item.setComment(comment);
mCommentAdatper.notifyItemChanged(comment.getPosition());
}
//...
};
comments.addChildEventListener(childEventListener);
DatabaseReference likes = FirebaseDatabase.getInstance().getReference("Comments Liked");
ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
long likes = dataSnapshot.getChildrenCount();
String commendId = dataSnapshot.getKey();
Item item = getItemByCommentId(commendId);
Comment comment = item.getComment();
LikeStatus likeStatus = item.getLikeStatus();
likeStatus.likes = likes;
mCommentAdatper.notifyItemChanged(comment.getPosition());
}
// ...
};
likes.addChildEventListener(childEventListener);
Комментарии:
1. Эй, чувак, спасибо за твой ответ. Я пытаюсь понять, как делать то, что вы говорите мне делать. Можете ли вы привести мне пример того, как это сделать с помощью одного из моих методов, который неправильно размещен? Так, например,
holder.commentLike(...)
следует поместить в другое место, чтобы сделать представление более общим? И после его вызова удалите прослушиватель …?2. Можете ли вы также поделиться кодом likeComment и dislikeComment? Было бы проще предоставить вам пример.
3. Да, чувак, абсолютно. Но вы думаете, что я пишу метод
commentLike();
andcommentDislike();
вViewholder
? Потому что, если это так, это не так. Я просто не хотел перегружать вопрос ненужным кодом. Позвольте мне продолжить и загрузить мойCommentAdapter
класс, чтобы вы могли его увидеть, или, может быть, мы могли бы даже пообщаться, и я отправлю его вам таким образом. Все, что вы предпочитаете.4. Я добавил несколько примеров кода к своему ответу. Пытался упростить его и поэтому пропустил некоторые части. Главное, что вы должны использовать адаптер по-другому и вывести слушателей из него.
5. В CommentAdapter в конструкторе
public CommentAdapter(Context mContext, List<Item> items, String mPostId)
какой элемент вы передаете для списка? Это должен быть мой класс bean, верно? Итак, комментарий… Я думаю