双向数据绑定、RecyclerView、ViewModel、Room、LiveData、Oh My

Bin*_*ink 8 android-recyclerview android-databinding android-room android-livedata android-viewmodel

Android 开发新手,我\xe2\x80\x99m 试图将我的注意力集中在与 RecyclerView、ViewModel、Room 和 LiveData 结合的双向数据绑定上。我理解单向绑定,但不能\xe2\x80\x99t 找出双向。

\n\n

简而言之,我\xe2\x80\x99d 希望能够点击 id/switch_enabled 开关并更新 Db 以反映这一点(然后我计划利用它来更新类/Db 中的其他成员)。我认为我需要一些帮助来设置 ViewModel 上的 set(value) 并在 Db 中更新正确的 RecyclerView 项目,但我\xe2\x80\x99m 不确定如何执行此操作,或者这是否是正确或最佳的方法。

\n\n

谢谢。

\n\n

班级:

\n\n
data class Person (@ColumnInfo(name = "first_name") val firstName: String,\n                   @ColumnInfo(name = "last_name") val lastName: String,\n\n                   //...\n\n                   val enabled: Boolean = true\n){\n    @PrimaryKey(autoGenerate = true)\n    var id: Long = 0\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

RecyclerView 的布局详细信息:

\n\n
<layout xmlns:android="http://schemas.android.com/apk/res/android"\n    xmlns:app="http://schemas.android.com/apk/res-auto"\n    xmlns:tools="http://schemas.android.com/tools">\n\n    <data>\n        <variable\n            name="p" type="com.example.data.Person" />\n    </data>\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:id="@+id/constraintLayout"\n        android:layout_width="match_parent"\n        android:layout_height="wrap_content">\n\n        <TextView\n            android:id="@+id/first_name"\n            android:layout_width="wrap_content"\n            android:layout_height="wrap_content"\n            android:text="@{p.firstName}"\n            app:layout_constraintStart_toStartOf="parent"\n            app:layout_constraintTop_toTopOf="parent"\n            tools:text="John" />\n\n        <TextView\n            android:id="@+id/last_name"\n            android:layout_width="wrap_content"\n            android:layout_height="wrap_content"\n            android:ellipsize="end"\n            android:text="@{\' \' + p.lastName}"\n            app:layout_constraintStart_toEndOf="@id/first_name"\n            app:layout_constraintTop_toTopOf="parent"\n            tools:text=" Doubtfire" />\n\n        <Switch\n            android:id="@+id/switch_enabled"\n            android:layout_width="wrap_content"\n            android:layout_height="wrap_content"\n            android:checked="@={p.enabled}"\n            app:layout_constraintBaseline_toBaselineOf="@id/last_name"\n            app:layout_constraintEnd_toEndOf="parent" />\n\n        <!--...-->\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>\n
Run Code Online (Sandbox Code Playgroud)\n\n

视图模型:

\n\n
class MainViewModel(private val repository: DataRepository) : ViewModel() {\n    private val _people: LiveData<List<Person>>\n//    @Bindable?\n//    @get:Bindable?\n    var people: LiveData<List<Person>>\n        @Bindable\n        get() = _people\n        set(value) {\n            //Find out which member of the class is being changed and update the Db?\n            Log.d(TAG, "Value for set is $value!")\n        }\n    init {\n        _people = repository.livePeople()\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

分段:

\n\n
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,\n                          savedInstanceState: Bundle?): View? {\n    val binding = FragmentPeopleBinding.inflate(inflater, container, false)\n    val context = context ?: return binding.root\n\n    val factory = Utilities.provideMainViewModelFactory(context)\n    viewModel = ViewModelProviders.of(requireActivity(), factory).get(MainViewModel::class.java)\n\n    val adapter = PeopleViewAdapter()\n    viewModel.people.observe(this, Observer<List<Person>> {\n        adapter.submitList(it)\n    })\n\n    binding.apply {\n        vm = viewModel\n        setLifecycleOwner(this@PeopleFragment)\n        executePendingBindings()\n        rvPeopleDetails.adapter = adapter\n    }\n    return binding.root\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

列表适配器:

\n\n
class PeopleViewAdapter: ListAdapter<Person, PeopleViewAdapter.ViewHolder>(PeopleDiffCallback()) {\n    class PeopleDiffCallback : DiffUtil.ItemCallback<Person>() {\n        override fun areItemsTheSame(oldItem: Person, newItem: Person): Boolean {\n            return oldItem.id == newItem.id\n        }\n\n        override fun areContentsTheSame(oldItem: Person, newItem: Person): Boolean     {\n            return oldItem.number == newItem.number\n        }\n    }\n\n    class ViewHolder(val binding: FragmentPeopleDetailBinding) : RecyclerView.ViewHolder(binding.root) {\n\n        fun bind(person: Person) {\n            binding.p = person\n        }\n    }\n\n    @NonNull\n    override fun onCreateViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder =\n            ViewHolder(FragmentPeopleDetailBinding.inflate(LayoutInflater.from(parent.context), parent, false))\n\n    @NonNull\n    override fun onBindViewHolder(@NonNull holder: ViewHolder, position: Int) {\n        holder.apply {\n            bind(getItem(position))\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

lin*_*rox 3

我刚刚遇到了同样的问题,即使用 ViewModel 和 RecyclerView 列表在 MVVM 架构中设置双向数据绑定。我确定在这种情况下不可能或不值得努力让两种方式绑定工作,因为您没有直接使用 recyclerview 项布局中的视图模型(您使用的布局变量是 Person 类型,而不是你的视图模型)。

我建议实际上将视图模型添加为布局变量,然后android:onClick="@{() -> viewmodel.onSwitchClicked()}"在视图模型中使用并实现该方法。

在这里查看我的项目的详细信息: https: //github.com/linucksrox/ReminderList