0

I'm using data binding, I want to set a text to a TextView via LiveData like this:

binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel

to set the textview

viewModel.toolbarTitle.postValue(R.string.profile_title_my_documents)

view model implementation

val toolbarTitle = MutableLiveData<@androidx.annotation.StringRes Int>()

and then the xml (removed irrelevant stuff)

<?xml version="1.0" encoding="utf-8"?>
<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="packagename.MyViewModel"
        />
  </data>

  <TextView
        android:id="@+id/title"
        style="@style/toolbar_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{viewModel.toolbarTitle}"
        />
</layout>

so basically if I remove the LiveData and use an Int value it works fine, but with LiveData I get an exception:

2020-04-09 14:38:59.929 19744-19744/ E/AndroidRuntime: FATAL EXCEPTION: main
    Process: , PID: 19744
    java.lang.RuntimeException: Failed to call observer method
        at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:226)

...
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: android.content.res.Resources$NotFoundException: String resource ID #0x0
        at android.content.res.Resources.getText(Resources.java:348)
        at android.widget.TextView.setText(TextView.java:5831)
        at ToolbarBindingImpl.executeBindings(ToolbarBindingImpl.java:146)
        at androidx.databinding.ViewDataBinding.executeBindingsInternal(ViewDataBinding.java:472)
        at androidx.databinding.ViewDataBinding.executeBindingsOn(ViewDataBinding.java:486)
        at MyBindingImpl.executeBindings(MyBindingImpl.java:151)
        at androidx.databinding.ViewDataBinding.executeBindingsInternal(ViewDataBinding.java:472)
        at androidx.databinding.ViewDataBinding.executePendingBindings(ViewDataBinding.java:444)
        at androidx.databinding.ViewDataBinding$OnStartListener.onStart(ViewDataBinding.java:1685)
        at java.lang.reflect.Method.invoke(Native Method)

it crashes most likely because when the screen open the livedata int value is O and it gets sent to the textview and crashes because there is no resId == 0

How to get around this? and second question, is this a good approach to use livedata like this or is it unnecessary?

UPDATE I used this to get around zero values:

android:text='@{viewModel.toolbarTitle == 0 ? "" : context.getString(viewModel.toolbarTitle)}'
TootsieRockNRoll
  • 3,218
  • 2
  • 25
  • 50

2 Answers2

1

Yes This is the right approch but here is some modification you can try.

in your ViewModel

val toolbarTitle = MutableLiveData<String>()

change it to a string.

While setting a value for it

viewModel.toolbarTitle.postValue(context.getString(R.string.profile_title_my_documents))

You can have application context and this will work.

Parth
  • 791
  • 7
  • 17
  • thanks this definitely works, but to be honest this would be extra effort to have to add getstring every time, I would've thought there is some way to automatically use resIds, but it makes sense since it would generally crash if the value is zero either way – TootsieRockNRoll Apr 09 '20 at 13:07
  • okay I think I figured it out, I put this instead: `android:text='@{viewModel.toolbarTitle == 0 ? "" : context.getString(viewModel.toolbarTitle)}'` – TootsieRockNRoll Apr 09 '20 at 13:10
  • 2
    Yes, Although you can try setting a default value to your live data. `val toolbarTitle = MutableLiveData<@androidx.annotation.StringRes Int>().apply { postValue(R.string.profile_title_my_documents) }` – Parth Apr 09 '20 at 13:11
  • 1
    you passed the Context in your ViewModel, it kind of defeats the purpose of ViewModel . Read https://stackoverflow.com/a/51451877/311420 – Raymond Chenon May 19 '21 at 23:13
0

You can also set direct from activity or fragment to xml. Here, what you are doing from activity you are setting that value in viewmodel, after that you are setting viewmodel from activity to xml.

Direct way is :

 <?xml version="1.0" encoding="utf-8"?>
<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="packagename.MyViewModel"
            />

        <variable
            name="title"
            type="String" />
    </data>

    <TextView
        android:id="@+id/title"
        style="@style/toolbar_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{title}"
        />
</layout>

From activity :

bindObject.title= "Title"
Shweta Chauhan
  • 6,739
  • 6
  • 37
  • 57