MVVM模式下如何动态Inflate View?DataBinding新手求助
嘿,刚上手DataBinding和MVVM的话,这种动态增删视图的需求确实得换个思路——不能像传统写法那样直接在Activity里硬怼View操作了。我来给你拆解下MVVM模式下的标准实现方式,保证符合数据驱动UI的核心思想~
核心思路是用数据驱动视图:把动态视图的状态、增删逻辑全部放到ViewModel里,通过Observable集合管理数据,再让DataBinding自动完成视图的渲染和更新,完全避免在Activity/Fragment里直接操作View。
1. 定义单个动态视图的数据模型
每个要添加的双TextView视图,对应一个数据类来存储它的展示内容:
data class DynamicItem( val firstText: String, val secondText: String )
2. 编写ViewModel管理数据与操作
ViewModel里维护一个Observable集合(DataBinding能监听它的变化),同时提供添加、删除的方法:
class MainViewModel : ViewModel() { // ObservableArrayList会自动通知DataBinding更新UI val itemList = ObservableArrayList<DynamicItem>() // 添加按钮的点击逻辑:往集合里加新数据 fun onAddItemClick() { // 这里可以根据业务生成真实数据,我这里用序号模拟 val newItem = DynamicItem( "第一栏文本${itemList.size + 1}", "第二栏文本${itemList.size + 1}" ) itemList.add(newItem) } // 删除指定项的逻辑:从集合里移除对应数据 fun onDeleteItemClick(item: DynamicItem) { itemList.remove(item) } }
3. 编写动态视图的子布局
创建item_dynamic.xml,这是你要动态加载的双TextView+删除按钮布局,同时绑定数据和ViewModel的删除方法:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <!-- 当前项的数据 --> <variable name="item" type="com.yourpackage.DynamicItem" /> <!-- 全局ViewModel --> <variable name="viewModel" type="com.yourpackage.MainViewModel" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="16dp" android:gravity="center_vertical"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@{item.firstText}" /> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@{item.secondText}" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="删除" android:onClick="@{() -> viewModel.onDeleteItemClick(item)}" /> </LinearLayout> </layout>
4. 编写主布局与RecyclerView绑定
MVVM里动态列表首选RecyclerView(性能和可维护性都更好),主布局activity_main.xml里添加添加按钮和RecyclerView,绑定ViewModel:
<?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> <variable name="viewModel" type="com.yourpackage.MainViewModel" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="添加视图" android:onClick="@{() -> viewModel.onAddItemClick()}" /> <androidx.recyclerview.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" app:itemList="@{viewModel.itemList}" app:viewModel="@{viewModel}" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" /> </LinearLayout> </layout>
5. 编写DataBinding版RecyclerView Adapter
这个适配器负责把数据和子布局绑定:
class DynamicItemAdapter : RecyclerView.Adapter<DynamicItemAdapter.ViewHolder>() { private var items: List<DynamicItem> = emptyList() lateinit var viewModel: MainViewModel override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val inflater = LayoutInflater.from(parent.context) val binding = ItemDynamicBinding.inflate(inflater, parent, false) return ViewHolder(binding) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = items[position] holder.binding.item = item holder.binding.viewModel = viewModel holder.binding.executePendingBindings() // 立即完成绑定,避免延迟 } override fun getItemCount(): Int = items.size // 更新数据的方法,ViewModel的集合变化时调用 fun submitList(newItems: List<DynamicItem>) { items = newItems notifyDataSetChanged() } class ViewHolder(val binding: ItemDynamicBinding) : RecyclerView.ViewHolder(binding.root) }
6. 添加BindingAdapter连接RecyclerView与数据
写一个BindingAdapter,让RecyclerView能自动接收ViewModel的集合数据:
object RecyclerViewBindingAdapters { @BindingAdapter("itemList", "viewModel") @JvmStatic fun setupRecyclerView( recyclerView: RecyclerView, itemList: List<DynamicItem>?, viewModel: MainViewModel? ) { // 初始化适配器(如果还没有的话) val adapter = recyclerView.adapter as? DynamicItemAdapter ?: run { DynamicItemAdapter().also { recyclerView.adapter = it } } // 绑定ViewModel和数据 viewModel?.let { adapter.viewModel = it } itemList?.let { adapter.submitList(it) } } }
7. 在Activity中完成最终绑定
最后在Activity里初始化DataBinding和ViewModel,把它们关联起来:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) val viewModel = ViewModelProvider(this)[MainViewModel::class.java] binding.viewModel = viewModel binding.lifecycleOwner = this // 让DataBinding感知生命周期,自动更新UI } }
最后要注意的点
别忘了在app模块的build.gradle里开启DataBinding:
android { ... buildFeatures { dataBinding true } }
这样一来,点击添加按钮时,ViewModel会往集合里加数据,DataBinding自动通知RecyclerView新增视图;点击删除按钮时,ViewModel移除对应数据,视图也会自动消失——完全符合MVVM数据驱动UI的理念,没有任何直接操作View的代码~
内容的提问来源于stack exchange,提问作者Codershree

