12

I am learning data binding and mvvm. I have an issue where I would like a BaseViewModel.kt to include some UI related variables such as an isLoading flag and loadingText. When a network request is made, I set isLoading to true and some child of my base view model should set the text. For example for a LoginViewModel.kt the text might be 'logging in'. Is it possible to pass these variables to an included base layout?

So a login_activity.xml might include this in it's layout:

    <data>
        <import type="android.view.View" />
        <variable
            name="viewModel"
            type="core.sdk.ui.login.LoginViewModel" />
    </data>

<!-- Various click listeners using the viewModel variable -->

    <include
        android:id="@+id/progress_include"
        layout="@layout/progress_bar"
        android:visibility="@{viewModel.isLoading ? View.VISIBLE : View.GONE}"
        bind:viewModel="@{viewModel}"/>

Now I want my progress_bar.xml to be nice and generic and use the base view model:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="core.sdk.ui.login.LoginActivity">

<data>

    <import type="android.view.View" />

    <variable
        name="viewModel"
        type="core.sdk.ui.base.BaseViewModel" />

</data>

<LinearLayout
    android:id="@+id/circular_progress"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:gravity="center"
    android:orientation="vertical">

    <android.support.v4.widget.ContentLoadingProgressBar
        style="@style/Widget.AppCompat.ProgressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/progress_text"
        style="@style/TextAppearance.AppCompat.Subhead"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fontFamily="sans-serif-thin"
        android:gravity="center_horizontal"
        android:text="@{viewModel.loadingText}"
        android:textStyle="italic"
        tools:text="loading..." />
</LinearLayout>

The error I get is something like

****/ data binding error ****msg:Cannot find the setter for attribute 'bind:viewModel' with parameter type core.sdk.ui.login.LoginViewModel

If this isn't possible the only alternative I can see is to remove the include and copy and paste the progress bar + text to every view model which isn't very nice.

ericn
  • 10,647
  • 12
  • 60
  • 105
Daniel Wilson
  • 16,545
  • 10
  • 76
  • 108

3 Answers3

10

I think you should cast it in the binding:

<include
    android:id="@+id/progress_include"
    layout="@layout/progress_bar"
    android:visibility="@{viewModel.isLoading ? View.VISIBLE : View.GONE}"
    bind:viewModel="@{(core.sdk.ui.base.BaseViewModel)viewModel}"/>
Xavier Rubio Jansana
  • 5,998
  • 1
  • 23
  • 46
5

for those who still don't have solution, just check your code and check whether the name of "bind" attribute is same as the one used in the included layout

<include
       ...
        bind:viewModel="@{viewModel}"/>

and

<data>
    ...
    <variable
        name="viewModel"
        type="core.sdk.ui.base.BaseViewModel" />

</data>
Aleyam
  • 931
  • 1
  • 9
  • 10
3

You can use binding with include like

loading_view.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    >

    <data>

        <import type="android.view.View"/>

        <variable
            name="visibility"
            type="boolean"
            />

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

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ff0"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        android:visibility="@{visibility?View.VISIBLE:View.GONE}"
        >
        <ProgressBar
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:text="@{text}"
            tools:text="AA"
            />
    </LinearLayout>
</layout>

Using

<include
        layout="@layout/loading_view"
        app:visibility="@{viewModel.loadingCondition}"
        app:text='@{"AA"}'
        />

you can also pass the hard value like

app:visibility="@{false}"
app:text="@{@string/loading_text}"
Linh
  • 43,513
  • 18
  • 206
  • 227
  • I wish I could mark this as the answer as well it's very useful thank you! – Daniel Wilson Oct 25 '17 at 17:01
  • I've done this but the text is just an empty string (doesn't display), not any value, no matter what I put there. I've left off the visibility part, so I know it's not that. Help? – Elliptica May 01 '19 at 04:42
  • @Elliptica, default of visibility is false, if you want to show, you need to change it to true – Linh May 01 '19 at 04:47
  • 1
    In your example, yes, but in mine default is true. I don't set visibility to false. What's more, I've put a background on my TextView so I can see the view is rendering, it's just empty (no text). – Elliptica May 01 '19 at 05:09