68

As per http://developer.android.com/tools/data-binding/guide.html#imports, we can have such simple expressions in visibility:

<TextView
..
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

But when I try to do the same in an include tag, like so:

<include
android:id="@+id/image_layout"
layout="@layout/image_layout"
android:visibility="@{notification.notifType == 0 ? View.VISIBLE : View.GONE}"/>

Then Studio not only shows the expression in red, but upon building it gives the following error in the auto-generated binding class:

Error:(138, 29) error: cannot find symbol method setVisibility(int)

Here's where the error occurs in the auto-generated binding class

// batch finished
if ((dirtyFlags & 0x3L) != 0) {
    // api target 1
    this.imageLayout.setVisibility(NotifTypeNotificatio1);
}
imageLayout.executePendingBindings();
Ishaan Garg
  • 3,014
  • 3
  • 25
  • 28
  • 2
    I suspect that you need that `android:visibility` expression to be on the root view of the `image_layout` resource, passing over `notification` or `notification.notifType`. – CommonsWare Mar 01 '16 at 13:10
  • @CommonsWare Yep. That worked. But the included layout is reusable, so I'm not very keen on setting visibility in that layout's root view. And it no doubt reduces readability. Would be real nice to get visibility working on the include tag itself. – Ishaan Garg Mar 01 '16 at 13:34

13 Answers13

82

I imagine what you are trying to do would look something like this:

In the layout you are including, specify a boolean variable and bind it to the desired view's visibility

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

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

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

    </data>

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="@{isVisible ? View.VISIBLE : View.GONE}"/>

</layout>

Then In your calling layout bind your value

<include
    android:id="@+id/image_layout"
    layout="@layout/image_layout"
    bind:isVisible="@{notification.notifType == 0}"/>
SeptimusX75
  • 2,155
  • 1
  • 15
  • 10
  • with binding conversion you dont have to have ```@{isVisible ? View.VISIBLE : View.GONE}"```, can get away with ```@{isVisible} ``` ```@BindingConversion fun convertBooleanToVisibility(visible: Boolean): Int { return if (visible) View.VISIBLE else View.GONE }``` – iamsujan Jun 01 '18 at 18:14
  • Is it not called a binding adapter? Not familiar with BindingConversion, but binding adapters do pretty much what you are describing. @iamsujan – Carson J. Feb 06 '19 at 18:33
  • 2
    they both are different, conversion does not take a view as the parameter, but the binding adapter does. Also, BindingConversion works with the existing attribute (android:xx) whereas adapters are more to create new attribute like app:imageUrl @CarsonJ – iamsujan Feb 07 '19 at 19:07
  • @iamsujan That's actually sick! I'll have to look into that. – Carson J. Feb 14 '19 at 16:45
10

Try:

this.imageLayout.getRoot().setVisibility(NotifTypeNotificatio1);
julzborrego
  • 109
  • 1
  • 3
8

You are able to pass all necessary parameters from parent xml into included xml via "http://schemas.android.com/apk/res-auto" namespace.

The syntax is <res-auto_namespace>:<variable_name> e.g. app:isVisible

For example

<?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">

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <include
            layout="@layout/include_user_image"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:isVisible="@{true}" />

    </android.support.design.widget.CoordinatorLayout>
</layout>

include_user_image.xml

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

    <data>

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

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

    </data>

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="@{isVisible ? View.VISIBLE : View.GONE}" />
</layout>
yoAlex5
  • 29,217
  • 8
  • 193
  • 205
8

Data binding is not needed, instead get a reference to the root view of the <include> layout and set its visibility. For example,

binding.myIncludeLayout.root.visibility = View.VISIBLE

apsommer
  • 586
  • 1
  • 9
  • 18
7

This is a bit late, but I have run across this recently.

I believe this is actually a bug in the Data Binding compiler as it is possible to set android:visibility attribute on <include> tag directly (ie. without Databinding).

Maks
  • 7,562
  • 6
  • 43
  • 65
6

Best Way

  1. You can directly pass Integer value of visibility like.
  2. You can set default value of visibility also by setting default=gone in binding.

For example this is included_layout.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

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

    </data>

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="@{visibility, default=gone}"
        />

</layout>

and use like

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

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

    </data>

   <include
     android:id="@+id/included_layout"
     layout="@layout/included_layout"
     app:visibility="@{View.VISIBLE}"/>

</layout>
Community
  • 1
  • 1
Khemraj Sharma
  • 57,232
  • 27
  • 203
  • 212
6

A better way.

