Search code examples
androidfirebaseandroid-recyclerviewfirebaseui

RecyclerView binded to FirebaseRecyclerAdapter, wrong row deleted when checked a checkbox


I bind a recyclerView to FirebaseRecyclerAdapter, and set OnClickListener for checkboxes in ViewHolder.

My idea:
When a checkbox of a row is checked, the adapter will getRef(clickedPosition) and call removeValue to delete the row from Firebase database. After that, the populateViewHolder is called and the application will show the latest data from the database.

Problem: If I delete from the bottom of the list upwards, there is no problem. However, if randomly check checkboxes, seems the wrong row will be deleted, and sometimes even will have NULL position and cause the Android app crash. I have Log the position, seems the position of checkboxes is not updated correctly after a row is deleted.

Here is my code:

public Firebase mRef;

// UI
RecyclerView mRecyclerView;

FirebaseRecyclerAdapter<Task, MessageViewHolder> adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_my_task);
   ...
   ...
   Firebase.setAndroidContext(this);
   mRef = new Firebase("https://what-to-do-list.firebaseio.com/todoItems");

   mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
   mRecyclerView.setHasFixedSize(true);
   mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
}


@Override
protected void onStart() {
    super.onStart();

    adapter =
            new FirebaseRecyclerAdapter<Task, MessageViewHolder>(
                    Task.class,
                    R.layout.custom_row_view,
                    MessageViewHolder.class,
                    mRef
            ) {
                @Override
                protected void populateViewHolder(MessageViewHolder messageViewHolder, Task t, int i) {

                    messageViewHolder.mText.setText(t.getName());
                    messageViewHolder.chkBox.setTag(i);

                    messageViewHolder.chkBox.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            CheckBox cb = (CheckBox) v;
                            int clickedPos = ((Integer) cb.getTag()).intValue();


                            Log.v("POSITION: ", String.valueOf(clickedPos));
                            adapter.getRef(clickedPos).removeValue();
                        }
                    });
                }      
            };
    mRecyclerView.setAdapter(adapter);
}

@JsonIgnoreProperties(ignoreUnknown = true)
public static class MessageViewHolder
        extends RecyclerView.ViewHolder {
    TextView mText;
    CheckBox chkBox;

    public MessageViewHolder(View v) {
        super(v);
        mText = (TextView) v.findViewById(R.id.list_item_name);
        chkBox = (CheckBox) v.findViewById(R.id.task_chkbox);
    }
}

Solution

  • Change your populateViewHolder to:

    @Override
    protected void populateViewHolder(MessageViewHolder messageViewHolder, Task t, int i) {
    
        final DatabaseReference ref = getRef(i);
        messageViewHolder.mText.setText(t.getName());
        messageViewHolder.chkBox.setTag(ref);
    
        messageViewHolder.chkBox.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                CheckBox cb = (CheckBox) v;
                String key = cb.getTag().getKey();
    
                adapter.getRef(key).removeValue();
             }
         });