MutableLiveData not updating in UI
I am unable to get the MutableLiveData in the UI to update to a new value either by using setValue() or postValue(). I can get this to work by changing the MutableLiveData to ObservableData but the point is to use LiveData not ObservableData.
You probably don't set a lifecycle owner for your binding
object. From the JAVADOC
for setLifecycleOwner()
this assumption sounds even more convincing (please see it here):
If a LiveData is in one of the binding expressions and no LifecycleOwner is set, the LiveData will not be observed and updates to it will not be propagated to the UI.
So, I took a closer look at your code.
Observation
I noticed that you don't show how your ViewModel
and binding are created. So, I have created a simple project meeting your requirements and updating the UI properly - setValue
or postValue
of LiveData
were changing the corresponding View
values seamlessly. Then, I filled in the missing pieces in your code based on my working example. Please see the updated code below.
Relevant Code
MainViewModel
public class MainViewModel extends ViewModel {
private MutableLiveData<String> photosCount = new MutableLiveData<>();
private MutableLiveData<Boolean> takePhoto = new MutableLiveData<>();
public MainViewModel() {
photosCount.setValue("0");
takePhoto.setValue(true);
}
public MutableLiveData<String> getPhotosCount() {
return photosCount;
}
public MutableLiveData<Boolean> getTakePhoto() {
return takePhoto;
}
public void createImageFile() {
...
photosCount.setValue(String.valueOf(count));
...
}
...
}
fragment_main.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="MainViewModel"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@={viewModel.photosCount}" />
</LinearLayout>
</layout>
MainFragment
public class MainFragment extends Fragment {
private MainViewModel mainViewModel;
private FragmentMainBinding binding;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_main, container,
false);
binding.setLifecycleOwner(this);
mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
binding.setViewModel(mainViewModel);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mainViewModel.getTakePhoto().observe(getViewLifecycleOwner(), takePhoto -> {
if (takePhoto) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
File photo = mainViewModel.storeImage();
if (photo != null) {
Uri photoURI = FileProvider.getUriForFile(getActivity(),"com.example.fileprovider", photo);
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
this.startActivityForResult(intent, 0);
}
}
}
});
binding.executePendingBindings();
}
}
Conclusion
Please note that a lifecycle owner must be set for binding
as binding.setLifecycleOwner(this)
. MainViewModel
must be set for binding
too. Otherwise, it won't work properly. When in doubt, always check the official documentation here.
I'm not sure if this is the answer to your question, but changing
myViewModel = new ViewModelProvider(this).get(MyViewModel.class);
to
myViewModel = new ViewModelProvider(requireActivity()).get(MyViewModel.class);
did the trick for me. According to the official docs, when initializing a ViewModel in a fragment, you need to use requireActivity()