Android Data Binding - how to use ViewStub with data binding
Asked Answered
C

8

22

Is there anyway to use viewStubs with dataBinding ? can ViewStubProxy help ?

My stub current looks like this:

    <ViewStub
  android:id="@+id/stub_import"
  android:inflatedId="@+id/panel_import"

  android:layout="@layout/progress_overlay"

  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_gravity="@{myobject.bottom ? bottom:top}" />

But this layout will be replaced when i inflate the viewStub so how can ViewStubs be used with android dataBinding ?

this is what i see from the docs:

ViewStubs

ViewStubs are a little different from normal Views. They start off invisible and when they either are made visible or are explicitly told to inflate, they replace themselves in the layout by inflating another layout.

Because the ViewStub essentially disappears from the View hierarchy, the View in the binding object must also disappear to allow collection. Because the Views are final, a ViewStubProxy object takes the place of the ViewStub, giving the developer access to the ViewStub when it exists and also access to the inflated View hierarchy when the ViewStub has been inflated.

When inflating another layout, a binding must be established for the new layout. Therefore, the ViewStubProxy must listen to the ViewStub's ViewStub.OnInflateListener and establish the binding at that time. Since only one can exist, the ViewStubProxy allows the developer to set an OnInflateListener on it that it will call after establishing the binding.

Cherianne answered 11/1, 2016 at 1:40 Comment(0)
A
34

Just set the listener as the doc says :

mBinding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
    @Override
    public void onInflate(ViewStub stub, View inflated) {
        ViewStubBinding binding = DataBindingUtil.bind(inflated);
        binding.setModel(model);
    }
});



public void inflateViewStub(View view) {
    if (!mBinding.viewStub.isInflated()) {
        mBinding.viewStub.getViewStub().inflate();
    }
}
Arborization answered 11/1, 2016 at 1:52 Comment(3)
As a note, ViewStubBinding isn't a class, it's the binding class for the view stub. It took me awhile to figure that out; hopefully, this comment will help someone else.Maritzamariupol
Can not resolve a symbol "ViewStubBinding". Any solution for this?Sommer
@Sommer ViewStubBinding is a generated class. If you xml name is jatin_activity it will be JatingActivityBindingExequatur
U
10

Declare your xml namespace, and pass the variable through that. This works with <include>, too, btw. Here's an example:

main_layout.xml:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:my-namespace="http://schemas.android.com/apk/res-auto">
    <data>
        <variable name="myData" type="com.example.SomeModel" />
    </data>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <ViewStub
            android:id="@+id/view_stub"
            android:inflatedId="@+id/view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout="@layout/another_layout"
            my-namespace:data="@{myData}"
            />

    </RelativeLayout>
</layout>

another_layout.xml:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
<!-- No need to declare my-namespace here -->
    <data>
        <variable name="data" type="com.example.SomeModel" />
    </data>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{data.someValue}" />

    </RelativeLayout>
</layout>

So now you just have to call inflate() and the layout will have the data. You can check the generated binding class to verify this. It even has type safety, so you can't pass any other type into data.

Unwonted answered 31/5, 2017 at 10:41 Comment(4)
When I use my-namespace:data I get an error because the binding compiler can't find data, but if I change it to my-namespace:viewModel it works.Schaffel
great answer! didn't know u can do that. And yes you need to use the name of the variable from the target xml file. "viewModel", "item", "data" whatever you're trying to setAntifebrile
No need to access views directly to call inflate(). Just bind ViewStub's visibility: when it goes to VISIBLE it inflates.Gusset
What about two way databinding. How does that work?Sext
F
4

Just to elaborate on @andrew-classen's accepted answer above, and include the @user9113597's answer as well:

In your layout that contains the stub (e.g. my_fragment.xml)

    <data class="MyDataBinding">
        ...
    </data>

    ...
    <ViewStub 
        android:id="@+id/stub_import"
        ... />

In your activity or fragment (example below is using a fragment):

MyDataBinding mBinding;
MyViewModel mViewModel;

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
    @Nullable Bundle savedInstanceState)
{
    mBinding = DataBindingUtil.inflate(inflater, R.layout.my_fragment, container, false); 
    mBinding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() 
    {
        @Override
        public void onInflate(ViewStub stub, View inflated)
        {
            ViewDataBinding dataBinding = DataBindingUtil.bind(inflated);
            binding.setVariable(BR.mViewModel, mViewModel));
        }
    });
}

public void inflateViewStub(View view) {
if (!mBinding.viewStub.isInflated()) {
    mBinding.viewStub.getViewStub().inflate();
}

}

