如何使用 Fragment 进行数据绑定
How to use data-binding with Fragment
我正在尝试遵循官方 google 文档中的数据绑定示例 https://developer.android.com/tools/data-binding/guide.html
除了我正在尝试将数据绑定应用于片段,而不是 activity。
我目前在编译时遇到的错误是
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
不知道应该绑定哪个布局文件 - 因此出现错误。有什么建议吗?
数据绑定实现必须在片段的 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;
}
可以简单地检索视图对象,如下所述
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;
}
在 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;
}
使用我的代码。
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();
}
我们实际上鼓励您使用生成的绑定的 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();
}
Docs for DataBindingUtil.inflate():
Use this version only if layoutId is unknown in advance. Otherwise, use the generated Binding's inflate method to ensure type-safe inflation.
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
}
Kotlin 中的另一个例子:
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。
即使其他答案也可能有效,但我想告诉您最好的方法。
按照 Android Documentation.
中的建议使用 Binding class's inflate
一种选择是通过 DataBindingUtil
膨胀,但只有您不知道已生成绑定 class.
--您已自动生成 binding class
,请使用 class 而不是使用 DataBindingUtil
。
在Java
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
HomeFragmentBinding binding = HomeFragmentBinding.inflate(inflater, container, false);
//set binding variables here
return binding.getRoot();
}
在 Kotlin 中
lateinit var binding: HomeFragmentBinding
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = HomeFragmentBinding.inflate(inflater, container, false)
return binding.root
}
在DataBindingUtilclass 中可以看到。
inflate
T inflate (LayoutInflater inflater,
int layoutId,
ViewGroup parent,
boolean attachToParent)
Use this version only if layoutId is unknown in advance. Otherwise, use the generated Binding's inflate method to ensure type-safe
inflation.
如果你的布局biniding class没有生成@See .
数据绑定Fragments中的完整示例
FragmentMyProgramsBinding 正在为 res/layout/fragment_my_programs
生成绑定 class
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);
}
}
如果您正在使用 ViewModel 和 LiveData 这是足够的语法
科特林语法:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return MartianDataBinding.inflate(
inflater,
container,
false
).apply {
lifecycleOwner = viewLifecycleOwner
vm = viewModel // Attach your view model here
}.root
}
大家都说inflate()
,但是如果我们想在onViewCreated()
中使用呢?
您可以使用 bind(view)
具体绑定方法 class 来获取 view
的 ViewDataBinding
实例。
通常我们这样写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)
}
如果所有 Fragment 都使用数据绑定,您甚至可以使用类型参数使其更简单。
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)
}
}
我不知道是否可以在那里断言 non-null,但是..你明白了。如果你希望它可以为空,你可以做到。
正如大多数人所说,但不要忘记设置 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();
}
关于数据绑定的非常有用的博客:
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)
}
在 Fragment 中像这样声明绑定 val :
private val binding by FragmentBinding<FragmentLoginBinding>(R.layout.fragment_login)
不要忘记在片段中写这个
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return binding.root
}
我一直在为我的申请寻找答案,这里是 Kotlin 语言的答案。
private lateinit var binding: FragmentForgetPasswordBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
binding=DataBindingUtil.inflate(inflater,R.layout.fragment_forget_password,container,false)
val viewModel=ViewModelProvider(this).get(ForgetPasswordViewModel::class.java)
binding.recoveryViewModel=viewModel
viewModel.forgetPasswordInterface=this
return binding.root
}
这是在 kotlin 中的实现方式:
//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 是视图元素的 ID。
Kotlin
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 中的最短路线;
class HomeFragment : Fragment(R.layout.fragment_home) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentHomeBinding.bind(view)
// todo
}
我正在尝试遵循官方 google 文档中的数据绑定示例 https://developer.android.com/tools/data-binding/guide.html
除了我正在尝试将数据绑定应用于片段,而不是 activity。
我目前在编译时遇到的错误是
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
不知道应该绑定哪个布局文件 - 因此出现错误。有什么建议吗?
数据绑定实现必须在片段的 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;
}
可以简单地检索视图对象,如下所述
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;
}
在 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;
}
使用我的代码。
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();
}
我们实际上鼓励您使用生成的绑定的 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();
}
Docs for DataBindingUtil.inflate():
Use this version only if layoutId is unknown in advance. Otherwise, use the generated Binding's inflate method to ensure type-safe inflation.
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
}
Kotlin 中的另一个例子:
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。
即使其他答案也可能有效,但我想告诉您最好的方法。
按照 Android Documentation.
中的建议使用Binding class's inflate
一种选择是通过 DataBindingUtil
膨胀,但只有您不知道已生成绑定 class.
--您已自动生成 binding class
,请使用 class 而不是使用 DataBindingUtil
。
在Java
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
HomeFragmentBinding binding = HomeFragmentBinding.inflate(inflater, container, false);
//set binding variables here
return binding.getRoot();
}
在 Kotlin 中
lateinit var binding: HomeFragmentBinding
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = HomeFragmentBinding.inflate(inflater, container, false)
return binding.root
}
在DataBindingUtilclass
inflate
T inflate (LayoutInflater inflater, int layoutId, ViewGroup parent, boolean attachToParent)
Use this version only if layoutId is unknown in advance. Otherwise, use the generated Binding's inflate method to ensure type-safe inflation.
如果你的布局biniding class没有生成@See
数据绑定Fragments中的完整示例
FragmentMyProgramsBinding 正在为 res/layout/fragment_my_programs
生成绑定 classpublic 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);
}
}
如果您正在使用 ViewModel 和 LiveData 这是足够的语法
科特林语法:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return MartianDataBinding.inflate(
inflater,
container,
false
).apply {
lifecycleOwner = viewLifecycleOwner
vm = viewModel // Attach your view model here
}.root
}
大家都说inflate()
,但是如果我们想在onViewCreated()
中使用呢?
您可以使用 bind(view)
具体绑定方法 class 来获取 view
的 ViewDataBinding
实例。
通常我们这样写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)
}
如果所有 Fragment 都使用数据绑定,您甚至可以使用类型参数使其更简单。
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)
}
}
我不知道是否可以在那里断言 non-null,但是..你明白了。如果你希望它可以为空,你可以做到。
正如大多数人所说,但不要忘记设置 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();
}
关于数据绑定的非常有用的博客: 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)
}
在 Fragment 中像这样声明绑定 val :
private val binding by FragmentBinding<FragmentLoginBinding>(R.layout.fragment_login)
不要忘记在片段中写这个
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return binding.root
}
我一直在为我的申请寻找答案,这里是 Kotlin 语言的答案。
private lateinit var binding: FragmentForgetPasswordBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
binding=DataBindingUtil.inflate(inflater,R.layout.fragment_forget_password,container,false)
val viewModel=ViewModelProvider(this).get(ForgetPasswordViewModel::class.java)
binding.recoveryViewModel=viewModel
viewModel.forgetPasswordInterface=this
return binding.root
}
这是在 kotlin 中的实现方式:
//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 是视图元素的 ID。
Kotlin
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 中的最短路线;
class HomeFragment : Fragment(R.layout.fragment_home) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentHomeBinding.bind(view)
// todo
}