1 year ago
#356369

CorMatt
Nested Child Fragment does NOT observe LiveData properly
I have a Parent Fragment, called WaterFountainFragment, that has a nested Fragment progamatically inflated inside a FrameLayout that is dependant of an user's choice in a RadioGroup. If the user chooses one option, it inflates one Child and, when chosen the other option, it inflates another child (in the name of being the most concise as possible, I'll only list one of those child fragments, since the problem happens in both of them).
The user enters the data he wants and save it on the database, using LiveData and Room dependencies to do so. However, if the user wants to go back and check which data was saved in an specific entry, then we start to face trouble.
The problem is, the saved data is shown in the Parent Fragment but, unfortunately, it does NOT load inside the child fragment nested on it.
First, let me show the parent class in which I think everything is working normally:
WaterFountainFragment Class
public class WaterFountainFragment extends Fragment {
(...)
@Override
public void onStart() {
super.onStart();
//modelEntry is a ViewModel, which class ViewModelEntry will be shown later
modelEntry = new ViewModelEntry(requireActivity().getApplication());
//The ID of an entry is sent to this fragment through a bundle (via setArgument());
if (waterFountainBundle.getInt(FOUNTAIN_ID) > 0) {
//getOneWaterFountain is a method inside modelEntry to obtain an entry from the database through
//LiveData and Room
modelEntry.getOneWaterFountain(waterFountainBundle.getInt(FOUNTAIN_ID))
.observe(getViewLifecycleOwner(), this::loadFountainInfo);
}
}
//this is a RadioGroup Listener where it inflates the specific child fragment inside a FrameLayout
public void typeFountainListener(RadioGroup group, int checkedID) {
int index = getCheckedFountainType(group);
switch (index) {
case 0:
getChildFragmentManager().beginTransaction()
.replace(R.id.water_fountain_info, new WaterFountainSpoutFragment()).commit();
break;
case 1:
getChildFragmentManager().beginTransaction()
.replace(R.id.water_fountain_info, new WaterFountainOtherFragment()).commit();
break;
default:
break;
}
}
//method to fill all fields inside this fragment
private void loadFountainInfo(WaterFountainEntry waterFountain) {
fountainLocationValue.setText(waterFountain.getFountainLocation());
typeWaterFountain
.check(typeWaterFountain.getChildAt(waterFountain.getTypeWaterFountain()).getId());
if (waterFountain.getFountainTypeObs() != null)
fountainTypeObsValue.setText(waterFountain.getFountainTypeObs());
//At this point the RadioGroupListener is active already and, when checking a RadioButton, it will
//inflate the child and send a fragmentResult to this child fragment
getChildFragmentManager()
.setFragmentResult(InspectionActivity.LOAD_CHILD_DATA, waterFountainBundle);
}
}
Now we have one of the 2 nested child fragments that are inflated:
WaterFountainSpoutFragment Class
public class WaterFountainSpoutFragment extends Fragment {
@Override
public void onStart() {
super.onStart();
//the ViewModel is instantiated here with the same name and method as the
//one instantiated in the ParentFragment
modelEntry = new ViewModelEntry(requireActivity().getApplication());
//FragmentResultListener is active and it does recieve the signal from the parent Fragment.
getParentFragmentManager()
.setFragmentResultListener(InspectionActivity.LOAD_CHILD_DATA, this, (key, bundle) ->
//However, inside this resultListener, this observer seems to not be triggered
modelEntry.getOneWaterFountain(bundle.getInt(WaterFountainFragment.FOUNTAIN_ID))
.observe(getViewLifecycleOwner(), this::loadSpoutFountainData)
);
}
}
Here we have the ViewModel, the repository and the Dao classes/interfaces that were used on those 2 classes above
ViewModelEntry class
public class ViewModelEntry extends AndroidViewModel {
public ViewModelEntry(@NonNull Application application) {
super(application);
repository = new ReportRepository(application);
allEntries = repository.getAllSchoolEntries();
}
public LiveData<WaterFountainEntry> getOneWaterFountain(int waterFountainID) {
return repository.getOneWaterFountain(waterFountainID);
}
}
ReportRepository Class
public class ReportRepository {
private ReportDatabase db;
private final WaterFountainDao waterFountainDao;
public ReportRepository(Application application) {
waterFountainDao = db.waterFountainDao();
}
public LiveData<WaterFountainEntry> getOneWaterFountain(int waterFountainID) {
return waterFountainDao.getOneWaterFountain(waterFountainID);
}
}
WaterFountainDao Interface
@Dao
public interface WaterFountainDao {
@Query("SELECT * FROM WaterFountainEntry WHERE waterFountainID == :waterFountain")
LiveData<WaterFountainEntry> getOneWaterFountain(int waterFountain);
}
What I know/tested so far
- Using a Toast, I confirmed that getParentFragmentManager().setFragmentResultListener() is being called. Even more so, the bundle recieve the right data.
- If I use the
modelEntry.getOneWaterFountain(bundle.getInt(WaterFountainFragment.FOUNTAIN_ID)).observe(getViewLifecycleOwner(), this::loadSpoutFountainData)
outside theresultListener
, it does load the correct data into the child Fragment fields. - The data entered by the user IS being stored in the database. I confirmed that using the Database Inspector, so it is not a case where "the data is not being stored properly, hence why is not loading".
- I use the same method in other Parent/Child Fragments, using the same format of resultListener and it DOES load the data.
- Using another method for creating this ViewModel, like
modelEntry = new ViewModelProvider.AndroidViewModelFactory(requireActivity().getApplication()).create(ViewModelEntry.class);
in both parent and child fragments results in the same problem
What I SUPPOSE it might be the case
I have wondered that it might be a situation where I am choosing the wrong LyfecycleOwner but I don't know if that is the case, mainly because of what I put on item 4.
Any help would be greatly appreciated.
java
android
android-livedata
observer-pattern
0 Answers
Your Answer