0

I have a FirebaseRecyclerAdapter displaying a list of objects with their states.

enter image description here

I click the arrow on the right and open an AlertDialog fragment where I can change the state of each item (check/uncheck the item).

enter image description here

The results from the AlertDialog fragment get sent back to the activity that contains the recyclerview by implementing an interface from the DialogFragment:

@Override
public void onDialogPositiveClick(Bundle bundle) {

    Hour hour = bundle.getParcelable("hour");

    mDatabase.child(protocolKey).child(String.valueOf(hour.getMilitaryHour())).setValue(hour);
    mProtocolAdapter.notifyDataSetChanged();
}

The data coming back from the Dialog Fragment is correct - the hour object in onDialogPositiveClick DOES SHOW THE CORRECT DATA WITH THE UPDATES THAT WERE MADE IN THE DIALOGFRAGMENT. AND THE DATA GETS UPDATED CORRECTLY IN THE DATABASE. However, the notifyDataSetChanged does not correctly update the list with their states.

I had a similar issue in another part of the project (currently stripped out to isolate things down to the current issue) and it turned out to be caused by a combination of using a listener and the views getting recycled. I'm guessing this is a similar instance but that fix (setting the listener to null first) is not working here.

Activity with Recyclerview:

public class DailyScheduleActivity2 extends AppCompatActivity implements HourlyTaskDialogFragment.HourlyTaskDialogListener  {

    private DatabaseReference mDatabase;
    private RecyclerView mProtocolRv;
    private String protocolKey;
    private ProtocolRecyclerViewAdapter mProtocolAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_daily_schedule);

        protocolKey = "daily/" + FirebaseAuth.getInstance().getCurrentUser().getUid() + "_" + new LocalDate();

        mDatabase = FirebaseDatabase.getInstance().getReference();

        Query query = mDatabase.child(protocolKey);

        FirebaseRecyclerOptions<Hour> options =
                new FirebaseRecyclerOptions.Builder<Hour>()
                        .setQuery(query, Hour.class)
                        .build();


        mProtocolRv = findViewById(R.id.rv_protocol);
        mProtocolRv.setHasFixedSize(false);
        LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
        mProtocolRv.setLayoutManager(mLayoutManager);
        mProtocolAdapter = new ProtocolRecyclerViewAdapter(this, mDatabase, protocolKey, options);
        mProtocolRv.setAdapter(mProtocolAdapter);
    }

    public static class ProtocolRecyclerViewAdapter extends FirebaseRecyclerAdapter<Hour, ProtocolRecyclerViewAdapter.HourHolder> {

        private final DailyScheduleActivity2 mParentActivity;

        ProtocolRecyclerViewAdapter(DailyScheduleActivity2 parent, DatabaseReference db, String protocolKey, FirebaseRecyclerOptions<Hour> options) {
            super(options);
            mParentActivity = parent;
        }

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

        @Override
        public void onBindViewHolder(@NonNull HourHolder holder, int position, final Hour currentHour) {

            holder.mHourCheckBox.setText(currentHour.toString());

            //https://stackoverflow.com/questions/25646048/how-to-convert-local-time-to-am-pm-time-format-using-jodatime
            LocalTime time = new LocalTime(currentHour.getMilitaryHour(), 0);
            DateTimeFormatter fmt = DateTimeFormat.forPattern("h:mm a");
            holder.mHour.setText(fmt.print(time));

            holder.mOpenTasks.setOnClickListener(null);
            holder.mOpenTasks.setOnClickListener(new View.OnClickListener() {

                @Override
                public void onClick(View view) {
                    DialogFragment hourlyTasksFragment = new HourlyTaskDialogFragment().newInstance(currentHour);
                    hourlyTasksFragment.show(mParentActivity.getSupportFragmentManager(), "HourlyTasks");
                }
            });
        }

        class HourHolder extends RecyclerView.ViewHolder {

            final TextView mHourCheckBox;
            final ImageView mOpenTasks;
            final TextView mHour;

            HourHolder(View view) {
                super(view);
                mHourCheckBox = (TextView) view.findViewById(R.id.cb_hour_all);
                mOpenTasks = (ImageView) view.findViewById(R.id.open_tasks);
                mHour = (TextView) view.findViewById(R.id.hour);
            }
        }
    }

    @Override
    public void onDialogPositiveClick(Bundle bundle) {

        Hour hour = bundle.getParcelable("hour");

        mDatabase.child(protocolKey).child(String.valueOf(hour.getMilitaryHour())).setValue(hour);
        mProtocolAdapter.notifyDataSetChanged();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mProtocolAdapter.startListening();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mProtocolAdapter.stopListening();
    }
}
ReyAnthonyRenacia
  • 17,219
  • 5
  • 37
  • 56
Michelle Williamson
  • 2,516
  • 4
  • 18
  • 19
  • Is it possible that `onDialogPositiveClick` is called from a different thread than the one that the Recycler is running on? I've had a lot of trouble with that in the past. – TheWanderer Sep 05 '18 at 21:24
  • Oh man. Hadn't thought about that. Not sure how I'd even troubleshoot it to find out. – Michelle Williamson Sep 05 '18 at 21:49
  • Just make a Handler running on the main thread and post your logic in it: `Handler handler = new Handler(Looper.getMainLooper());` – TheWanderer Sep 05 '18 at 21:51
  • Well, I tried the obvious of wrapping the contents of onDialogPositiveClick in runOnUiThread and had the same problem in addition to a few new ones. – Michelle Williamson Sep 05 '18 at 21:53
  • What are those new ones? Maybe the Recycler is created on a different thread. – TheWanderer Sep 05 '18 at 21:54
  • Oops. Disregard that. The new problems were from something else I was doing in a different activity. So the problem still persists with runOnUiThread but no new problems were created. – Michelle Williamson Sep 05 '18 at 21:58
  • Well looking at the readme for the adapter, I don't think you're supposed to call `notifyDataSetChanged()` yourself. Try moving `startListening()` to `onCreate()` and `stopListening()` to `onDestroy()`. – TheWanderer Sep 05 '18 at 22:08
  • Still no go. Moved startListening to onCreate and stopListening to onDestroy and tried both with and without notifyDataSetChanged and same results. – Michelle Williamson Sep 05 '18 at 22:15
  • I'm out of ideas then :/. I'd probably need to look at the UI flow (you're probably using the wrong DB element or missing some sort of link, but idk), but I have a headache, so I can't really think. – TheWanderer Sep 05 '18 at 22:18
  • Oh, good grief. I got it. It wasn't even related to the recycler view or adapter. Grr. Apparently, you can't use a setter of setState in Java/Android. It overrides some method. I changed my state property to completed and used setCompleted and isCompleted and voila, it started working. Or maybe it was the fact that I only had the property in the parent object and not the objects that extend the parent. I dunno but it's working. [Bangs head on desk] Thanks for trying to help! – Michelle Williamson Sep 05 '18 at 22:40
  • So...it wasn't either of those. It was the fact that I was setting the state/completed in 2 different places in the POJO class. – Michelle Williamson Sep 06 '18 at 00:49

0 Answers0