Databinding variable inheritance with included layout
Asked Answered
C

3

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.

Collop answered 25/10, 2017 at 9:6 Comment(0)
P
11

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}"/>
Peroxidase answered 25/10, 2017 at 9:29 Comment(1)
Thanks I could have sworn I tried that and it didn't like it :)Collop
N
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>
Nester answered 25/3, 2019 at 16:46 Comment(0)
F
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}"
Forb answered 25/10, 2017 at 9:39 Comment(4)
I wish I could mark this as the answer as well it's very useful thank you!Collop
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?Dor
@Elliptica, default of visibility is false, if you want to show, you need to change it to trueForb
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).Dor

© 2022 - 2024 — McMap. All rights reserved.