簡體   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