J'essaye de créer un chat. Cependant, je suis confronté à un gros problème. Lorsque je supprime une entrée du chat recyclerView, l'entrée y reste, mais est supprimée de la base de données Firebase comme prévu (je veux dire que seule la suppression de la base de données fonctionne). Il ne disparaît que lorsque je ferme l'activité et que je l'ouvre à nouveau. Mon recyclerView a une fonction de charger 10 entrées supplémentaires chaque fois qu'un utilisateur fait défiler.
Je ne comprends pas comment puis-je supprimer l'entrée de recyclerView?
J'ai besoin d'appeler la fonction loadMoreMessages ()? Rafraîchir l'activité?
MessageAdapter.java
public class ChatActivity extends AppCompatActivity { private static final String TAG = "ChatActivity"; //todo check is user is logedin //user with whom Iam talking to private String mChatUser; private Toolbar mChatToolbar; private DatabaseReference mRootRef; private TextView mTitleView, mLastSeenView; private CircleImageView mProfileImage; private FirebaseAuth mAuth; private String mCurrentUserId; private ImageButton mChatAddBtn; private ImageButton mChatSendBtn; private EditText mChatMessageView; private RecyclerView mMessagesList; private SwipeRefreshLayout mRefreshLayout; private final List<Messages> messagesList = new ArrayList<>(); private LinearLayoutManager mLinearLayout; private MessageAdapter mAdapter; private static final int TOTAL_ITEMS_TO_LOAD = 10; private int mCurrentPage = 1; private int itemPos =0; private String mLastKey = ""; private String mPrevKey = ""; private static final int GALLERY_PICK = 1; //Storage Firebase private StorageReference mImageStorage; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_chat); // create a toolbar and set the title as the user with we are chatting to mChatToolbar = (Toolbar) findViewById(R.id.chat_app_bar); setSupportActionBar(mChatToolbar); ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); // to add a custom view to the toolbar actionBar.setDisplayShowCustomEnabled(true); mRootRef = FirebaseDatabase.getInstance().getReference(); mAuth = FirebaseAuth.getInstance(); mCurrentUserId = mAuth.getCurrentUser().getUid(); mChatUser = getIntent().getStringExtra("user_id"); String userName = getIntent().getStringExtra("user_name"); getSupportActionBar().setTitle(userName); // String userName = getIntent().getStringExtra("user_name"); LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View action_bar_view = inflater.inflate(R.layout.chat_custom_bar, null); actionBar.setCustomView(action_bar_view); // ---------- Custom Action bar items --- mTitleView = (TextView) findViewById(R.id.custom_bar_title); mLastSeenView = (TextView) findViewById(R.id.custom_bar_seen); mProfileImage = (CircleImageView) findViewById(R.id.custom_bar_image); mChatAddBtn = (ImageButton) findViewById(R.id.chat_add_btn); mChatSendBtn = (ImageButton) findViewById(R.id.chat_send_btn); mChatMessageView = (EditText) findViewById(R.id.chat_message_view); mAdapter = new MessageAdapter(messagesList); mImageStorage= FirebaseStorage.getInstance().getReference(); mMessagesList = (RecyclerView) findViewById(R.id.messages_list); mRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.message_swipe_layout); mLinearLayout = new LinearLayoutManager(this); mMessagesList.setHasFixedSize(true); mMessagesList.setLayoutManager(mLinearLayout); //mMessagesList.setItemViewCacheSize(25); mMessagesList.setDrawingCacheEnabled(true); mMessagesList.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH); mMessagesList.setAdapter(mAdapter); loadMessages(); mTitleView.setText(userName); mRootRef.child("users").child(mChatUser).addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { String online = dataSnapshot.child("lastSeen").getValue().toString(); //String image = dataSnapshot.child() if(online.equals("true")){ mLastSeenView.setText("Online"); }else { GetTimeAgo getTimeAgo = new GetTimeAgo(); long lastTime = Long.parseLong(online); String lastSeenTime = getTimeAgo.getTimeAgo(lastTime, getApplicationContext()); mLastSeenView.setText(lastSeenTime); } } @Override public void onCancelled(@NonNull DatabaseError databaseError) { } }); mRootRef.child("Chat").child(mCurrentUserId).addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { if(!dataSnapshot.hasChild(mChatUser)){ Map chatAddMap = new HashMap(); chatAddMap.put("seen", false); chatAddMap.put("timestamp", ServerValue.TIMESTAMP); Map chatUserMap = new HashMap(); chatUserMap.put("Chat/" + mCurrentUserId + "/" + mChatUser, chatAddMap); //add map to current user chatUserMap.put("Chat/" + mChatUser + "/" + mCurrentUserId, chatAddMap); mRootRef.updateChildren(chatUserMap, new DatabaseReference.CompletionListener() { @Override public void onComplete(@Nullable DatabaseError databaseError, @NonNull DatabaseReference databaseReference) { if(databaseError != null){ //Log.d(TAG, "onComplete: CHAT LOG", databaseError.getMessage()); Log.e(TAG, "onComplete: CHAT_LOG" + databaseError.getMessage().toString()); } } }); } } @Override public void onCancelled(@NonNull DatabaseError databaseError) { } }); mChatSendBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sendMessage(); } }); // open gallery to get an image mChatAddBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent galleryIntent = new Intent(); galleryIntent.setType("image/*"); galleryIntent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(Intent.createChooser(galleryIntent,"SELECT IMAGE"), GALLERY_PICK); } }); //pagination mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { mCurrentPage++; // each time it loads a new page should go to position zero itemPos = 0; loadMoreMessages(); } }); } /** * load more messages everytime uesr refreshes */ private void loadMoreMessages() { DatabaseReference messageRef = mRootRef.child("messages").child(mCurrentUserId).child(mChatUser); Query messageQuery = messageRef.orderByKey().endAt(mLastKey).limitToLast(10); messageQuery.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) { Messages message = dataSnapshot.getValue(Messages.class); String messageKey = dataSnapshot.getKey(); if(!mPrevKey.equals(messageKey)){ messagesList.add(itemPos++, message); Log.d(TAG, "onChildAdded: xx" + mPrevKey + "---" + mLastKey); } else { mPrevKey = mLastKey; } if(itemPos == 1){ mLastKey = messageKey; } Log.d(TAG, "onChildAdded: TOTALKEUS" + "lastkey: " + mLastKey + " | Prev key : " + mPrevKey + " | Message Key : " + messageKey); mAdapter.notifyDataSetChanged(); mRefreshLayout.setRefreshing(false); mLinearLayout.scrollToPositionWithOffset(itemPos , 0); } @Override public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) { } @Override public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) { } @Override public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) { } @Override public void onCancelled(@NonNull DatabaseError databaseError) { } }); } /** * method used to load messages once */ private void loadMessages() { //query to get pagination and last 10 messages DatabaseReference messageRef = mRootRef.child("messages").child(mCurrentUserId).child(mChatUser); Query messageQuery = messageRef.limitToLast(mCurrentPage * TOTAL_ITEMS_TO_LOAD); messageQuery.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) { Messages message = dataSnapshot.getValue(Messages.class); itemPos++; if(itemPos == 1){ // get the key to be user as a start point when loading more items String messageKey = dataSnapshot.getKey(); mLastKey = messageKey; mPrevKey = messageKey; Log.d(TAG, "onChildAdded: last " +mLastKey + " prev " + mPrevKey + " messa " + messageKey + "-----------"); } messagesList.add(message); mAdapter.notifyDataSetChanged(); //pagination - define the bottom of the recycler view //automatic scroll to bottom mMessagesList.scrollToPosition(messagesList.size() - 1); mRefreshLayout.setRefreshing(false); } @Override public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) { } @Override public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) { } @Override public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) { } @Override public void onCancelled(@NonNull DatabaseError databaseError) { } }); } /** * send photo message * @param requestCode * @param resultCode * @param data */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(requestCode == GALLERY_PICK & resultCode == RESULT_OK){ Uri imageUri = data.getData(); final String current_user_ref = "messages/" + mCurrentUserId + "/" + mChatUser; final String chat_user_ref = "messages/" + mChatUser + "/" + mCurrentUserId; DatabaseReference user_message_push = mRootRef.child("messages") .child(mCurrentUserId).child(mChatUser).push(); final String push_id = user_message_push.getKey(); StorageReference filepath = mImageStorage.child("message_images").child( push_id + ".jpg"); filepath.putFile(imageUri).addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() { @Override public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) { if(task.isSuccessful()){ Task<Uri> result = task.getResult().getMetadata().getReference().getDownloadUrl(); result.addOnSuccessListener(new OnSuccessListener<Uri>() { @Override public void onSuccess(Uri uri) { String download_url = uri.toString(); Map messageMap = new HashMap(); messageMap.put("message", download_url); messageMap.put("seen", false); messageMap.put("type", "image"); messageMap.put("time", ServerValue.TIMESTAMP); messageMap.put("from", mCurrentUserId); messageMap.put("to", mChatUser); messageMap.put("messageID", push_id ); Map messageUserMap = new HashMap(); messageUserMap.put(current_user_ref + "/" + push_id, messageMap); messageUserMap.put(chat_user_ref + "/" + push_id, messageMap); mChatMessageView.setText(""); mRootRef.updateChildren(messageUserMap, new DatabaseReference.CompletionListener() { @Override public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) { if(databaseError != null){ Log.d("CHAT_LOG", databaseError.getMessage().toString()); } } }); } }); } } }); } } } ``
ChatActivity.java
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MessageViewHolder>{ public class MessageViewHolder extends RecyclerView.ViewHolder { public TextView messageText, displayName; public CircleImageView profileImage; public ImageView messageImage; public MessageViewHolder(View itemView) { super(itemView); messageText = (TextView) itemView.findViewById(R.id.message_text_layout); profileImage = (CircleImageView) itemView.findViewById(R.id.message_profile_layout); displayName = (TextView) itemView.findViewById(R.id.name_text_layout); messageImage = (ImageView) itemView.findViewById(R.id.message_image_layout); } } private List<Messages> mMessageList; private FirebaseAuth mAuth; private DatabaseReference mUserDatabase; private DatabaseReference mUserDatabaseSettings; private String rul, fromUser_t; private UserAccountSettings mUserAccountSettings; public MessageAdapter(List<Messages> mMessageList) { this.mMessageList = mMessageList; } @Override public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.message_single_layout,parent,false); mAuth = FirebaseAuth.getInstance(); return new MessageViewHolder(v); } @Override public void onBindViewHolder(final MessageViewHolder holder, final int position) { final String current_user_id = mAuth.getCurrentUser().getUid(); final Messages c = mMessageList.get(position); final String from_user = c.getFrom(); final String message_type = c.getType(); final String messageContent = c.getMessage(); /** * popup for text message */ // check if the message is from the current user if(from_user.equals(current_user_id)) { holder.messageText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //popup menu to select option CharSequence options[] = new CharSequence[] { "Delete from my phone", "Delete for Everyone" , "Cancel" }; final AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext()); builder.setTitle("Message Options"); builder.setItems(options, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //Click Event for each item. if(i == 0) { deleteSentMessage(position, holder); // Intent intent = new Intent(holder.itemView.getContext(), ChatActivity.class); // holder.itemView.getContext().startActivity(intent); //MessageViewHolder.class.notify(); holder.messageText.setPaintFlags(holder.messageText.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); } else if(i == 1) { deleteMessageForEveryOne(position, holder); // Intent intent = new Intent(holder.itemView.getContext(), ChatActivity.class); // holder.itemView.getContext().startActivity(intent); //notifyDataSetChanged(); //holder.messageText.setVisibility(View.GONE); } } }); builder.show(); } }); }else { // similar code to check if the message is from the other user } holder.messageText.setVisibility(View.GONE); holder.messageImage.setVisibility(View.GONE); if("text".equals(message_type)) { holder.messageText.setVisibility(View.VISIBLE); if(from_user.equals(current_user_id)) { holder.messageText.setBackgroundResource(R.drawable.message_text_background); holder.messageText.setTextColor(Color.WHITE); //todo left right not working holder.messageText.setGravity(Gravity.LEFT); }else { holder.messageText.setBackgroundColor(Color.WHITE); holder.messageText.setTextColor(Color.BLACK); holder.messageText.setGravity(Gravity.RIGHT); } holder.messageText.setText(c.getMessage()); }else { holder.messageImage.setVisibility(View.VISIBLE); UniversalImageLoader.setImage(messageContent,holder.messageImage,null,""); } } @Override public int getItemCount() { return mMessageList.size(); } /** function to Delete from my phone **/ private void deleteSentMessage(final int position, final MessageViewHolder holder) { DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference(); rootRef.child("messages") .child(mMessageList.get(position).getFrom()) .child(mMessageList.get(position).getTo()) .child(mMessageList.get(position).getMessageID()) .removeValue().addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if(task.isSuccessful()) { Toast.makeText(holder.itemView.getContext(), "Deleted Successfully.", Toast.LENGTH_SHORT).show(); }else { Toast.makeText(holder.itemView.getContext(), "Error Occurred.", Toast.LENGTH_SHORT).show(); } } }); } /** function to Delete from receiver phone **/ private void deleteReceiveMessage(final int position, final MessageViewHolder holder) { DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference(); rootRef.child("messages") .child(mMessageList.get(position).getTo()) .child(mMessageList.get(position).getFrom()) .child(mMessageList.get(position).getMessageID()) .removeValue().addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if(task.isSuccessful()) { Toast.makeText(holder.itemView.getContext(), "Deleted Successfully.", Toast.LENGTH_SHORT).show(); }else { Toast.makeText(holder.itemView.getContext(), "Error Occurred.", Toast.LENGTH_SHORT).show(); } } }); } /** function to Delete from both sides **/ private void deleteMessageForEveryOne(final int position, final MessageViewHolder holder) { final DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference(); rootRef.child("messages") .child(mMessageList.get(position).getTo()) .child(mMessageList.get(position).getFrom()) .child(mMessageList.get(position).getMessageID()) .removeValue().addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if(task.isSuccessful()) { rootRef.child("messages") .child(mMessageList.get(position).getFrom()) .child(mMessageList.get(position).getTo()) .child(mMessageList.get(position).getMessageID()) .removeValue().addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if(task.isSuccessful()) { Toast.makeText(holder.itemView.getContext(), "Deleted Successfully.", Toast.LENGTH_SHORT).show(); } } }); }else { Toast.makeText(holder.itemView.getContext(), "Error Occurred.", Toast.LENGTH_SHORT).show(); } } }); } }
3 Réponses :
Vous utilisez un ChildEventListener
dans votre messageQuery
, qui a ces méthodes principales:
onChildAdded
, qui est appelé initialement pour chaque nœud enfant correspondant à votre requête messageQuery
, puis lorsqu'un nœud enfant est ajouté à la base de données qui appartient à messageQuery
.onChildRemoved
, qui est appelé lorsqu'un nœud enfant (qui tombe) est supprimé de la base de données (ou du moins de la partie que messageQuery
écoute).onChildChanged
, qui est appelée lorsqu'un nœud enfant messageQuery
par messageQuery
est modifié dans la base de données. Si je regarde votre implémentation, vous n'avez implémenté que onChildAdded
. Ainsi, lorsque vous supprimez un nœud enfant de la base de données, Firebase en informe votre ChildEventListener
, mais cela ne fait rien.
Pour supprimer le message de l'interface utilisateur lorsqu'il est supprimé de la base de données, vous devrez implémenter:
@Override public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) { // TODO: remove the message matching dataSnapshot from messagesList // TODO: call adapter.notifyDataSetChanged() so that the UI gets updates }
Notifydatasetchanged dans l'application de chat sera lourd car il devra tout passer. Peut-être que vous devriez le changer en notifyItemRemoved afin que cette personne recherche comment notifier l'adaptateur et tout.
Merci Frank et Vedprakash. C'était une évidence. À ce moment-là, je ne comprenais pas comment passer la position et supprimer l'élément en utilisant onChildRemoved. Maintenant, je comprends. Ce que j'ai fait était basé sur cet article: Comment utiliser onChildRemoved dans Firebase Realtime Database?
@Override public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) { Toast.makeText(ChatActivity.this, "Entered on this part!", Toast.LENGTH_SHORT).show(); int index = keyList.indexOf(dataSnapshot.getKey()); messagesList.remove(index); keyList.remove(index); //mAdapter.notifyDataSetChanged(); mAdapter.notifyItemRemoved(index); }
Je l'ai testé et fonctionne très bien. Cependant, il y a un petit problème. Mon chat charge 10 éléments (définis par: private static final int TOTAL_ITEMS_TO_LOAD = 10; ). Si l'utilisateur fait défiler, il chargera plus d'éléments.
Lorsque j'ouvre la page de discussion, les éléments chargés vont de "2" à "11" Mais, lorsque je supprime l'élément, par exemple, "9" l'élément "1" apparaît. Cela signifie que l'élément juste avant la position zéro de la liste messages apparaîtra.
J'ai essayé plusieurs choses. Mon dernier essai a été défini sur mLinearLayout.scrollToPosition, diminuez le TOTAL_ITEMS_TO_LOAD. Et le problème reste vivant. :(
Avant de supprimer
Après avoir supprimé l'élément
J'ai besoin de quelques conseils pour résoudre ce problème. Je suis un peu perdu. Quelqu'un peut vous aider?
La liste change de taille, vous devez donc donner une nouvelle taille de liste à votre logique.
Si vous envisagez à un moment donné d'essayer d'utiliser Cloud Firestore , vous trouverez ici un didacticiel sur la création d'une application de chat Firestore complète et fonctionnelle.
Salut Alex, Merci pour votre suggestion!