繁体   English   中英

Recyclerview适配器的notifyDataSetChanged不更新RecyclerView

[英]Recyclerview Adapter's notifyDataSetChanged not updating RecyclerView

我有一个带有Firebase实时数据库和本地SQLite数据库的聊天应用程序RecyclerView 我将消息保存到本地数据库(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屏幕也不会更新,但是在后台,如果我键入或收到任何消息,它会保存到本地数据库中,但不会显示在屏幕上。

在以下情况下,可以使用Chat RecyclerView

  1. 当我从最近的应用中清除应用时
  2. 停止从本地数据库获取数据
  3. 首次启动聊天屏幕

我从通知直接进入聊天屏幕时遇到了这个问题。

这就是我正常加载聊天片段的方式。

getFragmentManager().beginTransaction().add(R.id.Navigation_Drawer, chatFragment).commit();

使用AsynctaskNotification加载Fragment

((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);

        }
    }
}

我认为您需要在此处实现LoaderCallbacks ,并听取数据库中所做的更改以相应地更新RecyclerView 在Github中创建了一个存储库,存储库显示了数据库的读取,写入和更新以及在数据库中进行的更改,以便在相应的RecyclerView通过向数据库注册内容观察者来产生适当的效果。

整个想法是让观察者连接到数据库表,该观察器将通知您表中的任何更改。 检测到更改后,将自动生成数据库读取查询,并且再次从表中重新加载数据时,将触发onLoadFinished函数中的RecyclerView更新。

我在Github项目中有一个演示实现,它显示了要在RecyclerView并自动更新RecyclerView的用户列表,而无需调用notifyDataSetChanged 请检查用于控制要在RecyclerView再次显示数据的适配器。 在您的情况下,这是避免任何复杂性的最简单的实现。

希望能有所帮助。

更新

阅读完代码后,我提出了一些您应该做的修复。 很难理解为什么它不能按您预期的那样工作,因为给定的代码格式和完整性不是很好。 但是,我可以在这里建议您一些关键的修补程序,可能会有所帮助。

append_chat_conversation函数中,不必每次发现新的聊天消息并将其添加到message时都调用notifyDataSetChanged 我建议不要在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函数。 每次调用chatDatabase函数时,您将创建一个新的LinearLayoutManager并将其分配给RecyclerView 我建议也将其删除。

如下修改您的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;
}

我通过深入研究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"/>

对于开发人员来说,这类问题确实令人头疼,但是我不知道用这一行代码就能解决这个问题。 谢谢大家的帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM