1

I'm using Firestore to hold documents representing people with a name. I'm using a RecyclerView to display them, populating it with Cards that display the names pulled from Firestore. I've implemented a swipe-to-delete feature, which deletes the person from Firestore when you swipe that person's card left or right in the RecyclerView.

My problem is that swiping a card does not cause the RecyclerView to update - when I check Firestore, the deletion does happen, but the RecyclerView just shows a gap where that card used to be. None of the cards below the removed card "move up" to fill the gap.

Inside of onCreate() for the activity that contains the RecyclerView, I call a function called setUpRecyclerView(). Here is that function, which contains the code for swiping:

private void setUpRecyclerView() {
    // create the query who's results you want to populate the recyclerView with
    Query query = clientsReference.orderBy("lastNameLowercase")
            .orderBy("firstNameLowercase");

    // how we get the query into the adapter
    FirestoreRecyclerOptions<Client> options = new FirestoreRecyclerOptions.Builder<Client>()
            .setQuery(query, Client.class)
            .build();

    // assign the adapter
    adapter = new ClientAdapter(options);

    RecyclerView recyclerView = findViewById(R.id.recycler_view);
    recyclerView.setHasFixedSize(true);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(adapter);

    /**
     * This section handles what happens when you swipe an item in the recyclerView list
     */
    new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0,
            ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
        @Override
        public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
            return false;
        }

        @Override
        public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
            int position = viewHolder.getAdapterPosition();
            adapter.deleteClient(position);
            //adapter.notifyItemRemoved(position);
        }
    }).attachToRecyclerView(recyclerView);

    /**
     * This section handles what happens when you click on an item in the recyclerView list
     */
    adapter.setOnItemClickListener(new ClientAdapter.OnItemClickListener() {
        @Override
        public void onItemClick(DocumentSnapshot documentSnapshot, int position) {
            Client client = documentSnapshot.toObject(Client.class);
            String id = documentSnapshot.getId();
            Intent intentDisplayClient = new Intent(MainActivity.this, DisplayClientActivity.class);

            // Pass ahead the client's id from Firestore to the next activity
            intentDisplayClient.putExtra(KEY_ID, id);
            startActivity(intentDisplayClient);
        }
    });
}

And here is the adapter class:

public class ClientAdapter extends FirestoreRecyclerAdapter<Client, ClientAdapter.ClientHolder> {

private OnItemClickListener listener;

/**
 * Create a new RecyclerView adapter that listens to a Firestore Query.
 */
public ClientAdapter(@NonNull FirestoreRecyclerOptions<Client> options) {
    super(options);
}

@Override
protected void onBindViewHolder(@NonNull ClientHolder holder, int position, @NonNull Client model) {
    String lastNameWithComma = model.getLastName() + ", ";
    holder.textViewLastName.setText(lastNameWithComma);
    holder.textViewFirstName.setText(model.getFirstName());
}

@NonNull
@Override
public ClientHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.client_list_item, parent, false);
    return new ClientHolder(v);
}

public void deleteClient(int position) {
    getSnapshots().getSnapshot(position).getReference().delete();
}

class ClientHolder extends RecyclerView.ViewHolder {
    TextView textViewLastName;
    TextView textViewFirstName;

    public ClientHolder(@NonNull View itemView) {
        super(itemView);
        textViewLastName = itemView.findViewById(R.id.rv_tv_last_name);
        textViewFirstName = itemView.findViewById(R.id.rv_tv_first_name);

        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = getAdapterPosition();
                if (position != RecyclerView.NO_POSITION && listener != null) {
                    listener.onItemClick(getSnapshots().getSnapshot(position), position);
                }
            }
        });
    }
}

public interface OnItemClickListener {
    void onItemClick(DocumentSnapshot documentSnapshot, int position);
}

public void setOnItemClickListener(OnItemClickListener listener) {
    this.listener = listener;
}
}

You'll notice that there's a line commented out in the onSwiped() function inside of setUpRecyclerView(). When that line is used, other undesired behavior happens: the RecyclerView DOES "move up" lower cards to fill the gap left behind by the removed card, but whichever card is at the bottom of the RecyclerView gets duplicated and placed at the bottom. So if it looks like this:

A B C D

...and I swipe away C, then it looks like this:

A B D D

So I'm stuck with either the list not updating at all, or updating in a weird way that makes no sense.

If you're interested, I'm following this tutorial to the letter, and it isn't working:

https://www.youtube.com/watch?v=dTuhMFP-a1g&list=PLrnPJCHvNZuAXdWxOzsN5rgG2M4uJ8bH1&index=6&t=0s

Tyler M.
  • 115
  • 1
  • 1
  • 11
  • Please refer [this](https://stackoverflow.com/a/51337327/11967202).I solved my issue using this.Hope it helps. – harsh Feb 01 '20 at 12:11
  • @harshsuvagiya I checked that thread and a few others that I found through it, and none of the proposed solutions fix my problem. Can you tell me specifically what you did to fix yours? – Tyler M. Feb 04 '20 at 00:12

3 Answers3

0

If you're using FirestoreRecyclerAdapter, you don't need to notify your adapter manually. The changes in the database will reflect in your UI. Hence, no need to call adapter.notifyItemRemoved(position).

Also, delete(...) is executed asynchronously. So you need to add a callback to see if the document was successfully deleted:

public void deleteClient(int position) {
    getSnapshots().getSnapshot(position).getReference().delete()
         .addOnSuccessListener(new OnSuccessListener<Void>() {
              @Override
              public void onSuccess(Void aVoid) {
                  Log.d(TAG, "DocumentSnapshot successfully deleted!");
              }
    })
    .addOnFailureListener(new OnFailureListener() {
         @Override
         public void onFailure(@NonNull Exception e) {
              Log.w(TAG, "Error deleting document", e);
         }
    });
}
Christilyn Arjona
  • 2,173
  • 3
  • 13
  • 20
  • I added the code you provided, and it says "TAG has private access in FirestorerecyclerAdapter". When I remove the Log lines (and just implement everything else, so that nothing happens during onSuccess or onFailure), it compiles and runs fine but my problem still exists. – Tyler M. Jan 09 '20 at 01:19
0

Please refer this.I solved my issue using this.Hope it helps.

You must write that in onStart() method. Have you done that too?

harsh
  • 11
  • 2
  • I saw your comment above and responded. You're saying I need to add that snapshot listener to onStart()? – Tyler M. Feb 05 '20 at 06:23
  • Sorry for late reply! just get all data as db.collection().document().addSnapshotListner(new EventValueListner)... instead of db.collection().document().get().OnComplete.... – harsh Feb 17 '20 at 11:25
  • I'm sorry, I'm very confused! You say "instead of ... OnComplete..." but I don't know where you're seeing OnComplete in my code. I don't have any of that code inside of onStart(). In your initial post, you linked some code, but I don't know where to put that. In onStart(), I'm already calling adapter.startListening(), but that's all I do there. If possible, could you edit your post with exactly what you think I need to do? – Tyler M. Feb 19 '20 at 18:53
  • I'm haven't tried FirestoreRecyclerAdapter. I was talking about fetching data in more simplest way that making our own adapter and sending list into that adapter's constructor. So you have to fetch data according to above comment and send that list to constructor.An again i'm sorry if had confused you again!! – harsh Feb 20 '20 at 09:37
0

In Query query remove .orderby and this works for me.