I'm doing a Group Chat with Firebase and currently I'm using a RecyclerView to display chat messages and I'm having a problem. When you open the app in the fragmented home and you go to chat activity and start chatting (adding elements to recycler view) all goes fine. But, when you go via the NavigationDrawer to another fragment and get back to the chat fragment using again this Navigation Drawer. When you add one element in the chat it appears all in the blank it just displays the last message. Anybody knows why does this happens?
Here I leave the RecyclerView Adapter Code:
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MessageAdapterViewHolder> {
private final Context context;
List<Message> messageList;
public static final int MSG_TYPE_LEFT = 0;
public static final int MSG_TYPE_RIGHT = 1;
public MessageAdapter(Context context, List<Message> messageList) {
this.context = context;
this.messageList = messageList;
}
@NonNull
@NotNull
@Override
public MessageAdapterViewHolder onCreateViewHolder(@NonNull @NotNull ViewGroup parent, int viewType) {
View view;
if (viewType == MSG_TYPE_RIGHT) {
view = LayoutInflater.from(context).inflate(R.layout.fragment_chat_message_sent, parent, false);
} else {
view = LayoutInflater.from(context).inflate(R.layout.fragment_chat_message_received, parent, false);
}
return new MessageAdapterViewHolder(view);
}
@Override
public int getItemViewType(int position) {
FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
assert firebaseUser != null;
if (messageList.get(position).getName().equals(firebaseUser.getDisplayName())) {
return MSG_TYPE_RIGHT;
} else {
return MSG_TYPE_LEFT;
}
}
@Override
public void onBindViewHolder(@NonNull @NotNull MessageAdapterViewHolder holder, int position) {
Message message = messageList.get(position);
int viewType = holder.getItemViewType();
if (viewType == MSG_TYPE_LEFT) {
holder.textViewMessageUsername.setText(message.getName());
holder.textViewMessageUsername.setOnClickListener(v -> {
Intent intent = new Intent(context, UserProfileActivity.class);
Bundle bundle = new Bundle();
bundle.putString("userId", message.getUserId());
bundle.putString("username", message.getName());
intent.putExtras(bundle);
context.startActivity(intent);
});
} else {
holder.constraintLayoutMessageRight.setOnLongClickListener(v -> {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(R.string.delete_message)
.setPositiveButton(R.string.yes, (dialogInterface, i) -> deleteMessage(position))
.setNegativeButton(R.string.no, (dialogInterface, which) -> dialogInterface.dismiss());
builder.create();
builder.show();
return false;
});
}
holder.textViewMessageMessage.setText(message.getMessage());
holder.textViewMessageTime.setText(message.getCreatedTime());
}
@Override
public int getItemCount() {
return messageList.size();
}
public static class MessageAdapterViewHolder extends RecyclerView.ViewHolder {
ConstraintLayout constraintLayoutMessageRight;
TextView textViewMessageMessage;
TextView textViewMessageTime;
TextView textViewMessageUsername;
public MessageAdapterViewHolder(@NonNull @NotNull View itemView) {
super(itemView);
textViewMessageMessage = itemView.findViewById(R.id.textViewMessageMessage);
textViewMessageTime = itemView.findViewById(R.id.textViewMessageTime);
constraintLayoutMessageRight = itemView.findViewById(R.id.constraintLayoutMessageRight);
textViewMessageUsername = itemView.findViewById(R.id.textViewMessageUsername);
}
}
private void deleteMessage(int position) {
DatabaseReference rootReference = FirebaseDatabase.getInstance().getReference();
DatabaseReference messagesReference = rootReference.child("messages");
messagesReference.child(messageList.get(position).getKey()).removeValue();
}
}
Here I leave the ChatFragment code:
public class ChatFragment extends Fragment {
private FirebaseAuth auth;
private final DatabaseReference rootReference = FirebaseDatabase.getInstance().getReference();
private final DatabaseReference messagesReference = rootReference.child("messages");
private User user;
private List<Message> messages;
private RecyclerView recyclerViewMessageList;
private EditText editTextChatSendMessage;
private ImageView imageViewChatSendMessage;
private Context context;
private SharedPreferences sharedPreferences;
private final Gson gson = new Gson();
private MessageAdapter messageAdapter;
private ConstraintLayout loadingLayout;
private LottieAnimationView lottieAnimationViewLoading;
public ChatFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = getActivity();
assert context != null;
sharedPreferences = context.getSharedPreferences(Constants.sharedPreferencesDocName, Context.MODE_PRIVATE);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_chat, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
init();
bindUI(view);
setListeners();
}
private void bindUI(View view) {
recyclerViewMessageList = view.findViewById(R.id.recyclerViewMessageList);
editTextChatSendMessage = view.findViewById(R.id.editTextChatSendMessage);
imageViewChatSendMessage = view.findViewById(R.id.imageViewChatSendMessage);
user = gson.fromJson(sharedPreferences.getString("user", null), User.class);
loadingLayout = view.findViewById(R.id.loadingLayout);
lottieAnimationViewLoading = view.findViewById(R.id.lottieAnimationViewLoading);
setAdapter();
}
private void setListeners() {
recyclerViewMessageList.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
if (bottom < oldBottom) {
recyclerViewMessageList.post(() -> {
if (messages != null) {
recyclerViewMessageList.scrollToPosition(messages.size() - 1);
}
});
}
});
imageViewChatSendMessage.setOnClickListener(view -> {
if (!TextUtils.isEmpty(editTextChatSendMessage.getText().toString())) {
final Date currentTime = new Date();
@SuppressLint("SimpleDateFormat") SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm");
TimeZone timeZone = simpleDateFormat.getTimeZone();
simpleDateFormat.setTimeZone(timeZone);
String date = simpleDateFormat.format(currentTime);
final Message message = new Message(user.getId(), user.getUsername(), editTextChatSendMessage.getText().toString().trim(), date);
editTextChatSendMessage.setText("");
messagesReference.push().setValue(message);
messageAdapter.notifyDataSetChanged();
}
});
}
private void init() {
auth = FirebaseAuth.getInstance();
user = new User();
messages = new ArrayList<>();
}
private void setAdapter() {
loadingLayout.setVisibility(View.VISIBLE);
final FirebaseUser currentUser = auth.getCurrentUser();
assert currentUser != null;
messagesReference.limitToLast(100).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
final Message message = snapshot.getValue(Message.class);
assert message != null;
message.setKey(snapshot.getKey());
if (isNetworkConnected()) {
messages.add(message);
} else {
Toast.makeText(getActivity(), R.string.no_network_available, Toast.LENGTH_SHORT).show();
}
displayMessages(messages);
}
@Override
public void onChildChanged(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
Message message = snapshot.getValue(Message.class);
assert message != null;
message.setKey(snapshot.getKey());
List<Message> newMessages = new ArrayList<>();
for (Message m : messages) {
if (m.getKey().equals(message.getKey())) {
newMessages.add(message);
} else {
newMessages.add(m);
}
}
messages = newMessages;
displayMessages(messages);
}
@Override
public void onChildRemoved(@NonNull DataSnapshot snapshot) {
Message message = snapshot.getValue(Message.class);
assert message != null;
message.setKey(snapshot.getKey());
List<Message> newMessages = new ArrayList<>();
for (Message m : messages) {
if (!m.getKey().equals(message.getKey())) {
newMessages.add(m);
}
}
messages = newMessages;
displayMessages(messages);
}
@Override
public void onChildMoved(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
}
});
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
linearLayoutManager.setStackFromEnd(true);
recyclerViewMessageList.setLayoutManager(linearLayoutManager);
MessageAdapter messageAdapter = new MessageAdapter(getActivity(), messages);
recyclerViewMessageList.setAdapter(messageAdapter);
messageAdapter.notifyDataSetChanged();
recyclerViewMessageList.scrollToPosition(messages.size() - 1);
}
@Override
public void onStart() {
super.onStart();
}
@Override
public void onResume() {
super.onResume();
messages = new ArrayList<>();
}
private void displayMessages(List<Message> messages) {
messageAdapter = new MessageAdapter(getActivity(), messages);
recyclerViewMessageList.setAdapter(messageAdapter);
recyclerViewMessageList.scrollToPosition(messages.size() - 1);
messageAdapter.notifyDataSetChanged();
loadingLayout.setVisibility(View.GONE);
lottieAnimationViewLoading.cancelAnimation();
}
private boolean isNetworkConnected() {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected();
}
}
And finally, I leave the MainActivity code:
public class MainActivity extends AppCompatActivity {
private AppBarConfiguration mAppBarConfiguration;
private FirebaseAuth firebaseAuth;
private GoogleSignInClient googleSignInClient;
private SharedPreferences sharedPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = getApplicationContext();
sharedPreferences = context.getSharedPreferences(Constants.sharedPreferencesDocName, Context.MODE_PRIVATE);
firebaseAuth = FirebaseAuth.getInstance();
GoogleSignInOptions googleSignInOptions = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.build();
googleSignInClient = GoogleSignIn.getClient(this, googleSignInOptions);
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.appBarMain.toolbar);
DrawerLayout drawer = binding.drawerLayout;
NavigationView navigationView = binding.navView;
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_chat, R.id.nav_profile)
.setOpenableLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.logOut) {
FirebaseUser currentUser = firebaseAuth.getCurrentUser();
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
if (currentUser != null) {
firebaseAuth.signOut();
goToLoginActivity();
} else if (account != null) {
googleSignInClient.signOut().addOnCompleteListener(task -> goToLoginActivity());
}
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
return NavigationUI.navigateUp(navController, mAppBarConfiguration)
|| super.onSupportNavigateUp();
}
private void goToLoginActivity() {
sharedPreferences.edit().clear().apply();
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
startActivity(intent);
finish();
}
}
I have to mention that nothing is crashing in the debug console. That's why I don't put debug logs.
Edit:
If you don't understand the problem, I've made a video: Click here
To solve your problem you can just remove the OnResume method because you are initializing the array every time you change between fragments and that is the problem.
public class ChatFragment extends Fragment {
private FirebaseAuth auth;
private final DatabaseReference rootReference = FirebaseDatabase.getInstance().getReference();
private final DatabaseReference messagesReference = rootReference.child("messages");
private User user;
private List<Message> messages;
private RecyclerView recyclerViewMessageList;
private EditText editTextChatSendMessage;
private ImageView imageViewChatSendMessage;
private Context context;
private SharedPreferences sharedPreferences;
private final Gson gson = new Gson();
private MessageAdapter messageAdapter;
private ConstraintLayout loadingLayout;
private LottieAnimationView lottieAnimationViewLoading;
public ChatFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = getActivity();
assert context != null;
sharedPreferences = context.getSharedPreferences(Constants.sharedPreferencesDocName, Context.MODE_PRIVATE);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_chat, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
init();
bindUI(view);
setListeners();
}
private void bindUI(View view) {
recyclerViewMessageList = view.findViewById(R.id.recyclerViewMessageList);
editTextChatSendMessage = view.findViewById(R.id.editTextChatSendMessage);
imageViewChatSendMessage = view.findViewById(R.id.imageViewChatSendMessage);
user = gson.fromJson(sharedPreferences.getString("user", null), User.class);
loadingLayout = view.findViewById(R.id.loadingLayout);
lottieAnimationViewLoading = view.findViewById(R.id.lottieAnimationViewLoading);
setAdapter();
}
private void setListeners() {
recyclerViewMessageList.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
if (bottom < oldBottom) {
recyclerViewMessageList.post(() -> {
if (messages != null) {
recyclerViewMessageList.scrollToPosition(messages.size() - 1);
}
});
}
});
imageViewChatSendMessage.setOnClickListener(view -> {
if (!TextUtils.isEmpty(editTextChatSendMessage.getText().toString())) {
final Date currentTime = new Date();
@SuppressLint("SimpleDateFormat") SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm");
TimeZone timeZone = simpleDateFormat.getTimeZone();
simpleDateFormat.setTimeZone(timeZone);
String date = simpleDateFormat.format(currentTime);
final Message message = new Message(user.getId(), user.getUsername(), editTextChatSendMessage.getText().toString().trim(), date);
editTextChatSendMessage.setText("");
messagesReference.push().setValue(message);
messageAdapter.notifyDataSetChanged();
}
});
}
private void init() {
auth = FirebaseAuth.getInstance();
user = new User();
messages = new ArrayList<>();
}
private void setAdapter() {
loadingLayout.setVisibility(View.VISIBLE);
final FirebaseUser currentUser = auth.getCurrentUser();
assert currentUser != null;
messagesReference.limitToLast(100).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
final Message message = snapshot.getValue(Message.class);
assert message != null;
message.setKey(snapshot.getKey());
if (isNetworkConnected()) {
messages.add(message);
} else {
Toast.makeText(getActivity(), R.string.no_network_available, Toast.LENGTH_SHORT).show();
}
displayMessages(messages);
}
@Override
public void onChildChanged(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
Message message = snapshot.getValue(Message.class);
assert message != null;
message.setKey(snapshot.getKey());
List<Message> newMessages = new ArrayList<>();
for (Message m : messages) {
if (m.getKey().equals(message.getKey())) {
newMessages.add(message);
} else {
newMessages.add(m);
}
}
messages = newMessages;
displayMessages(messages);
}
@Override
public void onChildRemoved(@NonNull DataSnapshot snapshot) {
Message message = snapshot.getValue(Message.class);
assert message != null;
message.setKey(snapshot.getKey());
List<Message> newMessages = new ArrayList<>();
for (Message m : messages) {
if (!m.getKey().equals(message.getKey())) {
newMessages.add(m);
}
}
messages = newMessages;
displayMessages(messages);
}
@Override
public void onChildMoved(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
}
});
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
linearLayoutManager.setStackFromEnd(true);
recyclerViewMessageList.setLayoutManager(linearLayoutManager);
MessageAdapter messageAdapter = new MessageAdapter(getActivity(), messages);
recyclerViewMessageList.setAdapter(messageAdapter);
messageAdapter.notifyDataSetChanged();
recyclerViewMessageList.scrollToPosition(messages.size() - 1);
}
@Override
public void onStart() {
super.onStart();
}
private void displayMessages(List<Message> messages) {
messageAdapter = new MessageAdapter(getActivity(), messages);
recyclerViewMessageList.setAdapter(messageAdapter);
recyclerViewMessageList.scrollToPosition(messages.size() - 1);
messageAdapter.notifyDataSetChanged();
loadingLayout.setVisibility(View.GONE);
lottieAnimationViewLoading.cancelAnimation();
}
private boolean isNetworkConnected() {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected();
}
}