Patrick Kariuki
Patrick Kariuki

Reputation: 9

RecyclerView populating each item every time i add a new item

I'm trying to make a replica chat app and I have a list that I need populated into the recyclerview. I'm getting data from firebase realtime database and every time I receive or actually send a message, All the previous item(messages) plus the new one are repopulated/duplicated into the recycler view.

What I have tried I have tried using .cleaar() method on my list before adding a new item to the list but now all other items in the recycler view disappear

here's my adapter

public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    public static final int MSG_TYPE_LEFT = 0;
    public static final int MSG_TYPE_RIGHT = 1;
    FirebaseUser firebaseUser;


    private Context ctx;
    private List<Messages> msgsR, msgsS;//ignore unused
    private ArrayList<Messages> dataSet;

    public MessageAdapter(Context context) {
        this.ctx = context;
        this.dataSet = new ArrayList<>();
        //this.msgsR = messagesReceived;
    }


    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (viewType == Messages.SENT_TYPE) {
            View view = LayoutInflater.from(ctx).inflate(R.layout.message_item_right, parent, false);
            return new ViewHolderS(view);
        }
        if (viewType == Messages.RECEIVED_TYPE) {
            View view = LayoutInflater.from(ctx).inflate(R.layout.message_item_left, parent, false);
            return new ViewHolderR(view);
        }
        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

        Messages object = dataSet.get(position);
        if (object != null) {
            switch (object.type) {
                case Messages.RECEIVED_TYPE:
                    ((ViewHolderR) holder).populate(object, position);
                    break;
                case Messages.SENT_TYPE:
                    ((ViewHolderS) holder).populate(object, position);
                    break;
            }
        }


    }
//recceives messages object to populate into list
//it does not matter where i put the .clear() method, after or below dataset.add() its still undesireable
    public void addMessageSent(Messages messages){
        dataSet.clear();
        dataSet.add(messages);
       // notifyItemInserted(dataSet.size()-1);
        //notifyItemRangeChanged(dataSet.size()-1, dataSet.size());

    }

    @Override
    public int getItemCount() {
        return dataSet.size();
    }

    // sent messages are handled here
    public static class ViewHolderS extends RecyclerView.ViewHolder {
        public TextView msg, time;
        public LinearLayout layout;

        public ViewHolderS(@NonNull View itemView) {
            super(itemView);

            layout = itemView.findViewById(R.id.cont);
            msg = itemView.findViewById(R.id.send_msg);
            time = itemView.findViewById(R.id.time);

        }

        private void populate(Messages messages, int position) {
            msg.setText(messages.getMessage());
            msg.setPadding(6, 4, 18, 4);
            msg.setMinWidth(100);
            msg.setMaxWidth(400);
            LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) msg.getLayoutParams();
            layoutParams.gravity = Gravity.START;
            layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT;
            layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT;
            layoutParams.topMargin = layoutParams.bottomMargin = layoutParams.rightMargin = 10;
            layoutParams.leftMargin = 20;

            msg.setLayoutParams(layoutParams);
            time.setText(messages.getTime());
        }
    }

    @Override
    public int getItemViewType(int position) {
        switch (dataSet.get(position).type) {
            case 0:
                return Messages.SENT_TYPE;
            case 1:
                return Messages.RECEIVED_TYPE;

            default:
                return -1;
        }

       
    }

// received messages are handled here
    private class ViewHolderR extends ViewHolderS {
        public TextView msg, time;
        public LinearLayout layout;

        public ViewHolderR(@NonNull View itemView) {
            super(itemView);
            layout = itemView.findViewById(R.id.cont);
            msg = itemView.findViewById(R.id.send_msg);
            time = itemView.findViewById(R.id.time);
        }

        private void populate(Messages messages, int position) {

            msg.setText(messages.getMessage());
            msg.setPadding(6, 4, 18, 4);
            msg.setMinWidth(100);
            msg.setMaxWidth(400);
            LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) msg.getLayoutParams();
            layoutParams.gravity = Gravity.START;
            layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT;
            layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT;
            layoutParams.topMargin = layoutParams.bottomMargin = layoutParams.rightMargin = 10;
            layoutParams.leftMargin = 20;

            msg.setLayoutParams(layoutParams);
            time.setText(messages.getTime());
        }
    }
}

and here's my data model

public class Messages {
    private String message;
    public static final int SENT_TYPE=0;
    public static final int RECEIVED_TYPE=1;
    public static final int AUDIO_TYPE=2;
    private long time;
    public int type;

    private String id;
    private String receiver;

    public Messages(String message,long time, String sender,String receiver, int type) {
        this.message = message;
        this.time = time;
        this.id = sender;
        this.type = type;
        this.receiver = receiver;
    }

