Reputation: 345
I am developing an activity using MutableLiveData to keep my fragments up-to-date with the results retrieved from a search, but the Observer is not being triggered after the list of results is updated.
I have, on the main activity, an EditText where I type what I want to search and, right below it, a ViewPager widget with three options, to where the results are subdivided into three categories (on three different fragments). When I first enter the activity, I run an initial search to retrieve data and populate the three fragments. This works perfectly.
SearchActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
mViewModel = ViewModelProviders.of(this).get(SearchViewModel.class);
/* Init TabMenu */
TabLayout tabSearchMenu = findViewById(R.id.tabSearchMenu);
ViewPager vpSearch = findViewById(R.id.vpSearchResult);
TabSearchPageAdapter tabSearchPageAdapter = new TabSearchPageAdapter(getSupportFragmentManager(), tabSearchMenu.getTabCount());
vpSearch.setAdapter(tabSearchPageAdapter);
vpSearch.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabSearchMenu));
/* Init Search */
etxtSearch = findViewById(R.id.etxtSearch);
etxtSearch.setOnEditorActionListener((textView, actionId, keyEvent) -> {
if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_DONE
|| keyEvent.getAction() == KeyEvent.ACTION_DOWN || keyEvent.getAction() == KeyEvent.KEYCODE_ENTER) {
mViewModel.searchText(etxtSearch.getText().toString());
}
return false;
});
}
SearchViewModel.java
public class SearchViewModel extends ViewModel {
private MutableLiveData<List<ProductItemModel>> listProducts = new MutableLiveData<>(new ArrayList<>());
private List<ProductItemModel> mProducts = new ArrayList<>();
[...]
public void searchText(String text) {
// make search...
// ...
// ...
// Process Json result.
CompletionHandler completionHandler = (content, error) -> {
if (content != null && content.length() > 0) {
Log.d(TAG, content.toString());
Log.d(TAG, "searchText: found result.");
listProducts = new MutableLiveData<>(new ArrayList<>());
mProducts = new ArrayList<>();
try {
JSONArray products = content.getJSONArray("hits");
for (int i = 0; i < products.length(); i++) {
JSONObject prodObject = products.getJSONObject(i);
ProductItemModel prodItem = new ProductItemModel(prodObject);
mProducts.add(prodItem);
Log.d(TAG, "searchText: " + prodObject.toString());
}
listProducts.setValue(mProducts);
} catch (JSONException e) {
e.printStackTrace();
}
} else {
Log.d(TAG, "searchText: no hits");
}
};
}
[...]
MutableLiveData<List<ProductItemModel>> getProductList() {
if (listProducts == null)
return new MutableLiveData<>(new ArrayList<>());
return listProducts;
}
}
SearchProductFragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InitFragment();
}
[...]
private void InitFragment() {
mViewModel = ViewModelProviders.of(Objects.requireNonNull(getActivity())).get(SearchViewModel.class);
mViewModel.SearchProducts();
final Observer<List<ProductItemModel>> listObserver = result -> {
Log.d(TAG, "listObserver: observer modified.");
if (result.size() > 0) {
InitProductList(result);
}
};
mViewModel.getProductList().observe(getActivity(), listObserver);
}
Already tried to move the method InitFragment()
inside the fragment to the OnActivityCreated
, but it didn't worked either.
I know for a fact that what is not working is the observer not being triggered. I've made tests with breakpoints.
As you guys can see, when the fragment is initialized the method SearchProducts()
is called and the result is sent perfectly to the MutableLiveData and the observer is triggered. But whenever I run a search calling the function searchText()
, retrieve the results from it, and update the value of my variable (works perfect till this point), the observer is not triggered.
Extra info:
Any ideas?
Upvotes: 0
Views: 488
Reputation:
The problem is you're returning different instances of MutableLiveData
, but your fragment is only observing the first instance that it gets in your InitFragment()
method.
You're also setting listProducts
to multiple instances of MutableLiveData
(first at its declaration site and again in the searchText()
method) rather than just updating the value that it holds.
To elaborate, you don't need the null check in the getProductsList()
method because listProducts
is initialized to a new instance of MutableLiveData
when the view model is created.
Now, the value held by that instance of MutableLiveData
might be null.. and it is only that value which you should be updating via setValue()
. Any null checks on the value of the MutableLiveData
could be done in the Observer
you created in your fragment.
Upvotes: 2