In your stub layout:

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

    <data>

        <variable
            name="mViewModel"
            type="com.example.myapp.MyViewModel" />
    </data>
    ...
Ferine answered 22/11, 2018 at 23:22 Comment(0)
T
2

Another way I did this was using the ViewStubProxy. The documentation/source code for ViewStubProxy is good.

When you create your new ViewStubProxy, pass in your ViewStub in the constructor. Then ViewStubProxy.setContainingBinding(<pass in your root layout binding>). If you don't do this you'll get nulls.

Then instead of setting the ViewStub.setOnInflateListener (the ViewStubProxy will set one internally for you), set the ViewStubProxy.setOnInflateListener to set your binding variables and LifecycleOwner.

Sample code:

   private void setBindings(){
      //usual outside layout binding steps
      binding = (MeasurementsLayoutBinding) getBinding();
      binding.setViewModel(viewModel());
      binding.setLifecycleOwner(this);

      //cartSideStubView is the root layout stubview
      cartViewStubProxy = new ViewStubProxy(cartSideStubView);
      cartViewStubProxy.setContainingBinding(binding);

      cartViewStubProxy.setOnInflateListener(((viewStub, view) -> {

         cartViewStubProxy.getBinding().setVariable(BR.viewModel, viewModel());
         cartViewStubProxy.getBinding().setLifecycleOwner(this);

        //after inflation you can find your viewstub views

         cartHitchToFixedAxleInputField = view.findViewById(R.id.cart_hitch_to_fixed_axle_input_field);       


      }));

   }
Tepee answered 7/6, 2019 at 16:6 Comment(0)
P
1

Set the listener to get access to inflated layout after successful inflating of viewStub

mBinding.viewStub.setOnInflateListener(new OnInflateListener() {
  @Override
  public void onInflate(ViewStub stub, View inflated) {
    ViewStubBinding binding = DataBindingUtil.bind(inflated); //call after succesful inflating of viewStub
  // set data here 
  }
});

Then inflate your viewStub

mBinding.viewStub.getViewStub().inflate();
Precedential answered 25/4, 2017 at 13:51 Comment(0)
C
1

I Also faced This Type of Issues, finally I got a solution and Its Perfectly Working.

ViewStub Holding Layout,,

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

    <data class="MBinding">

        <variable
            name="mmmmm"
            type="com.vvv.bbb.fragments.MFragment" />

    </data> 
  --------------------
  --------------------
   <ViewStub
     android:id="@+id/stub_import"
     -------------------
     -------------------
        <ViewStub/>
  --------------------
  --------------------
<layout/>

layout Inside ViewStub 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">

    <data class="MViewStubLayoutBinding">

        <variable
            name="mmmmm"
            type="com.vvv.bbb.fragments.MFragment" />

    </data>
  ----------------
  ----------------
  ----------------

code Inside the Class is,,,

public Class KKKKKK() {
 -----------------
MBinding mBinding;
MViewStubLayoutBinding mviewStubLayoutBinding;

 -----------------------------  
 -----------------------------

mBinding.stubImport.getViewStub().setLayoutResource(R.layout.progress_overlay);

   mBinding.stubImport.setOnInflateListener(new ViewStub.OnInflateListener() {
            @Override
            public void onInflate(ViewStub viewStub, View view) {
                mviewStubLayoutBinding=DataBindingUtil.bind(view);
            }
        });

        autoCheckBinding.vsAutoDetailsOne.getViewStub().inflate();
Cantonese answered 20/12, 2017 at 10:55 Comment(0)
M
1

if you are working with ViewModels, then you also need to set the lifecycle owner for inflated view, Remember to set the same owner that you set to mBinding variable during fragment/activity view.

mBinding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
    @Override
    public void onInflate(ViewStub stub, View inflated) {
        ViewStubBinding binding = DataBindingUtil.bind(inflated);
        binding.setModel(model);
        binding.setLifeCycleOwner(this) // setLifeCycleOwner
    }
});



public void inflateViewStub(View view) {
    if (!mBinding.viewStub.isInflated()) {
        mBinding.viewStub.getViewStub().inflate();
    }
}

hope this help someone.

Milquetoast answered 12/8, 2020 at 11:48 Comment(0)
L
0

I prefer inflating ViewStub layouts this way:

MainViewbinding mainBinding = MainViewbinding.inflate(LayoutInflater.from(context));

ItemStubBinding stubBinding = DataBindingUtil.inflate(LayoutInflater
.from(context), R.layout.item_stub, binding.container, true); // binding.container is the ViewGroup

This way, I can immediately use the views from the ViewStub binding.

Laaland answered 29/8, 2022 at 11:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.