0

I am trying to build chat feature in my android app. I am using the Recycler view to show the chat history. But when I call the RecyclerView adapter, it only calls the constructor and the getItemCount() method which returns 4 (which is correct). Can someone please help me. Here's the code.

I read that calling the setLayoutManager() after the set adapter method could fix the problem but it didn't work for me.

Already tried this RecyclerView not calling onCreateViewHolder or onBindView

    public class MessageListActivity extends AppCompatActivity {

    private static final int ACTIVITY_NUM = 1;


    private RecyclerView mMessageRecycler;
    private MessageListAdapter mMessageAdapter;
    private Button mSendButton;
    private EditText mTextbox;

    List<Message> messageList = new ArrayList<>();

    private ChatListItem chat;
    Context context;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_message_list);

        Intent i = getIntent();
        chat = (ChatListItem) i.getSerializableExtra("CHAT");

        mMessageRecycler = (RecyclerView) findViewById(R.id.reyclerview_message_list);

        mSendButton = (Button) findViewById(R.id.button_chatbox_send);
        mTextbox = (EditText) findViewById(R.id.edittext_chatbox);

        sendMessage(mSendButton);

        //creating bottom navigation view
        setupBottomNavigationView();

        DatabaseReference mRef = FirebaseDatabase.getInstance().getReference().child("Chat").child(chat.getChatId());
        mRef.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

                Iterable<DataSnapshot> children = dataSnapshot.getChildren();
                for(DataSnapshot child: children){

                    messageList.add(child.getValue(Message.class));
                }

                Log.d("testing0 ListActivity", messageList.toString());

                mMessageAdapter = new MessageListAdapter(MessageListActivity.this, messageList);
                mMessageRecycler.setAdapter(mMessageAdapter);
                mMessageRecycler.setLayoutManager(new LinearLayoutManager(getBaseContext()));

            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });

    }

    /**
     * BottomNavigationView setup
     */
    private void setupBottomNavigationView() {
        Log.d("MessageListActivity", "setupBottomNavigationView: setting up BottomNavigationView");
        BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavViewBar);
        BottomNavigationViewHelper.enableNavigation(MessageListActivity.this,bottomNavigationView);
        Menu menu = bottomNavigationView.getMenu();
        MenuItem menuItem = menu.getItem(ACTIVITY_NUM);
        menuItem.setChecked(true);
    }

    private void sendMessage(Button button){
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String content = mTextbox.getText().toString().trim();
                if (content.length() > 0) {

                    Message newMessage = new Message();
                    newMessage.setTextMessage(mTextbox.getText().toString());
                    newMessage.setIdSender(chat.getCustomerPhone());
                    newMessage.setIdReceiver(chat.getDriverPhone());
                    newMessage.setTimestamp(System.currentTimeMillis());
                    FirebaseDatabase.getInstance().getReference().child("Chat").child(chat.getChatId()).push().setValue(newMessage);

                    mTextbox.setText("");
                }
            }
        });

    }

}

    public class MessageListAdapter extends RecyclerView.Adapter {

    private static final int VIEW_TYPE_MESSAGE_SENT = 1;
    private static final int VIEW_TYPE_MESSAGE_RECEIVED = 2;

    private Context mContext;
    //private List<BaseMessage> mMessageList;
    private List<Message> mMessageList;

    //get current user
    private String phone_Number;
    private FirebaseAuth mauth = FirebaseAuth.getInstance();
    private FirebaseUser user = mauth.getCurrentUser();

    public MessageListAdapter(Context context, List<Message> messageList) {

        Log.d("testing3 messageAdapter", "inside constructor");

        mContext = context;
        mMessageList = messageList;
    }

    @Override
    public int getItemCount() {

        Log.d("testing4 messageAdapter", String.valueOf(mMessageList.size()));

        return mMessageList.size();
    }


    // Determines the appropriate ViewType according to the sender of the message.
    @Override
    public int getItemViewType(int position) {

        Log.d("testing0 messageAdaper", "inside messageListAdapter");

        Message message = (Message) mMessageList.get(position);

        phone_Number = user.getPhoneNumber();

        if (message.getIdSender().equals(phone_Number)) {
            // If the current user is the sender of the message
            return VIEW_TYPE_MESSAGE_SENT;
        } else {
            // If some other user sent the message
            return VIEW_TYPE_MESSAGE_RECEIVED;
        }
    }

    // Inflates the appropriate layout according to the ViewType.
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;

        Log.d("testing1 messageAdapter", "inside onCreateViewHolder");

        if (viewType == VIEW_TYPE_MESSAGE_SENT) {
            view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.item_message_sent, parent, false);
            return new SentMessageHolder(view);
        } else if (viewType == VIEW_TYPE_MESSAGE_RECEIVED) {
            view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.item_message_received, parent, false);
            return new ReceivedMessageHolder(view);
        }

        return null;
    }

    // Passes the message object to a ViewHolder so that the contents can be bound to UI.
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        Message message = (Message) mMessageList.get(position);

        Log.d("testing2 messageAdaper", "inside onBindViewHolder");

        switch (holder.getItemViewType()) {
            case VIEW_TYPE_MESSAGE_SENT:
                ((SentMessageHolder) holder).bind(message);
                break;
            case VIEW_TYPE_MESSAGE_RECEIVED:
                ((ReceivedMessageHolder) holder).bind(message);
        }
    }

    private class SentMessageHolder extends RecyclerView.ViewHolder {
        TextView messageText, timeText;

        SentMessageHolder(View itemView) {
            super(itemView);

            messageText = (TextView) itemView.findViewById(R.id.text_message_body);
            timeText = (TextView) itemView.findViewById(R.id.text_message_time);
        }

        void bind(Message message) {
            messageText.setText(message.getTextMessage());

            // Format the stored timestamp into a readable String using method.
            timeText.setText(new SimpleDateFormat("hh:mm KK").format(new Date(message.getTimestamp())));

        }
    }

    private class ReceivedMessageHolder extends RecyclerView.ViewHolder {
        TextView messageText, timeText;

        ReceivedMessageHolder(View itemView) {
            super(itemView);

            messageText = (TextView) itemView.findViewById(R.id.text_message_body);
            timeText = (TextView) itemView.findViewById(R.id.text_message_time);
        }

        void bind(Message message) {

            messageText.setText(message.getTextMessage());
            timeText.setText(new SimpleDateFormat("hh:mm KK").format(new Date(message.getTimestamp())));
        }
    }

XML

    <?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MessageActivity.MessageListActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/reyclerview_message_list"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent">
    </androidx.recyclerview.widget.RecyclerView>

    <!-- A horizontal line between the chatbox and RecyclerView -->
    <View
        android:layout_width="0dp"
        android:layout_height="2dp"
        android:background="#dfdfdf"
        android:layout_marginBottom="0dp"
        app:layout_constraintBottom_toTopOf="@+id/layout_chatbox"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

    <LinearLayout
        android:id="@+id/layout_chatbox"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:minHeight="48dp"
        android:background="#ffffff"
        app:layout_constraintBottom_toTopOf="@id/navBarContainer"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent">

        <EditText
            android:id="@+id/edittext_chatbox"
            android:hint="Enter message"
            android:background="@android:color/transparent"
            android:layout_gravity="center"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:maxLines="6"/>

        <Button
            android:id="@+id/button_chatbox_send"
            android:text="SEND"
            android:textSize="14dp"
            android:background="?attr/selectableItemBackground"
            android:clickable="true"
            android:layout_width="64dp"
            android:layout_height="48dp"
            android:gravity="center"
            android:layout_gravity="bottom" />

    </LinearLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/navBarContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent">

        <include layout="@layout/layout_navbar" />


    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

Maria Ahmed
  • 111
  • 4

2 Answers2

0

Try setting LayoutManager before setting the adapter

mMessageRecycler.setLayoutManager(new LinearLayoutManager(MessageListActivity.this,RecyclerView.VERTICAL,false));
mMessageRecycler.setAdapter(mMessageAdapter);
Manoj Perumarath
  • 9,337
  • 8
  • 56
  • 77
0

Setup your RecyclerView in onCreate(), not in the ValueEventListener callback:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    mMessageAdapter = new MessageListAdapter(MessageListActivity.this, messageList);
    mMessageRecycler.setAdapter(mMessageAdapter);
    mMessageRecycler.setLayoutManager(new LinearLayoutManager(getBaseContext()));

Then in the ValueEventListener callback just add the messages to your list and call mMessageAdapter.notifyDatasetChanged():

@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
    Iterable<DataSnapshot> children = dataSnapshot.getChildren();
    for(DataSnapshot child: children){
        messageList.add(child.getValue(Message.class));
    }

    Log.d("testing0 ListActivity", messageList.toString());
    mMessageAdapter.notifyDatasetChanged();
}
Tidder
  • 1,126
  • 1
  • 7
  • 15