    public Messages() {
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getTime() {

        SimpleDateFormat output = new SimpleDateFormat("HH:mm", Locale.getDefault());
        return output.format(new Date(time));
    }

    public void setTime(long time) {
        this.time = time;
    }

    public String getSender() {
        return id;
    }

    public void setSender(String sender) {
        this.id = sender;
    }

    public String getReceiver() {
        return receiver;
    }

    public void setReceiver(String receiver) {
        this.receiver = receiver;
    }
}

and below is activity class where I set the adapter and fill the list

recyclerView.setHasFixedSize(true);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
        linearLayoutManager.setStackFromEnd(true);
        recyclerView.setLayoutManager(linearLayoutManager);

list= new ArrayList();

        messageAdapter = new MessageAdapter(PersonChatActivity.this);
        recyclerView.setAdapter(messageAdapter);
        recyclerView.setItemAnimator(new DefaultItemAnimator());

if (Objects.requireNonNull(recyclerView.getAdapter()).getItemCount() > 0) {
            recyclerView.smoothScrollToPosition(recyclerView.getAdapter().getItemCount());
        }

//get sent messages from firebase
    private void getmessages() {
        DatabaseReference reference = database.getReference("Chats");
        reference.keepSynced(true);
        reference.child(senderId).child(receiver).push();
        reference.child(senderId).child(receiver).addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                messageSent.clear();
                for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
                    if (dataSnapshot.getValue() != null) {
                        String message = (String) dataSnapshot.child("message").getValue();
                        long time = (Long) dataSnapshot.child("time").getValue();
                        String senderId = (String) dataSnapshot.child("id").getValue();
                        String receiverId = (String) dataSnapshot.child("receiver").getValue();
                        assert firebaseUser != null;
                        String user = firebaseUser.getUid();
                        Messages msg = new Messages(message, time, senderId, receiverId,Messages.SENT_TYPE);
                        String Uid = msg.getSender();
                        if (!Uid.isEmpty() && Uid.equals(user)) {
//pass the new message object to messages adapter to fill the list
                           messageAdapter.addMessageSent(msg);
                        }

                    }
                }

            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
            }
        });
    }

EDIT UPDATE

As suggested I have made the necessary changes and code works like magic I wont update the right changes into the question in case someone makes the same mistake as I have plus the comment marked as answer basically highlights the correct changes,,

NEW PROBLEM The new problem is, on adding object message to the addMessagesent() previously populated recyclerview items get replaced by the new data.

to make it easy, on receiving a new message, all the previous visible sent messagees disappear and are replaced by the new received messages

here is my getmessageReceived() method

        DatabaseReference reference = database.getReference("Chats");
        reference.keepSynced(true);
        reference.child(receiver).child(senderId).push();
        reference.child(receiver).child(senderId).addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                messageReceived.clear();
                messageAdapter.clearAllMessage();
                for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
                    if (dataSnapshot.getValue() != null) {
                        String message = (String) dataSnapshot.child("message").getValue();
                        long time = (Long) dataSnapshot.child("time").getValue();
                        String senderId = (String) dataSnapshot.child("id").getValue();
                        String receiverId = (String) dataSnapshot.child("receiver").getValue();
                        assert firebaseUser != null;
                        String user = firebaseUser.getUid();
                        Messages msg = new Messages(message, time, senderId, receiverId,Messages.RECEIVED_TYPE);
                        String Uid = msg.getReceiver();
                        if (!Uid.isEmpty() && Uid.equals(user)) {
                            messageAdapter.addMessageSent(msg);
                            messageAdapter.notifyDataSetChanged();

                        }
                    }

                }

            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
            }
        });
    }

Upvotes: -1

Views: 56

Answers (1)

AgentP
AgentP

Reputation: 7220

You should not clear the data set in the addMessageSent() just add new item to data set as shown below

  public void addMessageSent(Messages messages){  
        dataSet.add(messages);
    }

and create a new method to clear the dataset in your adapter

public void clearAllMessage(){  
            dataSet.clear();
        }

And in getmessages() call clearAllMessage() like this

private void getmessages() {
        DatabaseReference reference = database.getReference("Chats");
        reference.keepSynced(true);
        reference.child(senderId).child(receiver).push();
        reference.child(senderId).child(receiver).addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                messageSent.clear();
                 clearAllMessage();
                for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
                    if (dataSnapshot.getValue() != null) {
                        String message = (String) dataSnapshot.child("message").getValue();
                        long time = (Long) dataSnapshot.child("time").getValue();
                        String senderId = (String) dataSnapshot.child("id").getValue();
                        String receiverId = (String) dataSnapshot.child("receiver").getValue();
                        assert firebaseUser != null;
                        String user = firebaseUser.getUid();
                        Messages msg = new Messages(message, time, senderId, receiverId,Messages.SENT_TYPE);
                        String Uid = msg.getSender();
                        if (!Uid.isEmpty() && Uid.equals(user)) {
//pass the new message object to messages adapter to fill the list
                           messageAdapter.addMessageSent(msg);
                           messageAdapter.notifyDataSetChanged();  // Call this also 
                        }

                    }
                }

            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
            }
        });
    }

Upvotes: 1

Related Questions