简体   繁体   English

从 Firestore 中删除项目后 RecyclerView 不更新

[英]RecyclerView does not update after removing an item from the Firestore

I am developing a program that reads all documents from Firestore and loads them into a RecyclerView.我正在开发一个程序,它从 Firestore 读取所有文档并将它们加载到 RecyclerView 中。 This works perfectly, but when I try to do the reverse, that is to remove a document from Firestore, the latter does not take off, it stays in the RecyclerView.这完美地工作,但是当我尝试做相反的事情时,即从 Firestore 中删除一个文档,后者不会起飞,它停留在 RecyclerView 中。 To get it off I have to go to another screen and then come back.为了得到它,我必须将 go 转到另一个屏幕,然后再回来。 Below I put the code:下面我放代码:

package project.dobus;

// Librerie.
import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.appcompat.widget.SearchView;
import com.google.firebase.firestore.DocumentChange;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.EventListener;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.FirebaseFirestoreException;
import com.google.firebase.firestore.QuerySnapshot;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class PassengerRequestPage extends AppCompatActivity { // Schermata per rifiutare e accettare le richieste degli user in attesa.

    // Dichiarazioni oggetti globali: devono essere visti fuori da OnCreate.
    RecyclerView recyclerView; // Schermata che contiene tutte le richieste.
    ArrayList<PassengerPage> userArrayList; // Contiene tutte le informazioni relative all'user.
    AdapterPassengerRequestPage myAdapter; // Oggetto di tipo AdapterPassengerRequestPage.
    SearchView searchView; // Serve per cercare nella barra di ricerca un passeggero.

    FirebaseFirestore Firestore; // Dichiarazione Database.

    @Override
    protected void onCreate(Bundle savedInstanceState) { // Alla creazione della schermata.
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_request_page); // Si collega con la schermata grafica activity_request_page.xml.

        // Oggetti della schermata grafica.
        Button backButton = findViewById(R.id.buttonback); // Bottone per tornare indietro nel pannello amministrativo.
        recyclerView = findViewById(R.id.recyclerView); // Schermata che contiene tutte le richieste.
        searchView = findViewById(R.id.searchviewRP); // Serve per cercare nella barra di ricerca un passeggero.

        userArrayList = new ArrayList<>(); // Contiene tutte le informazioni relative all'user.

        // Impostazioni del recyclerView.
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        myAdapter = new AdapterPassengerRequestPage(PassengerRequestPage.this,userArrayList);
        recyclerView.setAdapter(myAdapter);

        // Database.
        Firestore =  FirebaseFirestore.getInstance();

        // Variabili memorizzate.
        SharedPreferences variablesStored = getSharedPreferences("variablesStored", 0);

        // Controlla costantemente se nel database, nella raccolta "Admins", è presente o no il numero.
        DocumentReference usersDocumentsFirestore = Firestore.collection("Admins").document(variablesStored.getString("phoneNumber", "0"));
        usersDocumentsFirestore.addSnapshotListener((usersDocument, usersError) -> {
            if (usersError != null){
                return;
            }
            if (usersDocument != null && !usersDocument.exists()) { // Se non dovesse più esistere ritorna in AccessPage.
                Intent intent = new Intent(getApplicationContext(), AccessPage.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                startActivity(intent); // Va in AccessPage.
            }
        });

        // SearchView.
        searchView.clearFocus(); // Leva il focus presente sulla searchView.
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String s) { // Quando si preme invio per cercare quello che si è scritto.
                return false;
            }
            @Override
            public boolean onQueryTextChange(String newText) { // Ogni volta che il testo cambia.
                filterList(newText);
                return true;
            }
        });

        backButton.setOnClickListener(view -> { // Inizio: quando si clicca il bottone per tornare indietro nel pannello amministrativo.
            Intent intent = new Intent(getApplicationContext(), AdministrativePanel.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK  | Intent.FLAG_ACTIVITY_CLEAR_TASK);
            startActivity(intent); // Ritorna nel pannello amministrativo.
        }); // Fine: quando si clicca il bottone per tornare indietro nel pannello amministrativo.

        eventUserAdded(); // Evneto che gestisce l'aggiunta di un user alla schermata.
    }

    @SuppressLint("NotifyDataSetChanged")
    private void eventUserAdded() { // Inizio: Evento che gestisce l'aggiunta di un user alla schermata.
        Firestore.collection("Attesa").addSnapshotListener((snapshots, e) -> {
            if (e != null) return;
            assert snapshots != null;
            for (DocumentChange AttesaDocument : snapshots.getDocumentChanges()) {
                switch (AttesaDocument.getType()) {
                    case ADDED: // Ogni volta che viene aggiunto un item nella schermata.
                        PassengerPage userData = AttesaDocument.getDocument().toObject(PassengerPage.class);
                        userData.setPassengerPhoneNumber(AttesaDocument.getDocument().getId()); // Setta il numero dell'user prendendolo dall'ID del documento.
                        userData.setPassengerName((String) AttesaDocument.getDocument().get("Nome")); // Setta il nome dell'user.
                        userData.setPassengerSurname((String) AttesaDocument.getDocument().get("Cognome")); // Setta il cognome dell'user.
                        userData.setPassengerSchool((String) AttesaDocument.getDocument().get("Scuola")); // Setta la scuola dell'user.
                        userArrayList.add(userData); // Aggiunge le informazioni dell'user dentro la lista.
                        myAdapter.notifyDataSetChanged(); // Avvisa che qualcosa è stato cambiato.
                        break;
                    case REMOVED: // Ogni volta che viene rimosso un item dalla schermata.
                        
                        break;
                }
            }
        });
    } // Fine: Evento che gestisce l'aggiunta di un user alla schermata.

    private void filterList(String text){ // Inizio: Metodo che permette di cercare nella recyclerView la parola che si scrive nella searchView.
        ArrayList<PassengerPage> filteredList = new ArrayList<>();
        for(PassengerPage item: userArrayList){
            if(item.getPassengerName().toLowerCase().contains(text.toLowerCase()) || item.getPassengerSurname().toLowerCase().contains(text.toLowerCase()) || item.getPassengerPhoneNumber().contains(text)){
                filteredList.add(item);
            }
        }
        myAdapter.setFilteredList(filteredList);
    } // Fine: Metodo che permette di cercare nella recyclerView la parola che si scrive nella searchView.
}

