user14016240
user14016240

Reputation:

Why does recyclerview ScrollToBottom is not working efficiently?

I thoroughly searched nd tried to implement diff diff things, but it seems not possible,

Requirement: In my chat app, when I open particular user chat screen

  1. It should be scrolled to bottom so that user can see last msg/conversation
  2. When any new msg/new item will add at that time it should scroll to bottom

Please note, if I'll use below methods then my 1st requirement is not working

private void loadMessages() {
        try {
            DatabaseReference messageReference = rootReference.child(MESSAGES).child(currentUId).child(chatUser);
            Query query = messageReference.orderByChild(TIME);

            ValueEventListener valueEventListener = new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    messagesList.clear();
                    for (DataSnapshot ds : dataSnapshot.getChildren()) {
                        try {
                            Messages messages = ds.getValue(Messages.class);
                            messagesList.add(messages);
                            setAdapter();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {
                    databaseError.getMessage();
                }
            };
            query.addValueEventListener(valueEventListener);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

private void setAdapter() {
        try {
            if (mAdapter != null) {
                mLayoutManager.smoothScrollToPosition(binding.recyclerViewLayout.recyclerView, null, messagesList.size());
                mAdapter.notifyDataSetChanged();
            } else {
                mAdapter = new MessageAdapter(this, messagesList);
                mLayoutManager.setStackFromEnd(false);
                mLayoutManager.setSmoothScrollbarEnabled(true);
                binding.recyclerViewLayout.recyclerView.setLayoutManager(mLayoutManager);
                binding.recyclerViewLayout.recyclerView.setAdapter(mAdapter);
                binding.recyclerViewLayout.recyclerView.setVisibility(View.VISIBLE);
            }
        } catch (Exception exp) {
            exp.printStackTrace();
        }
    }

And if I'll use below method, then my 2nd requirement is not working!

private void loadMessages() {
            try {
                DatabaseReference messageReference = rootReference.child(MESSAGES).child(currentUId).child(chatUser);
                Query query = messageReference.orderByChild(TIME);
    
                ValueEventListener valueEventListener = new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        messagesList.clear();
                        for (DataSnapshot ds : dataSnapshot.getChildren()) {
                            try {
                                Messages messages = ds.getValue(Messages.class);
                                messagesList.add(messages);
                                Comparator<Messages> compare = Collections.reverseOrder();
                                Collections.sort(messagesList, compare);
                                setAdapter();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
    
                    @Override
                    public void onCancelled(DatabaseError databaseError) {
                        databaseError.getMessage();
                    }
                };
                query.addValueEventListener(valueEventListener);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    
        private void setAdapter() {
            try {
                if (mAdapter != null) {
                    mAdapter.notifyDataSetChanged();
                } else {
                    mAdapter = new MessageAdapter(this, messagesList);
                    LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
                    mLayoutManager.setReverseLayout(true);
                    mLayoutManager.setStackFromEnd(true);
                    mLayoutManager.setSmoothScrollbarEnabled(true);
                    binding.recyclerViewLayout.recyclerView.setLayoutManager(mLayoutManager);
                    binding.recyclerViewLayout.recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
                    binding.recyclerViewLayout.recyclerView.setAdapter(mAdapter);
                    binding.recyclerViewLayout.recyclerView.setVisibility(View.VISIBLE);
                }
            } catch (Exception exp) {
                exp.printStackTrace();
            }
        }

Please pay attention on setAdapter() as it is calling on for each loop, first time it is creating adapter and after that immediately with the next increment of loop, it calls notifydatasetchanged.

EDIT:

Complete code: https://drive.google.com/file/d/11W8jL4_c9vVxVjZM9xBXo4KzH5LFViUn/view?usp=sharing

Upvotes: 0

Views: 126

Answers (1)

Ali
Ali

Reputation: 529

Edit 4:

  1. Got it working (by adding a function addItem() in Adapter, notifyItemInserted(messagesList.size()); in addItem() function

  2. Added recyclerView.scrollToPosition(mAdapter.getItemCount() - 1); right after populating the List<Messages>

3.After every sendButtonClickEvent() added recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);

NOTE: The complete code is in the link given below.

MainActivity

package studio.devcode.recyclerviewdemo;

import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

import studio.devcode.recyclerviewdemo.MessageAdapter;
import studio.devcode.recyclerviewdemo.Messages;
import studio.devcode.recyclerviewdemo.R;

public class MainActivity extends AppCompatActivity {
    private ImageView imageViewSend;
    private EditText editTextMessage;
    private List<Messages> messagesList;
    private MessageAdapter mAdapter;
    private LinearLayoutManager mLayoutManager;
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mLayoutManager = new LinearLayoutManager(this);
        recyclerView = findViewById(R.id.recycler_view_layout);
        messagesList = new ArrayList<>();

        imageViewSend = findViewById(R.id.send);
        editTextMessage = findViewById(R.id.type_message);

        setData("12345", "abc");
        setData("12345", "hello");
        setData("123145", "you");
        setData("12345", "there");
        setData("121345", "aqqqbc");
        setData("123145", "abqqc");
        setData("123145", "aqqbc");
        setData("12345", "awwbc");
        setData("123145", "eeabc");
        setData("12345", "abeeec");
        setData("112345", "addbc");
        setData("12345", "abdcc");
        setData("112345", "abccc");
        setData("121345", "abccc");
        setData("12345", "abccc");
        setAdapter();

        recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
        onSendButtonClick();
        onSoftInputKeyboardOpen();
    }

    private void setData(String from, String message) {
        messagesList.add(new Messages(message, "text", from));
    }


    private void onSendButtonClick(){
        imageViewSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(!editTextMessage.getText().toString().isEmpty() || !editTextMessage.getText().toString().equals(" ")){
                    mAdapter.addItem(new Messages(editTextMessage.getText().toString().toUpperCase(), "text", "Ali"));
                    editTextMessage.setText("");
                    recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
                }
            }
        });
    }


    private void setAdapter() {
        try {
            if (mAdapter != null) {
                mAdapter.notifyDataSetChanged();
                recyclerView.scrollToPosition(mAdapter.getItemCount());


            } else {
                mAdapter = new MessageAdapter(this, messagesList, "12345");
                mLayoutManager.setSmoothScrollbarEnabled(true);
                mLayoutManager.setStackFromEnd(true);
                recyclerView.setLayoutManager(mLayoutManager);
                recyclerView.setAdapter(mAdapter);
                recyclerView.setVisibility(View.VISIBLE);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void onSoftInputKeyboardOpen(){
        recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
             @Override
             public void onLayoutChange(View v,
                                        int left, int top, int right, int bottom,
                                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
                 if (bottom < oldBottom) {
                     recyclerView.postDelayed(new Runnable() {
                         @Override
                         public void run() {
                             recyclerView.smoothScrollToPosition(
                                     recyclerView.getAdapter().getItemCount() - 1);
                         }
                     }, 100);
                 }
             }
         });
    }
}

Messages

package studio.devcode.recyclerviewdemo;

public class Messages {
    private String message;
    private String type;
    private String from;
    private long time;
    private boolean seen;

    public Messages(String message,String type,String from) {
        this.message = message;
        this.from = from;
        this.type = type;
    }

    public String getMessage() {
        return message;
    }

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

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }
}

MessageAdapter

package studio.devcode.recyclerviewdemo;

import android.content.Context;
import android.os.Build;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;



import java.util.List;



public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MyViewHolder> {

    private Context context;
    private List<Messages> messagesList;
    private String currentUid;

    public MessageAdapter(Context context, List<Messages> messagesList, String currentUid) {
        try {
            this.currentUid = currentUid;
            this.messagesList = messagesList;
            this.context = context;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = null;
        MyViewHolder myViewHolder;
        try {
            view = LayoutInflater.from(parent.getContext()).inflate(
                    R.layout.message_single_layout, parent, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        myViewHolder = new MyViewHolder(view);
        return myViewHolder;
    }

    protected void showLog(String msg) {
        Log.d(this.getClass().getSimpleName(), msg);
    }

    protected boolean checkNull(String data) {
        return data != null && (!data.isEmpty());
    }

    @Override
    public void onBindViewHolder(@NonNull final MyViewHolder holder, int position) {
        try {
            Messages messages = messagesList.get(position);
            if (messages != null) {
                String messageType = messages.getType();
                if (checkNull(messages.getFrom()) && messages.getFrom().equals(currentUid)) {
                    if (checkNull(messageType) && messageType.equals("text")) {
                        holder.chatText_1.setText(messages.getMessage());
                        holder.messageSingleLayout.setVisibility(View.GONE);
                        holder.messageSingleLayout_1.setVisibility(View.VISIBLE);
                    }
                } else {
                    if (checkNull(messageType) && messageType.equals("text")) {
                        holder.chatText.setText(messages.getMessage());
                        holder.messageSingleLayout.setVisibility(View.VISIBLE);
                        holder.messageSingleLayout_1.setVisibility(View.GONE);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

    public static class MyViewHolder extends RecyclerView.ViewHolder {
        public TextView chatText, chatText_1, chatTime, chatTime_1;
        public LinearLayout messageSingleLayout, messageSingleLayout_1;
        private final ImageView fileDownload;
        private final ImageView fileDownload_1;

        public MyViewHolder(View itemView) {
            super(itemView);
            chatText = itemView.findViewById(R.id.chatText);
            chatText_1 = itemView.findViewById(R.id.chatText_1);
            chatTime = itemView.findViewById(R.id.chatTime);
            chatTime_1 = itemView.findViewById(R.id.chatTime_1);
            messageSingleLayout = itemView.findViewById(R.id.messageSingleLayout);
            messageSingleLayout_1 = itemView.findViewById(R.id.messageSingleLayout_1);
            fileDownload = itemView.findViewById(R.id.fileDownload);
            fileDownload_1 = itemView.findViewById(R.id.fileDownload_1);

        }
    }

    public void addItem(Messages message) {
        messagesList.add(message);
        notifyItemInserted(messagesList.size());
    }


}

Please give a star and also follow on GitHub :D

Github Repo to complete Demo

Vido URL

Upvotes: 1

Related Questions