Как использовать привязку данных с фрагментом

Я пытаюсь следовать примеру привязки данных из официального документа google https://developer.android.com/tools/data-binding/guide.html

за исключением того, что я пытаюсь применить данные, ожидающие фрагмента, а не активность.

ошибка, которую я получаю при компиляции,

Error:(37, 27) No resource type specified (at 'text' with value '@{marsdata.martianSols}.

onCreate для фрагмента выглядит следующим образом:

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    MartianDataBinding binding = MartianDataBinding.inflate(getActivity().getLayoutInflater());
    binding.setMarsdata(this);
}

onCreateView для фрагмента выглядит следующим образом:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    return inflater.inflate(R.layout.martian_data, container, false);
}

и части моего файла макета для фрагмента выглядят следующим образом:

<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="marsdata"
            type="uk.co.darkruby.app.myapp.MarsDataProvider" />
    </data>
...

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

    </RelativeLayout>
</layout>

Мое подозрение в том, что MartianDataBinding не знает, какой файл макета он должен связывать, - следовательно, ошибка. Любые предложения?

Ответ 1

Реализация привязки данных должна быть в методе onCreateView фрагмента, удалить любые привязки данных, которые существуют в вашем методе OnCreate, ваш onCreateView должен выглядеть следующим образом:

public View onCreateView(LayoutInflater inflater, 
                         @Nullable ViewGroup container, 
                         @Nullable Bundle savedInstanceState) {
    MartianDataBinding binding = DataBindingUtil.inflate(
            inflater, R.layout.martian_data, container, false);
    View view = binding.getRoot();
    //here data must be an instance of the class MarsDataProvider
    binding.setMarsdata(data);
    return view;
}

Ответ 2

На самом деле вам рекомендуется использовать метод inflate вашего сгенерированного связывания, а не DataBindingUtil:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    MainFragmentBinding binding = MainFragmentBinding.inflate(inflater, container, false);
    //set variables in Binding
    return binding.getRoot();
}

Документы для DataBindingUtil.inflate():

Используйте эту версию, только если layoutId неизвестен заранее. В противном случае используйте сгенерированный метод накачивания Binding для обеспечения безопасной инфляции типа.

Ответ 3

Можно просто получить объект просмотра, как указано ниже

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

View view = DataBindingUtil.inflate(inflater, R.layout.layout_file, container, false).getRoot();

return view;

}

Ответ 4

Попробуйте это в Android DataBinding

FragmentMainBinding binding;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_main, container, false);
        View rootView = binding.getRoot();
        initInstances(savedInstanceState);
        return rootView;
}

Ответ 5

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

Используйте Binding class inflate как рекомендовано в документации Android.

Один из вариантов - использовать DataBindingUtil но только если вы не знаете, что сгенерирован класс привязки.

--You имеет автоматически сгенерированный binding class, используйте этот класс вместо DataBindingUtil.

На яве

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    HomeFragmentBinding binding = HomeFragmentBinding.inflate(inflater, container, false);
    //set binding variables here
    return binding.getRoot();
}

В котлине

lateinit var binding: HomeFragmentBinding 
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    binding = HomeFragmentBinding.inflate(inflater, container, false)
    return binding.root
}

В документации класса DataBindingUtil вы можете увидеть.

раздувать

T inflate (LayoutInflater inflater, 
                int layoutId, 
                ViewGroup parent, 
                boolean attachToParent)

Используйте эту версию, только если layoutId заранее неизвестен. В противном случае используйте сгенерированный метод инфляции Binding, чтобы обеспечить безопасную инфляцию.

Если ваш класс связывания макета не сгенерирован @См. Этот ответ.

Ответ 6

работает в моем коде.

private FragmentSampleBinding dataBiding;
private SampleListAdapter mAdapter;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    dataBiding = DataBindingUtil.inflate(inflater, R.layout.fragment_sample, null, false);
    return mView = dataBiding.getRoot();
}

Ответ 7

Синтаксис Kotlin:

lateinit var binding: MartianDataBinding
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    binding = DataBindingUtil.inflate(inflater, R.layout.martian_data, container, false)
    return binding.root
}

Ответ 8

Полный пример фрагментов привязки данных

FragmentMyProgramsBinding - это класс привязки, созданный для res/layout/frag_my_programs

public class MyPrograms extends Fragment {
    FragmentMyProgramsBinding fragmentMyProgramsBinding;

    public MyPrograms() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
    FragmentMyProgramsBinding    fragmentMyProgramsBinding = DataBindingUtil.inflate(inflater, R
                .layout.fragment_my_programs, container, false);
        return fragmentMyProgramsBinding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

    }
}

Ответ 9

Если вы используете ViewModel и LiveData Это достаточный синтаксис

Синтаксис Kotlin:

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    return MartianDataBinding.inflate(
        inflater,
        container,
        false
    ).apply {
        setLifecycleOwner([email protected])
        vm = viewModel    // Attach your view model here
    }.root
}

Ответ 10

Еще один пример в Котлине:

override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    val binding = DataBindingUtil
            .inflate< MartianDataBinding >(
                    inflater,
                    R.layout.bla,
                    container,
                    false
            )

    binding.modelName = // ..

    return binding.root
}

Обратите внимание, что имя "MartianDataBinding" зависит от имени файла макета. Если файл называется "martian_data", тогда правильное имя будет MartianDataBinding.

Ответ 11

Все говорят о inflate(), но что если мы захотим использовать его в onViewCreated()?

Вы можете использовать метод bind(view) конкретного класса привязки, чтобы получить экземпляр ViewDataBinding для view.


Обычно мы пишем BaseFragment примерно так (упрощенно):

// BaseFragment.kt
abstract fun layoutId(): Int

override fun onCreateView(inflater, container, savedInstanceState) = 
    inflater.inflate(layoutId(), container, false)

И использовать его в дочернем фрагменте.

// ConcreteFragment.kt
override fun layoutId() = R.layout.fragment_concrete

override fun onViewCreated(view, savedInstanceState) {
    val binding = FragmentConcreteBinding.bind(view)
    // or
    val binding = DataBindingUtil.bind<FragmentConcreteBinding>(view)
}


Если все фрагменты используют привязку данных, вы даже можете упростить их, используя параметр типа.

abstract class BaseFragment<B: ViewDataBinding> : Fragment() {
    abstract fun onViewCreated(binding: B, savedInstanceState: Bundle?)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        onViewCreated(DataBindingUtil.bind<B>(view)!!, savedInstanceState)
    }
}

Я не знаю, хорошо ли утверждать ненулевое значение там, но... вы поняли идею. Если вы хотите, чтобы это было обнуляемым, вы можете сделать это.

Ответ 12

Точно так же, как большинство сказали, но не забудьте установить LifeCycleOwner
Образец в Java, т.е.

public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    BindingClass binding = DataBindingUtil.inflate(inflater, R.layout.fragment_layout, container, false);
    ModelClass model = ViewModelProviders.of(getActivity()).get(ViewModelClass.class);
    binding.setLifecycleOwner(getActivity());
    binding.setViewmodelclass(model);

    //Your codes here

    return binding.getRoot();
}