我正在使用firebase开发基本聊天应用程序。我能够存储和检索来自firebase的数据/消息!但是当我滚动聊天(Recyclerview)时,一些消息暂时不会显示/消失。
当我回到userlist_activity,然后再次访问chat_activity时,它会显示所有消息。但是,如果我再次滚动聊天,一些消息就会消失,直到我回去再来。我已多次检查我的firebase数据库,看看我是否错过了这些消息。但不,数据库中存在所有消息。但仍然没有从聊天窗口消失!
这是我的MainActivity / ChatAcitivty:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
public static final String ANONYMOUS = "anonymous";
public static final int DEFAULT_MSG_LENGTH_LIMIT = 1000;
private static final int RC_SIGN_IN = 1;
private RecyclerView mMessageRecyclerView;
private MessageAdapter mMessageAdapter;
private ProgressBar mProgressBar;
private ImageButton mPhotoPickerButton;
private EditText mMessageEditText;
private Button mSendButton;
private String mUsername;
private String mUserId;
private String friendUserID;
private ArrayList<ChatMessage> friendlyMessages;
private ArrayList<ChatMessage> selectedItems = new ArrayList<>();
private boolean multiSelect = false;
private int mPosition;
private FirebaseDatabase mFirebaseDatabase; //Entry Point to the database
private DatabaseReference mMessageDatabaseReference; //Reference to the specific part of the database.
private ChildEventListener mChildEventListener;
private FirebaseAuth mFirebaseAuth;
private FirebaseAuth.AuthStateListener mAuthStateListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mUsername = ANONYMOUS;
mFirebaseDatabase = FirebaseDatabase.getInstance();
mFirebaseAuth = FirebaseAuth.getInstance();
friendUserID = getIntent().getStringExtra("clickedUserID");
setTitle(getIntent().getStringExtra("clickedUsername"));
SendBird.setFriendUserID(friendUserID);
mMessageDatabaseReference = mFirebaseDatabase.getReference().child("message");
mMessageDatabaseReference.keepSynced(true);
// Initialize references to views
mProgressBar = findViewById(R.id.progressBar);
mMessageRecyclerView = findViewById(R.id.recycler_view_message);
mPhotoPickerButton = findViewById(R.id.photoPickerButton);
mMessageEditText = findViewById(R.id.messageEditText);
mSendButton = findViewById(R.id.sendButton);
// Initialize message RecyclerView and its adapter
friendlyMessages = new ArrayList<>();
mMessageAdapter = new MessageAdapter(friendlyMessages, this);
mMessageRecyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(this);
mMessageRecyclerView.setLayoutManager(mLayoutManager);
mMessageRecyclerView.setAdapter(mMessageAdapter);
// Initialize progress bar
mProgressBar.setVisibility(ProgressBar.INVISIBLE);
// ImagePickerButton shows an image picker to upload a image for a message
mPhotoPickerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// TODO: Fire an intent to show an image picker
}
});
mMessageEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (charSequence.toString().trim().length() > 0) {
mSendButton.setEnabled(true);
} else {
mSendButton.setEnabled(false);
}
}
@Override
public void afterTextChanged(Editable editable) {
}
});
mMessageEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(DEFAULT_MSG_LENGTH_LIMIT)});
mSendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ChatMessage chatMessage = new ChatMessage(mUsername, null, mMessageEditText.getText().toString(),
System.currentTimeMillis(), mUserId, friendUserID);
mMessageDatabaseReference.push().setValue(chatMessage);
// Clear input box
mMessageEditText.setText("");
}
});
mAuthStateListener = new FirebaseAuth.AuthStateListener() {
@Override
public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
onSignedInInitialize(user.getDisplayName(), user.getUid());
} else {
onSignedOutCleanup();
}
}
};
}
private void onSignedOutCleanup() {
mUsername = ANONYMOUS;
clear();
detachDatabaseReadListener();
}
private void onSignedInInitialize(String userName, String userId) {
mUsername = userName;
mUserId = userId;
attachDatabaseReadListener();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.sign_out_menu:
AuthUI.getInstance().signOut(this);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
if (resultCode == RESULT_OK) {
Toast.makeText(this, "Signed in!", Toast.LENGTH_SHORT).show();
} else if (resultCode == RESULT_CANCELED) {
Toast.makeText(this, "Sign in canceled!", Toast.LENGTH_SHORT).show();
finish();
}
}
}
@Override
protected void onResume() {
super.onResume();
mFirebaseAuth.addAuthStateListener(mAuthStateListener);
attachDatabaseReadListener();
}
@Override
protected void onPause() {
super.onPause();
if (mAuthStateListener != null) {
mFirebaseAuth.removeAuthStateListener(mAuthStateListener);
}
detachDatabaseReadListener();
clear();
}
private void attachDatabaseReadListener() {
if (mChildEventListener == null) {
mChildEventListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
ChatMessage chatMessage = dataSnapshot.getValue(ChatMessage.class);
friendlyMessages.add(chatMessage);
//mMessageAdapter.notifyItemInserted(friendlyMessages.size() - 1);
mMessageAdapter.notifyDataSetChanged();
mMessageRecyclerView.smoothScrollToPosition(friendlyMessages.size() - 1);
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
};
mMessageDatabaseReference.addChildEventListener(mChildEventListener);
}
}
private void detachDatabaseReadListener() {
if (mChildEventListener != null) {
mMessageDatabaseReference.removeEventListener(mChildEventListener);
mChildEventListener = null;
}
}
public void clear() {
int size = this.friendlyMessages.size();
friendlyMessages.clear();
mMessageAdapter.notifyItemRangeRemoved(0, size);
}
}
这是我的MessageAdapter:
public class MessageAdapter extends RecyclerView.Adapter {
private static final int VIEW_TYPE_MESSAGE_SENT = 1;
private static final int VIEW_TYPE_MESSAGE_RECEIVED = 2;
private ArrayList<ChatMessage> chatList;
private Context mContext;
public MessageAdapter(ArrayList<ChatMessage> chatList, Context mContext) {
this.chatList = chatList;
this.mContext = mContext;
}
@Override
public int getItemViewType(int position) {
ChatMessage chatMessage = chatList.get(position);
if (chatMessage.getUserId().equals(SendBird.getCurrnetUserId())) {
// If the current user is the sender of the message
Log.d("Adapter", "type message sent");
return VIEW_TYPE_MESSAGE_SENT;
} else {
// If some other user sent the message
return VIEW_TYPE_MESSAGE_RECEIVED;
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if (viewType == VIEW_TYPE_MESSAGE_SENT) {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_message_sent, parent, false);
Log.d("Adapter", "create message sent");
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;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
ChatMessage message = chatList.get(position);
switch (holder.getItemViewType()) {
case VIEW_TYPE_MESSAGE_SENT:
((SentMessageHolder) holder).bind(message);
Log.d("Adapter", "bind message sent");
break;
case VIEW_TYPE_MESSAGE_RECEIVED:
((ReceivedMessageHolder) holder).bind(message);
}
}
@Override
public int getItemCount() {
return chatList.size();
}
private class SentMessageHolder extends RecyclerView.ViewHolder {
TextView messageTextView;
TextView authorTextView;
TextView mTimeTextView;
ImageView photoImageView;
public SentMessageHolder(View itemView) {
super(itemView);
messageTextView = itemView.findViewById(R.id.messageTextView);
authorTextView = itemView.findViewById(R.id.nameTextView);
photoImageView = itemView.findViewById(R.id.photoImageView);
mTimeTextView = itemView.findViewById(R.id.timeTextView);
}
void bind(ChatMessage message) {
String to = message.getzFriendUserId();
if (to.equals(SendBird.getFriendUserID())) {
messageTextView.setText(message.getText());
authorTextView.setText("You");
// Format the stored timestamp into a readable String using method.
mTimeTextView.setText(Utils.formatDateTime(message.getTime()));
} else {
itemView.setVisibility(View.GONE);
}
}
}
private class ReceivedMessageHolder extends RecyclerView.ViewHolder {
TextView messageTextView;
TextView authorTextView;
TextView mTimeTextView;
ImageView photoImageView;
ReceivedMessageHolder(View itemView) {
super(itemView);
messageTextView = itemView.findViewById(R.id.messageTextView);
authorTextView = itemView.findViewById(R.id.nameTextView);
photoImageView = itemView.findViewById(R.id.photoImageView);
mTimeTextView = itemView.findViewById(R.id.timeTextView);
}
void bind(ChatMessage message) {
String friendUserId = message.getzFriendUserId();
String sendId = message.getUserId();
if (friendUserId.equals(SendBird.getCurrnetUserId()) && SendBird.getFriendUserID().equals(sendId)) {
messageTextView.setText(message.getText());
authorTextView.setText(message.getName());
// Format the stored timestamp into a readable String using method.
mTimeTextView.setText(Utils.formatDateTime(message.getTime()));
} else {
itemView.setVisibility(View.GONE);
}
}
}
}
我认为它与firebase同步相关,但我不确定究竟是什么造成了这个问题。任何建议/答案都会非常有用!
更新
item_message_received.xml
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:background="@drawable/rounded_rectangle_white"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<TextView
android:id="@+id/messageTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:textAppearance="?android:attr/textAppearanceLarge"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Message" />
<TextView
android:id="@+id/nameTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:textAppearance="?android:attr/textAppearanceSmall"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/timeTextView"
app:layout_constraintStart_toStartOf="@+id/messageTextView"
app:layout_constraintTop_toBottomOf="@+id/messageTextView"
tools:text="Name" />
<TextView
android:id="@+id/timeTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:textAppearance="?android:attr/textAppearanceSmall"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/messageTextView"
app:layout_constraintTop_toBottomOf="@+id/messageTextView"
tools:text="2:35PM" />
<ImageView
android:id="@+id/photoImageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
item_message_sent.xml:
<android.support.constraint.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="wrap_content"
android:layout_marginBottom="4dp"
android:background="#E0E0E0">
<android.support.constraint.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:id="@+id/constraint2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginBottom="4dp"
android:background="@drawable/rounded_rectangle_white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/messageTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:textAppearance="?android:attr/textAppearanceLarge"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/constraint2"
tools:text="Message" />
<TextView
android:id="@+id/nameTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:textAppearance="?android:attr/textAppearanceSmall"
app:layout_constraintBottom_toBottomOf="@+id/constraint2"
app:layout_constraintEnd_toStartOf="@+id/timeTextView"
app:layout_constraintStart_toStartOf="@+id/messageTextView"
app:layout_constraintTop_toBottomOf="@+id/messageTextView"
tools:text="Name" />
<TextView
android:id="@+id/timeTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:textAppearance="?android:attr/textAppearanceSmall"
app:layout_constraintEnd_toEndOf="@+id/constraint2"
app:layout_constraintStart_toEndOf="@+id/messageTextView"
app:layout_constraintTop_toBottomOf="@+id/messageTextView"
tools:text="2:35PM" />
<ImageView
android:id="@+id/photoImageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>