. .

package project.dobus;

// Librerie
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class AdapterPassengerRequestPage extends RecyclerView.Adapter<AdapterPassengerRequestPage.MyViewHolder> { // Classe che gestisce il recycleView di PassengerRequestPage.

    Context context;

    // Lista che contiene tutte le informazioni del passeggero.
    ArrayList<PassengerPage> userArrayList;

    public AdapterPassengerRequestPage(Context context, ArrayList<PassengerPage> userArrayList) { // Inizio Costruttore.
        this.context = context;
        this.userArrayList = userArrayList;
    } // Fine: Costruttore.

    @NonNull
    @Override
    public AdapterPassengerRequestPage.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { // Inizio:  Crea la view di PassengerRequestPage con il layout card_user.xml.
        View requestPageView = LayoutInflater.from(context).inflate(R.layout.card_accept_user,parent,false);
        return new MyViewHolder(requestPageView);
    } // Fine:  Crea la view di PassengerRequestPage con il layout card_user.xml.

    @SuppressLint("NotifyDataSetChanged") // Serve per evitare un avviso con notifyDataSetChanged().
    public void setFilteredList(ArrayList<PassengerPage> filteredList){ // Inizio: Metodo che aggiorna l'array dei passeggeri con quello flitrato.
        this.userArrayList = filteredList;
        notifyDataSetChanged();
    } // Fine: Metodo che aggiorna l'array dei passeggeri con quello flitrato.

    // Gestisce tutte le azioni che riguardano gli oggetti grafici.
    @Override
    public void onBindViewHolder(@NonNull AdapterPassengerRequestPage.MyViewHolder holder, int position) { // Inizio onBindViewHolder.
        FirebaseFirestore Firestore = FirebaseFirestore.getInstance();  // Database Firestore.
        PassengerPage passengerUser = userArrayList.get(position);
        holder.passengerName.setText(passengerUser.passengerName); // Cambia il testo "passengerName" con il nome che prende dal database: viene assegnato in PassengerPage.
        holder.passengerSurname.setText(passengerUser.passengerSurname); // Cambia il testo "passengerSurname" con il nome che prende dal database: viene assegnato in PassengerPage.
        holder.passengerPhoneNumber.setText(passengerUser.passengerPhoneNumber); // Cambia il testo "passengerPhoneNumber" con il nome che prende dal database: viene assegnato in PassengerPage.
        holder.passengerSchool.setText(passengerUser.passengerSchool); // Cambia il testo "passengerSchool" con il nome che prende dal database: viene assegnato in PassengerPage.

        holder.declineUserButton.setOnClickListener(view -> { // Inizio: quando clicco il tasto per rifiutare un utente in attesa.
            Firestore.collection("Attesa").document(passengerUser.passengerPhoneNumber).delete(); // Rimuove dal database l'utente.
            // Permette di non crashare quando si preme a ripetizione il tasto per rifiutare un passeggero in attesa.
            if (holder.getAdapterPosition() == RecyclerView.NO_POSITION) {
                return;
            }
            userArrayList.remove(holder.getAdapterPosition()); // Rimuove dalla lista l'utente.
            notifyItemRemoved(holder.getAdapterPosition()); // Avvisa che è stato rimosso l'utente e sistema la schermata.
        }); // Fine: quando clicco il tasto per rifiutare un utente in attesa.

        holder.acceptUserButton.setOnClickListener(view -> Firestore.collection("Attesa") // Inizio: quando si clicca il bottone per accettare un utente in attesa.
        .get()
        .addOnCompleteListener(task -> {
            if (task.isSuccessful()) {
                for (QueryDocumentSnapshot AttesaDocument : task.getResult()) {
                    Map<String, Object> userData = new HashMap<>(); // Serve a memorizzare tutte le informazioni dell'user.
                    userData.put("Nome", AttesaDocument.get("Nome")); // Aggiunge dentro userData il nome dell'user.
                    userData.put("Cognome", AttesaDocument.get("Cognome")); // Aggiunge dentro userData il cognome dell'user.
                    userData.put("Scuola", AttesaDocument.get("Scuola")); // Aggiunge dentro userData la scuola dell'user.
                    Firestore.collection("Users").document(passengerUser.passengerPhoneNumber).set(userData); // Aggiunge nella nuova collezione "Users" i nuovi dati.
                    Firestore.collection("Attesa").document(passengerUser.passengerPhoneNumber).delete(); // Rimuove da attesa i dati.
                    // Permette di non crashare quando si preme a ripetizione il tasto per accettare un passeggero in attesa.
                    if (holder.getAdapterPosition() == RecyclerView.NO_POSITION) {
                        return;
                    }
                    userArrayList.remove(holder.getAdapterPosition()); // Rimuove dalla lista l'utente.
                    notifyItemRemoved(holder.getAdapterPosition()); // Avvisa che è stato rimosso l'utente e sistema la schermata.
                }
            }
        })); // Fine: quando si clicca il bottone per accettare un utente in attesa.
    } // Fine onBindViewHolder.

    // Conta i valori presenti nella lista.
    @Override
    public int getItemCount() {
        return userArrayList.size();
    }



    // Gestisce gli elementi presenti nel file card_user.xml.
    public static class MyViewHolder extends RecyclerView.ViewHolder {

        TextView passengerName, passengerSurname, passengerPhoneNumber, passengerSchool; // Testo che contiene le informazioni del passeggero.
        Button declineUserButton, acceptUserButton; // Bottoni per accettare e rifiutare le richieste.
        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            passengerPhoneNumber = itemView.findViewById(R.id.idDocumento); // Oggetto grafico per il numero di telefono.
            passengerName = itemView.findViewById(R.id.tvFirstName); // Testo per il nome dell'user.
            passengerSurname = itemView.findViewById(R.id.tvLastName); // Testo per il cognome dell'user.
            passengerSchool = itemView.findViewById(R.id.idScuola); // Testo per la scuola dell'user.
            declineUserButton = itemView.findViewById(R.id.rimuoviButton); // Bottone per rifiutare l'user.
            acceptUserButton = itemView.findViewById(R.id.okButton); // Bottone per per accettare l'user.
        }

    }
}

. .

    package project.dobus;

// Librerie.
import com.google.firebase.firestore.DocumentId;

public class PassengerPage { // Classe che serve per modificare le informazioni degli user in attesa.

    String passengerName, passengerSurname, passengerPhoneNumber, passengerSchool; // Informazioni degli user.

    // Costruttore vuoto.
    public PassengerPage(){}

    // Costruttore.
    public PassengerPage(String passengerName, String passengerSurname, String passengerPhoneNumber) {
        this.passengerName = passengerName;
        this.passengerSurname = passengerSurname;
        this.passengerPhoneNumber = passengerPhoneNumber;
    }

    public String getPassengerName() { return passengerName; } // Prende quello che contiene PassengerName.

    public void setPassengerName(String passengerName) { this.passengerName = passengerName; } // Setta PassengerName con il nome dell'user.

    public String getPassengerSurname() { return passengerSurname; } // Prende quello che contiene PassengerSurname.

    public void setPassengerSurname(String passengerSurname) { this.passengerSurname = passengerSurname; } // Setta PassengerSurname con il cognome dell'user.

    @DocumentId
    public String getPassengerPhoneNumber() { return passengerPhoneNumber; } // Prende quello che contiene passengerName.

    @DocumentId
    public void setPassengerPhoneNumber(String passengerPhoneNumber) { this.passengerPhoneNumber = passengerPhoneNumber; } // Setta PassengerPhoneNumber con il numero dell'user.

