如何使用 Fragment 进行数据绑定
Posted
技术标签:
【中文标题】如何使用 Fragment 进行数据绑定【英文标题】:How to use data-binding with Fragment 【发布时间】:2016-04-14 20:50:43 【问题描述】:我正在尝试遵循官方谷歌文档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_
android:layout_
android:text="@marsdata.martianSols"
/>
</RelativeLayout>
</layout>
我的怀疑是MartianDataBinding
不知道它应该绑定哪个布局文件 - 因此出现错误。有什么建议吗?
【问题讨论】:
【参考方案1】:数据绑定实现必须在fragment的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;
【讨论】:
我为这个问题苦苦挣扎了好几个小时。问题是我返回了错误的视图。 +1View view = binding.getRoot();
我已经被困在这个问题上很长时间了,以至于我在 developer.android.com 上找不到任何关于它的文档,我真的很沮丧......解决了这个问题.谢谢!
如果您使用 LiveData 和 ViewModel,请务必阅读 this answer。
什么是 setMarsdata() ?我想我们在这里使用 setViewModel()??
binding.setLifecycleOwner(this)
也需要被调用才能更新 UI。否则,UI 更改将不可见。至少在我使用 LiveData 时【参考方案2】:
实际上鼓励您使用生成的 Binding 的 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():
仅当 layoutId 事先未知时才使用此版本。否则,使用生成的 Binding 的 inflate 方法来确保类型安全的膨胀。
【讨论】:
不幸的是,这让我在构建时出现cannot be resolved to a type
错误。在我看来这并不可靠。如果我先使用DataBindingUtil.inflate(inflater, R.layout.fragment_camera, container, false);
,然后将其更改为FragmentCameraBinding.inflate(inflater, container, false);
,它可以工作,但重建后它再次出现错误。
效果很好。实际上不需要指定布局 res id(我之前想知道),因为它会自动从生成的绑定文件中选择。
在这个例子中你在哪里设置片段布局ID(例如R.layout.fragment_)?
这应该是公认的答案。鼓励使用布局生成的绑定,而不是DataBindingUtil.inflate
@LeninRajRajasekaran 布局 id 是通过使用 MainFragmentBinding
类来暗示的。该类是从布局文件生成的,因此会自动应用所需的布局。【参考方案3】:
即使其他答案也可能效果很好,但我想告诉最好的方法。
按照Android Documentation中的建议使用Binding class's inflate
。
一种选择是通过DataBindingUtil
膨胀但只有当您不知道已生成绑定类时。
--您已经自动生成了binding 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
在DataBindingUtil类documentation中可以看到。
膨胀
T inflate (LayoutInflater inflater, int layoutId, ViewGroup parent, boolean attachToParent)
仅当 layoutId 事先未知时才使用此版本。否则,使用生成的 Binding 的 inflate 方法来确保类型安全 通货膨胀。
如果你的布局绑定类没有生成@See this answer。
【讨论】:
为什么不使用以LayoutInflater
作为唯一参数的inflate
方法?
@FlorianWalther 在没有ViewGroup container
的情况下可以工作吗?
好吧,我不知道我什么时候写的这个评论。但我在这里得到了一些好的答案:***.com/questions/61571381/…
@FlorianWalther 好的,我已经阅读了答案,当attachToRoot
是true
时需要container
。【参考方案4】:
如果您使用的是 ViewModel 和 LiveData,这样的语法就足够了
Kotlin 语法:
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
【讨论】:
【参考方案5】:正如大多数人所说,但不要忘记设置 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();
【讨论】:
【参考方案6】:在 Android 数据绑定中试试这个
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;
【讨论】:
【参考方案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】:如下所述,可以简单地检索视图对象
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;
【讨论】:
【参考方案9】:数据绑定Fragments的完整示例
FragmentMyProgramsBinding 是为 res/layout/fragment_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);
【讨论】:
【参考方案10】:在我的代码中工作。
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();
【讨论】:
【参考方案11】:我一直在为我的申请寻找答案,这里是 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
【讨论】:
【参考方案12】: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。
【讨论】:
【参考方案13】:关于数据绑定的非常有用的博客: 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
【讨论】:
【参考方案14】:在 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。
【讨论】:
【参考方案15】:每个人都说inflate()
,但如果我们想在onViewCreated()
中使用它呢?
您可以使用具体绑定类的bind(view)
方法获取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)
我不知道在那里断言非空是可以的,但是..你明白了。如果你希望它可以为空,你可以这样做。
【讨论】:
使用这种方式时如何绑定viewModel?以上是关于如何使用 Fragment 进行数据绑定的主要内容,如果未能解决你的问题,请参考以下文章
为啥不在 Android Fragment 数据绑定中使用 lateinit 修饰符?
如何用 Android 数据绑定替换 androidx.fragment.app.FragmentContainerView 中的片段
单 Activity 多 Fragment 架构如何使用路由进行统一跳转