我有一个RecyclerView
聊天应用程序与firebase实时数据库和本地SQLite数据库。我将消息保存到本地数据库(SQLite),然后调用adapter.notifyDataSetChanged()
。
如果消息已经在数据库中(消息唯一ID),则SQLite将在数据库insertOrThrow
方法上返回0。我正在检查这样的可用性。
if (id == 0) {
Log.d(TAG,"Database Already Has Value Of This Random Id ");
adapter.notifyDataSetChanged();
} else {
Chat_Wrapper chat_wrapper = new Chat_Wrapper(Chat_Msg, null, null, null, null, null, null, Chat_TimeStamp, UserPhone_Intent, UserImage_Intent, Chat_FROM, null,null,id);
message.add(chat_wrapper);
adapter.notifyDataSetChanged();
}
但是,即使正在调用else
语句,我的RecyclerView
屏幕也不会更新,但在后台,如果我输入或接收任何消息,它会保存到本地数据库但不会#39} ; t在屏幕上显示。
聊天RecyclerView
适用于以下情况
当我直接通知聊天屏幕时,我正面临这个问题。
我是如何正常加载聊天片段的。
getFragmentManager().beginTransaction().add(R.id.Navigation_Drawer, chatFragment).commit();
使用Fragment
Notification
加载Asynctask
((Navigation_Drawer)context).getFragmentManager().beginTransaction().replace(R.id.Navigation_Drawer, chatFragment).commit();
我是如何从通知中启动ChatFragment的。
public class FireBase_Messaging_Service extends FirebaseMessagingService {
public static final String TAG="###FireBase MSG###";
public static final int NOTIFICATION=5;
String UserName;
String ID;
String Msg;
Map<String,String> data;
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Log.d(TAG,"From "+remoteMessage.getFrom());
if (remoteMessage.getData().size()>0){
data = remoteMessage.getData();
Log.d(TAG,"Message Data "+remoteMessage.getData());
data = remoteMessage.getData();
UserName = data.get("name");
ID = data.get("ID");
Msg = data.get("Message");
showNotification(Msg,ID,UserName);
}
if (remoteMessage.getNotification()!=null){
Log.d(TAG,"Message Notification Body "+remoteMessage.getNotification().getBody());
// Toast.makeText(this, "Notification "+remoteMessage.getNotification().getBody(), Toast.LENGTH_LONG).show();
}
}
private void showNotification(String Message,String ID,String UserName) {
Log.d(TAG,"Show Notification "+Message+" "+ID);
Intent intent=new Intent(this, Navigation_Drawer.class);
intent.putExtra("Type","Text");
//intent.putExtra("Type",MsgType);
intent.putExtra("ID",ID);
intent.putExtra("uname",UserName);
intent.putExtra("Message",Msg);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent=PendingIntent.getActivity(this,NOTIFICATION,intent,PendingIntent.FLAG_UPDATE_CURRENT);
int color = getResources().getColor(R.color.black);
String ChannelID = "Message";
notificationChannel(ChannelID,"Chat");
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(),ChannelID)
.setSmallIcon(R.drawable.default_x)
.setColor(color)
.setContentTitle(UserName)
.setContentText(Message)
.setChannelId(ChannelID)
.setTicker("My App")
.setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND | Notification.FLAG_SHOW_LIGHTS)
.setLights(0xff00ff00, 1000, 500) // To change Light Colors
.setStyle(new NotificationCompat.BigTextStyle().bigText(Message))//For Expandable View
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this);
managerCompat.notify(NOTIFICATION,builder.build());
}
@Override
public void onDeletedMessages() {
super.onDeletedMessages();
}
private void notificationChannel (String ChannelID, String channelName) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(ChannelID,channelName, NotificationManager.IMPORTANCE_DEFAULT);
channel.setLightColor(Color.GREEN);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
}
}
}
我还注意到,通过记录将数据添加到列表message.add(chat_wrapper)
之后,它首先显示了大小的增加,但是当while循环结束时,它显示ArrayList
的最后一个大小。
这是ChatFragment
班。
public class Chat_Screen_Fragment extends Fragment implements View.OnClickListener, ChildEventListener{
public static final String TAG = "###CHAT SCREEN###";
List<Chat_Wrapper> message = new ArrayList<>();
Chat_Adapter adapter;
RecyclerView recyclerView;
LinearLayoutManager layoutManager;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
View v=inflater.inflate(R.layout.chat_screen_main_fragment,container,false);
setRetainInstance(true);
// GET INTENT VALUES FROM USER PROFILE CLASS
UserName_Intent = getArguments().getString("Get_Name");
UserImage_Intent = getArguments().getString("Get_Image");
UserPhone_Intent = getArguments().getString("Get_Phone");
UserID_Intent = getArguments().getString("Get_ID");
FirebaseToken_Intent = getArguments().getString("Get_Token"); //Firebase Token of other person
Room_Name_Intent = getArguments().getString("Get_Other"); // Room Name of chat
UserLastSeen_Intent=getArguments().getString("LastSeen");
//Sender_FCMToken = Session.getFirebaseID();
// RECYCLER VIEW
recyclerView = v.findViewById(R.id.Chat_Screen_Message_List);
layoutManager = new LinearLayoutManager(getActivity());
layoutManager.setStackFromEnd(true);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(layoutManager);
databaseReference = FirebaseDatabase.getInstance().getReference().child(Room_Name_Intent);
databaseReference.addChildEventListener(this);
adapter = new Chat_Adapter(getActivity(), message);
recyclerView.setAdapter(adapter);
// FETCH OLD MESSAGE FROM DATABASE
chatDatabase();
return v;
}
// FIREBASE REAL TIME DATABASE WHICH FETCH ALL MESSAGES (SYNC) FROM ONLINE DATABASE
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
append_chat_conversation(dataSnapshot);
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
append_chat_conversation(dataSnapshot);
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
private synchronized void append_chat_conversation(DataSnapshot dataSnapshot) {
iterator = dataSnapshot.getChildren().iterator();
while (iterator.hasNext()) {
// GETTING DATA FROM FIREBASE DATABASE
Chat_Msg = (String) ((DataSnapshot) iterator.next()).getValue();
Chat_FROM = (String) ((DataSnapshot) iterator.next()).getValue();
Chat_FROM_ID = (String) ((DataSnapshot) iterator.next()).getValue();
Chat_TO = (String) ((DataSnapshot) iterator.next()).getValue();
Chat_TimeStamp = (String) ((DataSnapshot) iterator.next()).getValue();
Chat_Type= (String) ((DataSnapshot) iterator.next()).getValue();
Random_ID=(String) ((DataSnapshot) iterator.next()).getValue();
Chat_FCM_FROM= (String) ((DataSnapshot) iterator.next()).getValue();
Chat_FCM_TO= (String) ((DataSnapshot) iterator.next()).getValue();
Log.d(TAG, "Chat Items " + Chat_Msg + " " + Random_ID);
Chat_Database tempChatDatabase = new Chat_Database(getActivity());
boolean hasValue = tempChatDatabase.CheckValueExist(Random_ID);
Log.d(TAG,"DATABASE ALREADY HAS VALUE OF TIMESTAMP= "+hasValue);
if (!hasValue) {
Log.d(TAG,"DATABASE DON'T HAVE SAME ENTRY FOR TIME STAMP. ENTERED INTO HAS VALUE");
Log.d(TAG,"Chat Message "+Chat_Msg);
if (Chat_Type.equals("Typed_Message")) {
Log.d(TAG, "VIEW TYPE IS Message " + Chat_Msg);
long id = chat_database.Insert_Chat(Session.getUserID(),Room_Name_Intent, UserID_Intent, "Text", Chat_Msg, Chat_FROM, Chat_TO, Chat_TimeStamp, Chat_FCM_FROM, Chat_FCM_TO, Session.getPhoneNO(), UserPhone_Intent,Random_ID,UserImage_Intent,UserLastSeen_Intent,Chat_FROM_ID);
//Adding Chat Data Into Database
Log.d(TAG,"Database Entry ID "+id);
if (id == 0) {
Log.d(TAG,"Database Already Has Value Of This Random Id ");
adapter.notifyDataSetChanged();
continue;
} else {
Log.d(TAG,"Database Don't Has Value Of This Random Id ");
Log.d(TAG,"Message Size "+message.size());
Chat_Wrapper chat_wrapper = new Chat_Wrapper(Chat_Msg, null, null, null, null, null, null, Chat_TimeStamp, UserPhone_Intent, UserImage_Intent, Chat_FROM, null,null,id);
message.add(chat_wrapper);
Log.d(TAG,"Message Size "+message.size());
adapter.notifyDataSetChanged();
Log.d(TAG,"Adapter Notified Data Set "+adapter.getItemCount());
recyclerView.post(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(adapter.getItemCount());
}
});
}
}
Log.d(TAG, "MESSAGE ARRAY SIZE " + message.size());
chat_database.isDatabaseClose();
}
private void chatDatabase(){
//Database Init and Filling Adapter
Log.d(TAG,"Chat Database Function");
chat_database=new Chat_Database(getActivity());
chatCursor=chat_database.getUserChat(UserID_Intent);
boolean checkDB_Exist=functions.DatabaseExist(getActivity(),"CHAT_DATABASE.DB");
boolean chatItemsCounts=chatCursor.getCount()>0;
chatCursor.moveToFirst();
Log.d(TAG,"Value At Chat Database "+ checkDB_Exist+" "+chatItemsCounts);
if (checkDB_Exist && chatCursor.getCount()>0 && chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID")).equals(UserID_Intent)){
Log.d(TAG,"Database Exist Chat Database");
message.clear();
chatCursor.moveToFirst();
do {
database_rowID=chatCursor.getInt(chatCursor.getColumnIndex("ID"));
database_userID=chatCursor.getString(chatCursor.getColumnIndex("USER_ID"));
database_RoomName =chatCursor.getString(chatCursor.getColumnIndex("ROOM_NAME"));
database_ReceiverID=chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID"));
database_MessageType=chatCursor.getString(chatCursor.getColumnIndex("MESSAGE_TYPE"));
database_Message=chatCursor.getString(chatCursor.getColumnIndex("USER_MESSAGE"));
database_MsgFrom=chatCursor.getString(chatCursor.getColumnIndex("SENDER_NAME"));
database_MsgTo=chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_NAME"));
database_TimeStamp=chatCursor.getString(chatCursor.getColumnIndex("TIME_STAMP"));
database_FCMfrom=chatCursor.getString(chatCursor.getColumnIndex("SENDER_TOKEN"));
database_FCMto=chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_TOKEN"));
database_LocalPath=chatCursor.getString(chatCursor.getColumnIndex("DOWNLOADED_AT"));
database_PhoneFrom=chatCursor.getString(chatCursor.getColumnIndex("MY_PHONE"));
database_PhoneTo=chatCursor.getString(chatCursor.getColumnIndex("OTHER_PHONE"));
Log.d(TAG,"Value Of Database Message String = "+database_Message);
Log.d(TAG,"Row ID of Database "+database_rowID);
// Check Message Type
Log.d(TAG,"Message Type Is Text");
Chat_Wrapper text = new Chat_Wrapper(database_Message, null, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom,null,null,database_rowID);
message.add(text);
}
while(chatCursor.moveToNext());
Room_Name_Intent = database_RoomName;
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
adapter.notifyDataSetChanged();
chatCursor.close();
boolean value = chat_database.isDatabaseClose();
recyclerView.post(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(message.size()-1);
}
});
Log.d(TAG,"Value Of Database Close or Not "+value);
}
}
}
答案 0 :(得分:0)
我认为你需要在这里实现LoaderCallbacks
并听取数据库中所做的更改以相应地更新RecyclerView
。我在这里创建了一个repository in Github,它显示了数据库的读取,写入和更新,并使数据库中的更改在相应的RecyclerView
中具有正确的效果,并向数据库注册了内容观察者。
整个想法是让一个观察者附加到您的数据库表中,该表将通知您表中的任何更改。检测到更改时,将自动生成数据库读取查询,并在从表中重新加载数据时将触发RecyclerView
函数内的onLoadFinished
更新。
我在Github项目中有一个演示实现,该项目显示了RecyclerView
中显示的用户列表,并在不调用RecyclerView
的情况下自动更新notifyDataSetChanged
。请检查控制数据的适配器,以便再次显示RecyclerView
中的数据。这是您在案例中可能避免任何复杂性的最简单的实现。
希望有所帮助。
<强>更新强>
在完成代码之后,我想出了一些你应该做的修复。很难理解为什么它没有像你预期的那样工作,因为给出的代码格式和完整性都不是很好。但是,我可以在这里建议一些可能有用的关键修复。
在append_chat_conversation
功能中,每次找到新的聊天消息时都不必致电notifyDataSetChanged
,并将其添加到message
。我想建议,不要在message
列表中添加聊天消息。您可能会删除以下if-else
块。只需更新聊天数据库并在其中插入新的聊天消息即可。所以我建议您删除以下部分。
if (id == 0) {
Log.d(TAG,"Database Already Has Value Of This Random Id ");
adapter.notifyDataSetChanged();
continue;
} else {
Log.d(TAG,"Database Don't Has Value Of This Random Id ");
Log.d(TAG,"Message Size "+message.size());
Chat_Wrapper chat_wrapper = new Chat_Wrapper(Chat_Msg, null, null, null, null, null, null, Chat_TimeStamp, UserPhone_Intent, UserImage_Intent, Chat_FROM, null,null,id);
message.add(chat_wrapper);
Log.d(TAG,"Message Size "+message.size());
adapter.notifyDataSetChanged();
Log.d(TAG,"Adapter Notified Data Set "+adapter.getItemCount());
recyclerView.post(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(adapter.getItemCount());
}
});
}
在此行之前调用chatDatabase
函数以从数据库加载消息。
// FETCH MESSAGE FROM DATABASE
chatDatabase();
Log.d(TAG, "MESSAGE ARRAY SIZE " + message.size());
chat_database.isDatabaseClose();
现在修复您的chatDatabase
功能。您每次调用LinearLayoutManager
函数时,都会创建一个新的RecyclerView
并将其分配给chatDatabase
。我建议也删除它。
修改您的chatDatabase
功能,如下所示。
private void chatDatabase() {
//Database Init and Filling Adapter
Log.d(TAG, "Chat Database Function");
chat_database = new Chat_Database(getActivity());
chatCursor = chat_database.getUserChat(UserID_Intent);
boolean checkDB_Exist = functions.DatabaseExist(getActivity(), "CHAT_DATABASE.DB");
boolean chatItemsCounts = chatCursor.getCount() > 0;
chatCursor.moveToFirst();
Log.d(TAG, "Value At Chat Database " + checkDB_Exist + " " + chatItemsCounts);
if (checkDB_Exist && chatCursor.getCount() > 0 && chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID")).equals(UserID_Intent)) {
Log.d(TAG, "Database Exist Chat Database");
message.clear();
chatCursor.moveToFirst();
do {
database_rowID = chatCursor.getInt(chatCursor.getColumnIndex("ID"));
database_userID = chatCursor.getString(chatCursor.getColumnIndex("USER_ID"));
database_RoomName = chatCursor.getString(chatCursor.getColumnIndex("ROOM_NAME"));
database_ReceiverID = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID"));
database_MessageType = chatCursor.getString(chatCursor.getColumnIndex("MESSAGE_TYPE"));
database_Message = chatCursor.getString(chatCursor.getColumnIndex("USER_MESSAGE"));
database_MsgFrom = chatCursor.getString(chatCursor.getColumnIndex("SENDER_NAME"));
database_MsgTo = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_NAME"));
database_TimeStamp = chatCursor.getString(chatCursor.getColumnIndex("TIME_STAMP"));
database_FCMfrom = chatCursor.getString(chatCursor.getColumnIndex("SENDER_TOKEN"));
database_FCMto = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_TOKEN"));
database_LocalPath = chatCursor.getString(chatCursor.getColumnIndex("DOWNLOADED_AT"));
database_PhoneFrom = chatCursor.getString(chatCursor.getColumnIndex("MY_PHONE"));
database_PhoneTo = chatCursor.getString(chatCursor.getColumnIndex("OTHER_PHONE"));
Log.d(TAG, "Value Of Database Message String = " + database_Message);
Log.d(TAG, "Row ID of Database " + database_rowID);
// Check Message Type
Log.d(TAG, "Message Type Is Text");
Chat_Wrapper text = new Chat_Wrapper(database_Message, null, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
message.add(text);
} while (chatCursor.moveToNext());
Room_Name_Intent = database_RoomName;
adapter.setChatMessages(message); // Add a public function in your adapter to set the chat messages
adapter.notifyDataSetChanged();
chatCursor.close();
boolean value = chat_database.isDatabaseClose();
recyclerView.post(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(message.size() - 1);
}
});
Log.d(TAG, "Value Of Database Close or Not " + value);
}
}
适配器中的setChatMessages
函数看起来像这样。
public void setChatMessages(ArrayList<Message> messageList) {
this.message = messageList;
}
答案 1 :(得分:0)
我通过挖掘android生命周期来解决这个问题。正如我上面提到的,只有当我从通知中输入 ChatFragment 时才会遇到此问题。我只是活动状态问题。代码正在运作。我在做什么我总是在通知点击(在Firebase代码中)重新创建活动。
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
在Intent
中挖掘大量代码并更改多个标志之后。只有android:launchMode="singleTask"
解决了我的问题。只有在不存在时才会创建活动。现在,在点击通知时,它不会调用活动onDestroy
方法。它是onStart
的启动活动,可以避免重新创建活动。
我在 AndroidManifest的 NavigationDrawer活动代码中添加此标记。
<activity
android:name=".Navigation_Drawer"
android:launchMode="singleTask"
android:theme="@style/AppTheme"/>
这类问题对开发人员来说真的很头疼,但我不知道用这一行代码可以解决这个问题。谢谢大家的帮助。