Search code examples
androidandroid-viewmodelandroid-mvvm

ViewModel does not get updated in the layout when setters are called from method other than onCreate()


I have a class named MyInfoModel that has some instance variables in it to store data about the user and a method to notify the observers of the class.

private final List<ObserverInterface> observers = new ArrayList<>();
private int id;
private String name;
private String imageURL;

// Getters, Setters, Register and Unregister Methods for Instance Variables
// Also All Setter Methods call the notifyObservers() method after setting the values

private void notifyObservers(){
   Log.d(this.getClass().getName(), "Notifying All Observers");
   for (ObserverInterface observer : observers){
        observer.updateViews();
   }
}

I have an interface ObserverInterface that has only one method void updateViews(); which is overridden by other classes that implement this interface.

I am using Retrofit to request some data which gets updated in a static object in the Constants class.

public class Constants {
   public static MyInfoModel INFO_CONSTANTS = new MyInfoModel();
}

Also, I have a View Model MainActivityViewModel that extends ViewModel class and has variables, getters and setters to support the layout of the activity.

private int id;
private String name;
private String imageURL;

// Getters and Setters

I have used DataBinding in my layout like this

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="viewModel"
            type="com.itsyourap.app.viewModel.MainActivityViewModel" />
    </data>

<TextView <!-- Layout Tags -->
         android:text="@{viewModel.Name}" />

Then I have the MainActivity where I want to wait for any change in the values of variables of Constants.INFO_CONSTANTS, so I implement the ObserverInterface interface and Override the updateViews() method like this:-

public class MainActivity extends AppCompatActivity implements ObserverInterface {

    private MainViewModel mainViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        mainViewModel = new ViewModelProvider(this).get(MainActivityViewModel.class);
        mainBinding.setViewModel(mainViewModel);
        mainBinding.getViewModel().setName("Alpha Beta Gamma"); // Works
        mainBinding.getViewModel().setImageURL("somelink.jpg"); // Works
    }

    public MainActivity(){
        Constants.INFO_CONSTANTS.register(this);
    }

    @Override
    public void updateViews() {
        if (mainViewModel != null){
            Log.d(this.getClass().getName(), "Updating Views");
            mainViewModel.setName(Constants.INFO_CONSTANTS.getName()); // Does Not Work
            mainViewModel.setImageURL(Constants.INFO_CONSTANTS.getImageURL()); // Does Not Work
        }
    }
}

The problem I am facing that the updateViews() is called successfully (according to LogCat) but the Layout isn't updated at the runtime. I have tried using LiveData and MutableLiveData but they do not seem to work as well. Also I tried using mainBinding.setLifecycleOwner(this); but it didn't work as well.

Also calling the setters from the onCreate() method works perfectly but does not work from the updateViews() method.

How should I make it work? I just want the values in the layout to change as soon as the values in the ViewModel gets changed.

Thanks in Advance


Solution

  • Calling setViewModel() after modifying the variables solves the issue

    @Override
        public void updateViews() {
            if (mainViewModel != null){
                Log.d(this.getClass().getName(), "Updating Views");
                mainViewModel.setName(Constants.INFO_CONSTANTS.getName());
                mainViewModel.setImageURL(Constants.INFO_CONSTANTS.getImageURL()); 
                mainBinding.setViewModel(mainViewModel); // Solves The Problem
            }
        }