Using a nullable string resource id from view model in Android data binding

I am trying to map API errors (exceptions) to String resources in my view model. My view model looks like the following.

@HiltViewModel
class AccountViewModel @Inject constructor(accountRepository: AccountRepository) : ViewModel() {

  val isLoadingProfile = MutableLiveData(false)
  val profile = MutableLiveData<Profile>()
  val profileLoadError = MutableLiveData<Int>()

  fun loadProfile() {
    isLoadingProfile.postValue(true)
    viewModelScope.launch(Dispatchers.IO) {
      try {
        profile.postValue(accountRepository.getProfile())
      } catch (e: Throwable) {

        // assign String resource id to profileLoadError
        profileLoadError.postValue(
          when (e) {
            is NetworkError -> R.string.network_error
            else -> R.string.unknown_error
          }
        )

      } finally {
        isLoadingProfile.postValue(false)
      }
    }
  }
}

And my error text view XML looks like the following.

<TextView
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="@{viewModel.profileLoadError ?? ``}"
  android:visibility="@{viewModel.profileLoadError != null ? View.VISIBLE : View.GONE}" />

But I am getting a class cast exception when I try to run it.

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.CharSequence

If I remove the null coalescing operator from the data binding expression like the following, I get a resource not found exception.

android:text="@{viewModel.profileLoadError}"
Caused by: android.content.res.Resources$NotFoundException: String resource ID #0x0

I know that it is kind of an idiotic question, but how do I fix this?

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

is NetworkError -> R.string.network_error ->in this line you are returning String to post value for profileLoadError of type Int.That’s why you got ClassCastException.

So change the type of LiveData to String

val profileLoadError = MutableLiveData()

Solution 2

After the suggestion from @androidLearner, I created a binding adapter for the android:text attribute.

@BindingAdapter("android:text")
fun TextView.setText(@StringRes resId: Int?) {
  resId ?: return
  if (resId == ResourcesCompat.ID_NULL) {
    text = ""
  } else {
    setText(resId)
  }
}

Now, I can pass the string id without worrying about its nullability.

android:text="@{viewModel.profileLoadError}"

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply