2

I've read the following:

ArrayList not updating after using .clear()
notifydataSetChanged working but only showing 1 result in listview Android
notifyDataSetChanged() does not affect my adapterView
Android: notifyDataSetChanged(); not working
notifydatasetchanged() not working after onbackpressed()
Android notifyDataSetChanged not working
Android notifyDataSetChanged() not working

And still can't get my adapter to update from a new data set.

My code in debug mode is as follows:

public class PetInformationActivity extends AppCompatActivity
implements ConfirmDialogFragment.ConfirmDialogListener, MedicalInformationFragment.OnFragmentInteractionListener{

private static List<Assignment> listAssignments = new ArrayList<>();
private static AssignmentsAdapter mAdapter;
private static PetInformationViewModel sPetInformationViewModel;
.
.
.
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
.
.
.
/**
* get the providers' IDs assigned to the pet: fillAssignmentsArray()
*/
listAssignments = sPetInformationViewModel.getAssignedProviders(petId);

In debug at this point:
mAdapter = null
listAssignments = (ArrayList@5411) size = 0

Log.d(TAG, "listAssignments has " + listAssignments.size() + " Assignment objects in it");  
Log.d(TAG, "listAssignments is: " + listAssignments);   

In debug at this point:
mAdapter = null
listAssignments = (ArrayList@5461) size = 1
0 = (Assignment@5496)"Assignment{mType='Veterinarian', mProviderName='red vet'}"

mAdapter = new AssignmentsAdapter(this, listAssignments);   

In debug at this point:
mAdapter = (AssignmentsAdapter@5499)
listAssignments = (ArrayList@5461) size = 1
0 = (Assignment@5496)"Assignment{mType='Veterinarian', mProviderName='red vet'}"

mRecyclerView.setAdapter(mAdapter);
.
.
.
}

public void onResume(){
super.onResume();
Log.d(TAG, "Entered: onResume");
listAssignments.clear();
Log.d(TAG, "listAssignments is: " + listAssignments);

In debug at this point:
mAdapter = (AssignmentsAdapter@5499)
listAssignments = (ArrayList@5461) size = 0**

listAssignments = sPetInformationViewModel.getAssignedProviders(petId);
Log.d(TAG, "listAssignments is: " + listAssignments);

In debug at this point:
mAdapter = (AssignmentsAdapter@5499)
listAssignments = (ArrayList@5594) size = 1
0 = (Assignment@5607)"Assignment{mType='Veterinarian', mProviderName='red vet'}"

mAdapter.notifyDataSetChanged();
}  

At this point the screen is blank when it should show the text shown in Assignment@5607. It appears that the notififyDataSetChanged() notified mAdapter to the change from the text shown in onCreate (ArrayList@5461) to clear in onResume (ArrayList@5461) but not for the new call for data in onResume (ArrayList@5594).

This seems to indicate that when mAdapter was initialized to listAssignments it was set to use the ArrayList at address @5461. When a call for new data was made in onResume, at a) return from use of the Back button and (b) when this Activity first starts, a new ArrayList was created at a different address.

Therefore, it seems that I have not incorporated my ArrayList properly as all updates to listAssignments will result in a brand new ArrayList object that is never used to update mAdapter, which will always be updated to the initial ArrayList at address @5461.

I have searched for information as to how to properly bind an Adapter to an ArrayList but I haven't found anything that alludes to more than I have done. I assume that the intention for notifyDataSetChanged() is to update using an existing Adapter so that the existing Adapter doesn't have to be destroyed and a new one created at each data set change as inferred here:

notifyDataSetChanged() fails to update adapter onClick

Jeff
  • 431
  • 4
  • 16

2 Answers2

0

The reference you pass to the ArrayAdapter is different from the one assigned here:

listAssignments = sPetInformationViewModel.getAssignedProviders(petId);

As far as the ArrayAdapter is concerned, the listAssignments it has a reference to got cleared OnResume and nothing was ever added back to it. It doesn't know anything about the new listAssignments reference.

Try doing this:

public void onResume()
{
    super.onResume();
    Log.d(TAG, "Entered: onResume");
    listAssignments.clear();
    Log.d(TAG, "listAssignments is: " + listAssignments);
    List<Assignment> newList = sPetInformationViewModel.getAssignedProviders(petId);
    listAssignments.addAll(newList);
    mAdapter.notifyDataSetChanged();
}
pnavk
  • 4,552
  • 2
  • 19
  • 43
  • OK. That works in showing the correct value at the Activity's launch. However, notifyDataSetChanged() in onResume (as you suggested) is supposed to redisplay a new value upon return from another Activity. As this new value is obtained from a SQLite DB, there is no value passed from that Activity, the value is obtained in onResume as shown in your suggestion. However, it doesn't work, the old value is displayed and the new ArrayList address is new, not @5461. Therefore, the adapter is never updated! Do I need to replace the Adapter with a new one every time? Other posts say no. – Jeff Jan 19 '19 at 22:10
  • @Jeff - have you verified that the items returned by your ViewModel in OnResume are different when you return from another Activity? For what it's worth, if you want to refresh your list when you return from another activity, its better to use `startActivityForResult` and `onActivityResult` because `OnResume` runs for numerous other scenarios including starting an Activity, device rotation, etc. See this answer: https://stackoverflow.com/a/37227482/2754727 – pnavk Jan 19 '19 at 22:25
  • Yes, the address of ArrayList is 5461 at Activity launch and 5767 at return from the called Activity using onBackPressed() from the called Activity. I will be looking into those methods, however the called Activity simply stores a value in a DB table that is then accessed as shown in your onResume(), there is no value passed back to the calling Activity. Is there something other than List that should be used that would use the same base address throughout the life of the calling Activity (PetInformationActivity) or is there a way to set its base address to never change? – Jeff Jan 20 '19 at 00:00
  • I got it to work by adding: mAdapter = new AssignmentsAdapter(this, listAssignments); mRecyclerView.setAdapter(mAdapter); to your onResume suggestion. However, this work around may be very inefficient and does not resolve the fact that notifyDataSetChanged() doesn't work because the ArrayList address changes at each call and thus the latest data set is never used to update the display. – Jeff Jan 20 '19 at 00:35
  • @Jeff - you may want to try exposing a method inside your AssignmentsAdapter that modifies the list reference it holds internally. That may work without having to initialize a new adapter – pnavk Jan 23 '19 at 22:47
0

Alright, I'm going out on a limb here and say that notifyDataSetChanged() does not work for ArrayList because at each load its address changes, see my original post and comments to pnavk to see the details. As I understand it, Java provides no means to set in stone the address of an ArrayList as C or C++ does via a pointer, therefore the Adapter is set to a memory location of the original ArrayList object and so points to the same data at every notifyDataSetChanged() call even though Java has used a new memory location for that ArrayList object, or created a brand new ArrayList object of the same name.

Therefore, my answer for updating a RecyclerView from a new data set in an ArrayList is to reset the Adapter in onResume as new and then reassign that Adapter to your RecyclerView as I mentioned in my reply to pnavk:

mAdapter = new AssignmentsAdapter(this, listAssignments); 
mRecyclerView.setAdapter(mAdapter);
Jeff
  • 431
  • 4
  • 16