Как использовать привязку данных с фрагментом
Я пытаюсь следовать примеру привязки данных из официального документа 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
не знает, с каким файлом макета он должен быть связан - отсюда и ошибка. Какие-либо предложения?
17 ответов
Реализация привязки данных должна быть в методе 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;
}
Вы на самом деле рекомендуется использовать inflate
метод вашего сгенерированного Binding, а не 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, чтобы обеспечить безопасную инфляцию.
Даже другие ответы могут работать хорошо, но я хочу сказать лучший подход.
использование Binding class's inflate
как рекомендовано в документации Android.
Один из вариантов - надуть DataBindingUtil
но когда только вы не знаете, сгенерировали класс привязки.
- Вы сгенерировали 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, чтобы обеспечить безопасную инфляцию.
Если ваш класс связывания макета не сгенерирован @ См. Этот ответ.
Если вы используете ViewModel и LiveData Это достаточный синтаксис
Синтаксис Котлина:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return MartianDataBinding.inflate(
inflater,
container,
false
).apply {
setLifecycleOwner(this@MartianData)
vm = viewModel // Attach your view model here
}.root
}
Как и большинство из них, но не забудьте установить LifeCycleOwner
Sample в 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();
}
Синтаксис Котлина:
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
}
Попробуйте это в 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;
}
Можно просто получить вид объекта, как указано ниже
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;
}
Полный пример в Фрагментах привязки данных
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);
}
}
Работает в моем коде.
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();
}
Вот как это можно сделать в котлине:
//Pass the layout as parameter to the fragment constructor
class SecondFragment : Fragment(R.layout.fragment_second) {
private var _binding: FragmentSecondBinding? = null
private val binding get() = _binding!!
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentSecondBinding.bind(view) //if the view is already inflated then we can just bind it to view binding.
}
//Note: Fragments outlive their views. Make sure you clean up any references to the binding class
// instance in the fragment's onDestroyView() method.
override fun onDestroyView() {
Toast.makeText(activity, "On destroy", Toast.LENGTH_SHORT).show()
super.onDestroyView()
_binding = null
}
}
Вы можете получить доступ к элементам представления из ваших макетов, например:
binding.tvName.text = "Messi"
где tvName - это идентификатор элемента представления.
Котлин
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View?
{
val binding = FragmentFirstBinding.inflate(inflater,container,false)
return binding.root;
}
где FragmentFirstBinding автоматически генерируется студией Android с использованием привязки представления. В моем фрагменте кода имя FirstFragment .
Во-первых, вам нужно добавить эту строку в файл build.Gradle(модуль приложения).
buildFeatures{
viewBinding true
}
Я нашел ответ для своего приложения, и вот ответ для языка Kotlin.
приватная привязка переменных lateinit: FragmentForgetPasswordBinding переопределить удовольствие onCreateView ( надувной: LayoutInflater, контейнер: ViewGroup?, saveInstanceState: комплект?): Посмотреть? { binding=DataBindingUtil.inflate(надувной,R.layout.fragment_forget_password, контейнер, ложь) val viewModel=ViewModelProvider(это).get(ForgetPasswordViewModel::class.java) binding.recoveryViewModel= модель просмотра viewModel.forgetPasswordInterface= это вернуть binding.root }
Очень полезный блог о привязке данных: https://link.medium.com/HQY2VizKO1
class FragmentBinding<out T : ViewDataBinding>(
@LayoutRes private val resId: Int
) : ReadOnlyProperty<Fragment, T> {
private var binding: T? = null
override operator fun getValue(
thisRef: Fragment,
property: KProperty<*>
): T = binding ?: createBinding(thisRef).also { binding = it }
private fun createBinding(
activity: Fragment
): T = DataBindingUtil.inflate(LayoutInflater.from(activity.context),resId,null,true)
}
Объявите привязку val следующим образом во фрагменте:
private val binding by FragmentBinding<FragmentLoginBinding>(R.layout.fragment_login)
Не забудьте написать это фрагментом
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return binding.root
}
Еще один пример в Котлине:
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.
Все говорят о 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)
}
}
Я не знаю, что там можно утверждать ненулевое значение, но... вы поняли идею. Если вы хотите, чтобы он допускал значение NULL, вы можете это сделать.
Кратчайший путь в Котлине;
class HomeFragment : Fragment(R.layout.fragment_home) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentHomeBinding.bind(view)
// todo
}