On the top layout, declare the boolean or an observable field whose value toggle the visibility of the included layout. Then remember to give the included layout an id else it wont work

<?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>
        <import type="android.view.View"/>
        <variable
            name="show"
            type="Boolean" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:background="@color/colorPrimary">


        <include layout="@layout/progress"
            android:id="@+id/progress"
            android:visibility="@{show?View.VISIBLE:View.GONE}"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Edijae Crusar
  • 3,473
  • 3
  • 37
  • 74
  • 3
    `Then remember to give the included layout an id else it wont work` The whole reason it wasn't working. Very useful answer :) – Dan Apr 13 '20 at 09:00
6

To provide visibility in include tag, you need to convert the included layout to data-binding even though no data is passed on included layout.

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

    </data>
    <LinearLayout
        android:id="@+id/myblock"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/main_bg_alternative"
        android:orientation="vertical"
        android:paddingTop="15dp"
        android:paddingBottom="20dp">
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
   <TextView
        android:layout_width="wrap_content"
        android:text="@string/hello"
        android:layout_height="wrap_content"/>
   </LinearLayout>
</layout>

and on your activity XML, you can use visibility as

<include
     layout="@layout/include_body"
     android:visibility="@{results.size()>0 ? View.VISIBLE : View.GONE, default=gone}"
     tools:visibility="visible" />
Tony Lip
  • 554
  • 6
  • 14
  • 1
    This is the simplest answer and likely the most correct (using data binding as it is intended to be used without workarounds or unnecessary boilerplate code). It solved the problem for me. – Pilot_51 Oct 01 '21 at 15:23
3

I've bumped into the same error. Databinding was working with every view element except the included layouts.

There's no need to have a workaround for this, what I did is I've surrounded the included layout's root with <layout> tag and put a collapsed <data/> tag in there too - initialised the included layout for databinding.

Voila! It worked.

Marton
  • 121
  • 6
  • 1
    As I just commented on Tony Lip's answer, posted a couple days after yours and had more upvotes (it's a shame that happens), this is the simplest and certainly most correct answer. The `data` tag is unnecessary if there is nothing in it. – Pilot_51 Oct 01 '21 at 15:36
2

This is the direct approach, without having to pass the variable inside the included layout

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include
        layout="@layout/layout_errors"
        android:visibility="@{isVisible ? 0:1 }"
       />

</androidx.constraintlayout.widget.ConstraintLayout>

The above code was all I needed

The interesting thing is if I remove the above ternary operator and simplify it like this,

<include
    layout="@layout/layout_errors"
    android:visibility="@{isVisible}"
   />

with addition of an BindingAdapter class with following method,

@BindingAdapter("android:visibility")
    public static void setVisibility(View view, boolean isVisible) {
        view.setVisibility(isVisible ? View.VISIBLE : View.GONE);
    } 

Boom! the OP error invades my entire BuildOutput trace, interesting world of Android

iCantC
  • 2,852
  • 1
  • 19
  • 34
1

Thank to everybody, you help me a lot. Your answers were part of my succes on this trouble.

We wanted to set visibility of an image view, that was also target of some animation. The Motion Scene was taking the management of the visibility. Set the motion property app:visibilityMode to "ignore" solve everything.

<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<ConstraintSet android:id="@+id/expanded">
<!-- ... -->
    <Constraint
        android:id="@+id/myImageViewId"
        app:visibilityMode="ignore"/>
</ConstraintSet>
<!-- ... -->
</MotionScene>
Romain TAILLANDIER
  • 1,765
  • 10
  • 18
1

There are two ways, how to solve this issue.

I think, that's because DataBinding can't access included view without some internal id. If included view is not data-binding view, it doesn't have generated id and it's unaccessible for databinding.

  1. set to <include> it's own id (it must have width/height set too, otherwise id is ignored or used id from included root view):
<include
  android:id="@+id/includedViewCustomId"
  layout="@layout/image_layout"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:visibility="@{notification.notifType == 0}"/>
  1. Encapsulate included layout to <layout> tag (don't need to add empty <data></data> object..
<layout>
  <TextView ... />
</layout>
mtrakal
  • 6,121
  • 2
  • 25
  • 35
-2

You can find the included layout by its id and hide it using the visibility attribute.

For example in Kotlin,

binding.root.findViewById<CardView>(R.id.layout_inclulded).visibility = View.GONE

where,

root is the default attribute for the root element in the parent layout.

CardView is the root element of the included layout in my case.

R.id.layout_inclulded is the id of the included layout in the parent layout.

shaheer_
  • 406
  • 1
  • 4
  • 12