Android Data Binding - как использовать ViewStub с привязкой данных

Есть ли в любом случае использовать ViewStubs с DataBinding? может помочь ViewStubProxy?

Мой ток заглушки выглядит так:

    <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}" />

Но этот макет будет заменен, когда я надуваю ViewStub, так как же можно использовать ViewStubs с привязкой данных Android?

это то, что я вижу из документов:

ViewStubs

ViewStubs немного отличаются от обычных видов. Они начинаются невидимыми, и когда их либо делают видимыми, либо явно просят их надуть, они заменяют себя в макете, раздувая другой макет.

Поскольку ViewStub по существу исчезает из иерархии View, View для объекта привязки также должен исчезать, чтобы разрешить сбор. Поскольку представления являются окончательными, объект ViewStubProxy занимает место ViewStub, предоставляя разработчику доступ к ViewStub, когда он существует, а также доступ к раздутой иерархии View, когда ViewStub был раздут.

При накачивании другого макета необходимо установить привязку для нового макета. Поэтому ViewStubProxy должен прослушать ViewStub ViewStub.OnInflateListener и установить привязку в это время. Поскольку может существовать только один, ViewStubProxy позволяет разработчику установить для него OnInflateListener, который он будет вызывать после установления привязки.

9 ответов

Решение

Просто установите слушателя так, как говорит доктор:

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();
    }
}

Объявите свое пространство имен xml и передайте переменную через него. Это работает с <include>тоже кстати. Вот пример:

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>

Так что теперь вам просто нужно позвонить inflate() и макет будет иметь данные. Вы можете проверить сгенерированный класс привязки, чтобы убедиться в этом. У него даже есть безопасность типов, так что вы не можете передать любой другой тип в data,

Просто, чтобы уточнить принятый ответ @ andrew-classen выше, а также включить ответ @user9113597:

В вашем макете, который содержит заглушку (например, my_fragment.xml)

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

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

В вашей деятельности или фрагменте (пример ниже использует фрагмент):

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();
}

}

В вашем макете заглушки:

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

    <data>

        <variable
            name="mViewModel"
            type="com.example.myapp.MyViewModel" />
    </data>
    ...

Другой способ, которым я это сделал, - использование ViewStubProxy. Документация / исходный код для ViewStubProxy хороши.

Когда вы создаете свой новый ViewStubProxy, передайте свой ViewStub в конструктор. потомViewStubProxy.setContainingBinding(<pass in your root layout binding>). Если вы этого не сделаете, вы получите нули.

Затем вместо установки ViewStub.setOnInflateListener (ViewStubProxy установит его для вас внутри), установите ViewStubProxy.setOnInflateListener, чтобы установить свои переменные привязки и LifecycleOwner.

Образец кода:

   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);       


      }));

   }

Настройте слушателя, чтобы получить доступ к надутому макету после успешного надувания 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 
  }
});

Тогда надуйте свой viewStub

mBinding.viewStub.getViewStub().inflate();

Если вы работаете с ViewModels, вам также необходимо установить владельца жизненного цикла для расширенного представления. Не забудьте установить того же владельца, который вы установили для переменной mBinding во время просмотра фрагмента / активности.

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();
    }
}

надеюсь, что это поможет кому-то.

Я также столкнулся с этим типом проблем, наконец, я получил решение и его прекрасно работает.

ViewStub Holding Макет,,

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

макет внутри ViewStub является,,,

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

Код внутри класса есть,,,

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();

Можно получить доступ к привязке в любое время, используя

layout_binding.viewStub.binding

а затем приведение в качестве сгенерированного класса Binding для вашего макета

(layout_bindinng.viewStub.binding as YourLayoutBinding).viewModel = viewModel

Будьте осторожны с нулевой проверкой, так как она будет нулевой перед раздутием.

Я предпочитаю раздувать макеты ViewStub следующим образом:

      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

Таким образом, я могу сразу использовать представления из привязки ViewStub.

Другие вопросы по тегам