    public String getPassengerSchool() { return passengerSchool; } // Prende quello che contiene PassengerSchool.

    public void setPassengerSchool(String passengerSchool) { this.passengerSchool = passengerSchool; } // Setta PassengerSchool con il numero dell'user.
}

How can I fix the code so that when I remove a document from Firestore it automatically removes it in real time from the RecyclerView?如何修复代码,以便当我从 Firestore 中删除文档时,它会自动从 RecyclerView 中实时删除它?

I tried to put我试着把

myAdapter.notifyDataSetChanged();

in this part在这部分

   case REMOVED: 
   /////
   break;

but nothing changes..但没有任何改变..

The problem is here:问题在这里:

Firestore.collection("Attesa").addSnapshotListener((snapshots, e) -> {
    if (e != null) return;
    assert snapshots != null;
    for (DocumentChange AttesaDocument : snapshots.getDocumentChanges()) {
        switch (AttesaDocument.getType()) {
            case ADDED: // Ogni volta che viene aggiunto un item nella schermata.
                PassengerPage userData = AttesaDocument.getDocument().toObject(PassengerPage.class);
                userData.setPassengerPhoneNumber(AttesaDocument.getDocument().getId()); // Setta il numero dell'user prendendolo dall'ID del documento.
                userData.setPassengerName((String) AttesaDocument.getDocument().get("Nome")); // Setta il nome dell'user.
                userData.setPassengerSurname((String) AttesaDocument.getDocument().get("Cognome")); // Setta il cognome dell'user.
                userData.setPassengerSchool((String) AttesaDocument.getDocument().get("Scuola")); // Setta la scuola dell'user.
                userArrayList.add(userData); // Aggiunge le informazioni dell'user dentro la lista.
                myAdapter.notifyDataSetChanged(); // Avvisa che qualcosa è stato cambiato.
                break;
            case REMOVED: // Ogni volta che viene rimosso un item dalla schermata.
                
                break;
        }
    }

You're using a realtime listener , which means your code already gets notified whenever a document gets deleted (or anything else changes in Attesa . But your code only handles the initial documents and additions, and actively ignores the removal of the documents or changes to existing documents.您正在使用实时侦听器,这意味着每当删除文档(或Attesa中的任何其他更改)时,您的代码都会收到通知。但是您的代码仅处理初始文档和添加内容,并主动忽略文档的删除或更改现有文件。

You'll want to handle all three event types:您需要处理所有三种事件类型:

  • ADDED : handle this by taking the data from the snapshot, and adding it to your userArrayList (you already do this). ADDED :通过从快照中获取数据并将其添加到您的userArrayList来处理此问题(您已经这样做了)。
  • REMOVED : handle this by finding the corresponding item in the userArrayList and removing it from there. REMOVED :通过在userArrayList中找到相应的项目并将其从那里删除来处理此问题。
  • CHANGED : handle this by finding the corresponding item in the userArrayList and updating it with the data from the snapshot. CHANGED :通过在userArrayList中找到相应的项目并使用快照中的数据对其进行更新来处理此问题。

In all these cases you'll want to notify the adapter of the change by calling notifyDataSetChanged .在所有这些情况下,您都需要通过调用notifyDataSetChanged来通知适配器更改。 You'll probably want to do that outside of the for loop, as there's no need to notify the adapter multiple times for a single set of changes.您可能希望在for循环之外执行此操作,因为对于一组更改无需多次通知适配器。

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

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