Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LiveData and ViewModel does not update textview

I am having a small issue, when i comment out the binding.setData(dataContainer); in onChanged while observing Livedata in my Activity, I am not able to update the UI, when I uncomment it, the UI is updated. Please guide and do a little code review if you feel the need.

I am having a Runnable that runs repeatedly after every x seconds. This is made Livedata which I observe in my Activity.

Thanks.

public class MainActivity extends AppCompatActivity {

  TextOnScreen dataContainer;
  ActivityMainBinding binding;
  ViewModelClass modelClass;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    modelClass = ViewModelProviders.of(this).get(ViewModelClass.class);
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    dataContainer = new TextOnScreen("hello");
    binding.setData(dataContainer);

    modelClass.getLiveData().observe(this, new Observer<String>() {
      @Override
      public void onChanged(String s) {
        Log.e("TAG", "--onChanged--" + s);
        dataContainer.onscreen.set(s);

        // this line allows the textview to update
        // other wise no change on UI is seen
        binding.setData(dataContainer);
      }
    });
  }
}


public class TextOnScreen {

  public final ObservableField<String> onscreen=new ObservableField<>();

  public TextOnScreen(String t) {
    onscreen.set(t);
  }

  public String getOnscreen() {
    return onscreen.get();
  }

  public void setOnscreen(String onscreen) {
    this.onscreen.set(onscreen);
  }
}


    public class ViewModelClass extends ViewModel{

      private MutableLiveData<String> liveData;

      public ViewModelClass() {
        ModelClass modelClass = new ModelClass();
        liveData= modelClass.generateName();
      }

      public LiveData<String> getLiveData() {
        return liveData;
      }
    }


public class ModelClass {

  MutableLiveData<String > data =new MutableLiveData<>();


  MutableLiveData<String> generateName(){
    final android.os.Handler handler=new android.os.Handler();
    Runnable runnable=new Runnable() {
      @Override
      public void run() {

        data.setValue(data.getValue()+"*");
        handler.postDelayed(this,1000);
      }
    };
    handler.postDelayed(runnable,2000);
    return data;
  }

}

<?xml version="1.0" encoding="utf-8"?>
<layout>
  <data>
    <variable
      name="data"
      type="dsfa.drish.com.livedatasample.TextOnScreen"/>
  </data>
<android.support.constraint.ConstraintLayout
  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"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".MainActivity">

  <TextView
    android:id="@+id/textview"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{data.onscreen}"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>
</layout>
like image 802
ashishdhiman2007 Avatar asked May 07 '18 11:05

ashishdhiman2007


People also ask

Is LiveData deprecated?

Don't worry, LiveData is not going to be deprecated.

Is LiveData necessary for ViewModel?

No, It is not mandatory to use LiveData always inside ViewModel, it is just an observable pattern to inform the caller about updates in data. If you have something which won't be changed frequently and can be accessed by its instance.


2 Answers

For me it was just missing:

binding.setLifecycleOwner(this);
like image 82
Flovettee Avatar answered Nov 04 '22 06:11

Flovettee


I could achieve the same desired result, while maintaining most of your code, by removing TextOnScreen and modifying MainActivity to:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ViewModelClass modelClass = ViewModelProviders.of(this).get(ViewModelClass.class);

    ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

    binding.setData(modelClass); // <-- set the ViewModel to be binded
    binding.setLifecycleOwner(this); // <-- this enables MutableLiveData to be update on your UI

    modelClass.getLiveData().setValue("hello");

    modelClass.getLiveData().observe(this, new Observer<String>() {
        @Override
        public void onChanged(String s) {
            Log.e("TAG", "--onChanged--" + s);
        }
    });
}

and on your activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
    <variable
        name="data"
        type="com.rafaelfukuda.stackoverflow.ViewModelClass"/>
</data>
    <android.support.constraint.ConstraintLayout
    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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{data.liveData}"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>
    </android.support.constraint.ConstraintLayout>
</layout> 

Other code improvements:

I would keep my models very simple (POJO with no MutableLiveData properties) and move the generateName to the ViewModel. That would maintain a proper MVVM structure and it will be very easy to understand and extend your code in the future.

Result:

Screen record of the desired result

like image 15
fukuda Avatar answered Nov 04 '22 06:11

fukuda