向聊天室添加消息后,FirestoreRecyclerAdapter Recyclerview UI 没有改变
FirestoreRecyclerAdapter Recyclerview UI doesn't change after adding a message to the chatroom
当我在聊天室中单击发送按钮时,我可以将消息添加到 firestore,但 recyclerview 不会实时更新,因此不会显示新消息。当我重新打开聊天室时确实会出现该消息,这不是最佳做法。通常 FirestoreRecyclerAdapter 应该在进行更改时自动更新查询并更新 recyclerview。
我在 Android Studio 中使用 Java 并使用片段而不是活动。
聊天室片段:
public class ChatRoomFragment extends Fragment {
private static final String TAG = "Notable:ChatRoom";
private FirebaseFirestore db;
private Query query;
private FirestoreRecyclerAdapter<Message, MessageAdapter.MessageHolder> adapter;
private FirebaseAuth mAuth;
private FirebaseUser currentUser;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.chatroom_fragment, container, false);
MaterialToolbar topBar = view.findViewById(R.id.topAppBar);
Button sendButton = view.findViewById(R.id.send_message);
EditText input = view.findViewById(R.id.message_edit_text);
RecyclerView chatRecyclerView = view.findViewById(R.id.chat_recyclerview);
mAuth = FirebaseAuth.getInstance();
currentUser = mAuth.getCurrentUser();
db = FirebaseFirestore.getInstance();
//FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder().setPersistenceEnabled(true).setCacheSizeBytes(FirebaseFirestoreSettings.CACHE_SIZE_UNLIMITED).build();
//db.setFirestoreSettings(settings);
String key = mAuth.getCurrentUser().getUid();
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
layoutManager.setStackFromEnd(true);
chatRecyclerView.setLayoutManager(layoutManager);
String title = this.getArguments().getString("buttonText");
topBar.setTitle(title);
query = db.collection("messages").whereEqualTo("chatGroup", title).orderBy("messageTime");
adapter = new MessageAdapter(getActivity(), query, key);
//https://medium.com/@akhilkc9/simple-android-chat-application-using-firestorerecycleradapter-7f632da2eaee
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
super.onItemRangeChanged(positionStart, itemCount);
chatRecyclerView.scrollToPosition(itemCount);
}
});
chatRecyclerView.setAdapter(adapter);
sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String text = input.getText().toString();
if(!text.equals("")){
db.collection("users").document(key).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
@Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists()){
String firstName = documentSnapshot.get("firstname").toString();
db.collection("messages").add(new Message(firstName, text, key, title));
}else{
Log.w(TAG, "No such document");
}
}
});
}
input.setText("");
}
});
return view;
}
@Override
public void onStart() {
super.onStart();
if(adapter!=null) adapter.startListening();
}
@Override
public void onStop() {
super.onStop();
if(adapter!=null) adapter.stopListening();
}
}
消息适配器:
public class MessageAdapter extends FirestoreRecyclerAdapter<Message, MessageAdapter.MessageHolder> {
private final String TAG = "MessageAdapter";
Context context;
String userId;
StorageReference storageReference;
private RequestOptions requestOptions = new RequestOptions();
private final int MESSAGE_IN_VIEW_TYPE = 1;
private final int MESSAGE_OUT_VIEW_TYPE = 2;
public MessageAdapter(@NonNull Context context, Query query, String userID) {
super(new FirestoreRecyclerOptions.Builder<Message>().setQuery(query, Message.class).build());
this.context = context;
this.userId = userID;
}
@Override
public int getItemViewType(int position) {
if(getItem(position).getMessageUserId().equals(userId)){
return MESSAGE_IN_VIEW_TYPE;
}
return MESSAGE_OUT_VIEW_TYPE;
}
@Override
public MessageHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = null;
if(viewType==MESSAGE_IN_VIEW_TYPE){
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.chat_item, parent, false);
}else{
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.chat_item_other, parent, false);
}
return new MessageHolder(view);
}
//Voordeel van Recyclerview is dat het automatisch een Viewholder heeft, dit heeft gridview niet automatisch
@Override
protected void onBindViewHolder(@NonNull MessageHolder holder, int position, @NonNull Message model) {
final TextView mText = holder.mText;
final TextView mUsername = holder.mUsername;
final TextView mTime = holder.mTime;
final TextView mDate = holder.mDate;
final ImageView imgProfile = holder.imgProfile;
if(mUsername != null && imgProfile != null){
mUsername.setText(model.getMessageUser());
//Glide.with(context).setDefaultRequestOptions(requestOptions).load(R.drawable.placeholder).diskCacheStrategy(DiskCacheStrategy.DATA).into(imgProfile);
//storageReference.child(model.getMessageUserId())
}
mText.setText(model.getMessageText());
mDate.setText(DateFormat.format("dd MMM", model.getMessageTime()));
mTime.setText(DateFormat.format("h:mm", model.getMessageTime()));
}
static class MessageHolder extends RecyclerView.ViewHolder {
TextView mText;
TextView mUsername;
TextView mTime;
TextView mDate;
ImageView imgProfile;
public MessageHolder(View itemView) {
super(itemView);
mText = itemView.findViewById(R.id.message_text);
mUsername = itemView.findViewById(R.id.message_user);
mTime = itemView.findViewById(R.id.message_time);
mDate = itemView.findViewById(R.id.message_date);
imgProfile = itemView.findViewById(R.id.imgDps);
}
}
}
chatroom_fragment.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">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/layout_group_chat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/topAppBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="@string/chat_group"
style="@style/Widget.MaterialComponents.Toolbar.Primary" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/chat_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="16dp"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="@+id/text_indicator"
app:layout_constraintTop_toBottomOf="@+id/layout_group_chat" />
<TextView
android:id="@+id/text_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/view"
app:layout_constraintStart_toStartOf="parent" />
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#e0e0e0"
app:layout_constraintBottom_toTopOf="@+id/layout_chatbox" />
<RelativeLayout
android:id="@+id/layout_chatbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
<EditText
android:layout_marginStart="16dp"
android:id="@+id/message_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:background="@android:color/transparent"
android:hint="@string/hint_message"
android:inputType="text"
android:maxLines="6"
tools:ignore="Autofill" />
<Button
android:id="@+id/send_message"
android:layout_width="74dp"
android:layout_height="48dp"
android:layout_alignParentEnd="true"
android:background="?attr/selectableItemBackground"
android:text="@string/send"
android:textColor="@color/design_default_color_primary" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
问题解决了,是因为我没有为firestore上的查询添加索引。
我从 android studio console(和 firestore)中得到一个错误,解释它见 link:
当我在聊天室中单击发送按钮时,我可以将消息添加到 firestore,但 recyclerview 不会实时更新,因此不会显示新消息。当我重新打开聊天室时确实会出现该消息,这不是最佳做法。通常 FirestoreRecyclerAdapter 应该在进行更改时自动更新查询并更新 recyclerview。 我在 Android Studio 中使用 Java 并使用片段而不是活动。
聊天室片段:
public class ChatRoomFragment extends Fragment {
private static final String TAG = "Notable:ChatRoom";
private FirebaseFirestore db;
private Query query;
private FirestoreRecyclerAdapter<Message, MessageAdapter.MessageHolder> adapter;
private FirebaseAuth mAuth;
private FirebaseUser currentUser;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.chatroom_fragment, container, false);
MaterialToolbar topBar = view.findViewById(R.id.topAppBar);
Button sendButton = view.findViewById(R.id.send_message);
EditText input = view.findViewById(R.id.message_edit_text);
RecyclerView chatRecyclerView = view.findViewById(R.id.chat_recyclerview);
mAuth = FirebaseAuth.getInstance();
currentUser = mAuth.getCurrentUser();
db = FirebaseFirestore.getInstance();
//FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder().setPersistenceEnabled(true).setCacheSizeBytes(FirebaseFirestoreSettings.CACHE_SIZE_UNLIMITED).build();
//db.setFirestoreSettings(settings);
String key = mAuth.getCurrentUser().getUid();
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
layoutManager.setStackFromEnd(true);
chatRecyclerView.setLayoutManager(layoutManager);
String title = this.getArguments().getString("buttonText");
topBar.setTitle(title);
query = db.collection("messages").whereEqualTo("chatGroup", title).orderBy("messageTime");
adapter = new MessageAdapter(getActivity(), query, key);
//https://medium.com/@akhilkc9/simple-android-chat-application-using-firestorerecycleradapter-7f632da2eaee
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
super.onItemRangeChanged(positionStart, itemCount);
chatRecyclerView.scrollToPosition(itemCount);
}
});
chatRecyclerView.setAdapter(adapter);
sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String text = input.getText().toString();
if(!text.equals("")){
db.collection("users").document(key).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
@Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists()){
String firstName = documentSnapshot.get("firstname").toString();
db.collection("messages").add(new Message(firstName, text, key, title));
}else{
Log.w(TAG, "No such document");
}
}
});
}
input.setText("");
}
});
return view;
}
@Override
public void onStart() {
super.onStart();
if(adapter!=null) adapter.startListening();
}
@Override
public void onStop() {
super.onStop();
if(adapter!=null) adapter.stopListening();
}
}
消息适配器:
public class MessageAdapter extends FirestoreRecyclerAdapter<Message, MessageAdapter.MessageHolder> {
private final String TAG = "MessageAdapter";
Context context;
String userId;
StorageReference storageReference;
private RequestOptions requestOptions = new RequestOptions();
private final int MESSAGE_IN_VIEW_TYPE = 1;
private final int MESSAGE_OUT_VIEW_TYPE = 2;
public MessageAdapter(@NonNull Context context, Query query, String userID) {
super(new FirestoreRecyclerOptions.Builder<Message>().setQuery(query, Message.class).build());
this.context = context;
this.userId = userID;
}
@Override
public int getItemViewType(int position) {
if(getItem(position).getMessageUserId().equals(userId)){
return MESSAGE_IN_VIEW_TYPE;
}
return MESSAGE_OUT_VIEW_TYPE;
}
@Override
public MessageHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = null;
if(viewType==MESSAGE_IN_VIEW_TYPE){
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.chat_item, parent, false);
}else{
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.chat_item_other, parent, false);
}
return new MessageHolder(view);
}
//Voordeel van Recyclerview is dat het automatisch een Viewholder heeft, dit heeft gridview niet automatisch
@Override
protected void onBindViewHolder(@NonNull MessageHolder holder, int position, @NonNull Message model) {
final TextView mText = holder.mText;
final TextView mUsername = holder.mUsername;
final TextView mTime = holder.mTime;
final TextView mDate = holder.mDate;
final ImageView imgProfile = holder.imgProfile;
if(mUsername != null && imgProfile != null){
mUsername.setText(model.getMessageUser());
//Glide.with(context).setDefaultRequestOptions(requestOptions).load(R.drawable.placeholder).diskCacheStrategy(DiskCacheStrategy.DATA).into(imgProfile);
//storageReference.child(model.getMessageUserId())
}
mText.setText(model.getMessageText());
mDate.setText(DateFormat.format("dd MMM", model.getMessageTime()));
mTime.setText(DateFormat.format("h:mm", model.getMessageTime()));
}
static class MessageHolder extends RecyclerView.ViewHolder {
TextView mText;
TextView mUsername;
TextView mTime;
TextView mDate;
ImageView imgProfile;
public MessageHolder(View itemView) {
super(itemView);
mText = itemView.findViewById(R.id.message_text);
mUsername = itemView.findViewById(R.id.message_user);
mTime = itemView.findViewById(R.id.message_time);
mDate = itemView.findViewById(R.id.message_date);
imgProfile = itemView.findViewById(R.id.imgDps);
}
}
}
chatroom_fragment.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">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/layout_group_chat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/topAppBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="@string/chat_group"
style="@style/Widget.MaterialComponents.Toolbar.Primary" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/chat_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="16dp"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="@+id/text_indicator"
app:layout_constraintTop_toBottomOf="@+id/layout_group_chat" />
<TextView
android:id="@+id/text_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/view"
app:layout_constraintStart_toStartOf="parent" />
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#e0e0e0"
app:layout_constraintBottom_toTopOf="@+id/layout_chatbox" />
<RelativeLayout
android:id="@+id/layout_chatbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
<EditText
android:layout_marginStart="16dp"
android:id="@+id/message_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:background="@android:color/transparent"
android:hint="@string/hint_message"
android:inputType="text"
android:maxLines="6"
tools:ignore="Autofill" />
<Button
android:id="@+id/send_message"
android:layout_width="74dp"
android:layout_height="48dp"
android:layout_alignParentEnd="true"
android:background="?attr/selectableItemBackground"
android:text="@string/send"
android:textColor="@color/design_default_color_primary" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
问题解决了,是因为我没有为firestore上的查询添加索引。 我从 android studio console(和 firestore)中得到一个错误,解释它见 link: