gpt4 book ai didi

android - android绑定(bind)数据的通用形式

转载 作者:行者123 更新时间:2023-12-02 12:05:55 25 4
gpt4 key购买 nike

我最近开始学习 Kotlin 中的 View 模型和数据绑定(bind)。我创建了一个示例项目,在其中我在一个 Activity 中创建了几个 fragment 。我很想知道如何使用 View 模型实现数据绑定(bind)的通用 fragment 。我不确定这是否可能,或者我是否走在正确的道路上。我在网上搜索并找到了一些线索,但没有完整的解决方案。

link1

link2

到目前为止,我已经创建了一个抽象的 BaseFragment。

abstract class BaseFragment<V : BaseViewModel> : Fragment()
{

lateinit var binding: FragmentHomeBinding

lateinit var viewModel : V


override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

binding = DataBindingUtil.inflate(inflater, getContentView(), container, false)
val view = binding.root

//here data must be an instance of the class MarsDataProvider
viewModel = ViewModelProviders.of(this).get(viewModel.javaClass)

setupUI()

binding.viewModel = viewModel

return view

}

abstract fun setupUI()

abstract fun getContentView() : Int
}

这是 HomeFragment 的代码
class HomeFragment : BaseFragment<HomeViewModel>() {
override fun setupUI() {
viewModel.errorMessage.observe(this, Observer {
errorMessage -> if(errorMessage != null) showError(errorMessage) else hideError()
})
}

override fun getContentView(): Int {
return R.layout.fragment_home
}


private var errorSnackbar: Snackbar? = null


private fun showError(@StringRes errorMessage:Int){
Log.d("anton","showError")
errorSnackbar = Snackbar.make(binding.root, errorMessage, Snackbar.LENGTH_INDEFINITE)
errorSnackbar?.setAction(R.string.retry, viewModel.errorClickListener)
errorSnackbar?.show()
}

private fun hideError(){
Log.d("anton","hideError")
errorSnackbar?.dismiss()
}

}

这是我拥有的 fragment 的 xml 布局之一
    <?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="app.series.com.series4go.viewmodels.HomeViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:mutableVisibility="@{viewModel.getLoadingVisibility()}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/post_list"
android:layout_width="0dp"
android:layout_height="0dp"
app:adapter="@{viewModel.getPostListAdapter()}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

我不确定如何更改
lateinit var binding: FragmentHomeBinding

在 BaseFragment 中它是通用的,因为我需要用
binding = DataBindingUtil.inflate(inflater, getContentView(), container, false)  

编辑

在玩弄了这段代码之后,我想到了这个:

基本 fragment :
    abstract class BaseFragment<V : BaseViewModel, T : ViewDataBinding> : Fragment()
{
lateinit var binding: T
lateinit var viewModel : V

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, getContentView(), container, false)
val view = binding.root
viewModel = ViewModelProviders.of(this).get(getViewModelClass())
setupUI()
bindViewToModel()
return view
}

abstract fun setupUI()

abstract fun getContentView() : Int

abstract fun getViewModelClass() : Class<V>

abstract fun bindViewToModel()

}

首页 fragment

类 HomeFragment : BaseFragment() {
override fun bindViewToModel() {
binding.viewModel = viewModel
}


override fun setupUI(){

binding.postList.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)

//here data must be an instance of the class MarsDataProvider

viewModel.errorMessage.observe(this, Observer { errorMessage ->
if (errorMessage != null) showError(errorMessage) else hideError()
})

}

override fun getContentView(): Int {
return R.layout.fragment_home
}


private var errorSnackbar: Snackbar? = null


private fun showError(@StringRes errorMessage:Int){
Log.d("anton","showError")
errorSnackbar = Snackbar.make(binding.root, errorMessage, Snackbar.LENGTH_INDEFINITE)
errorSnackbar?.setAction(R.string.retry, viewModel.errorClickListener)
errorSnackbar?.show()
}

private fun hideError(){
Log.d("anton","hideError")
errorSnackbar?.dismiss()
}

override fun getViewModelClass(): Class<HomeViewModel> {
return HomeViewModel::class.java
}

}

在这个解决方案中我唯一不喜欢的是函数 bindViewToModel,每个扩展基本 fragment 的 fragment 都需要在所有 fragment 中以相同的方式实现它。不确定如何将其移动到基本 fragment ,因为基本 fragment 不知道布局的任何变量(因为它是抽象的)。

我很高兴知道是否有地方可以改进此设计或解决此问题。

谢谢

编辑 2

遵循@Oya Canlı 的解决方案,我设法删除了抽象的 bindViewToModel 这是最终代码,以防有人有兴趣使用它。

基本 fragment :
abstract class BaseFragment<V : BaseViewModel, T : ViewDataBinding> : Fragment()
{
lateinit var binding: T
lateinit var viewModel : V

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, getContentView(), container, false)
val view = binding.root
viewModel = ViewModelProviders.of(this).get(getViewModelClass())
setupUI()

binding.setVariable(BR.viewModel, viewModel)

return view
}

abstract fun setupUI()

abstract fun getContentView() : Int

abstract fun getViewModelClass() : Class<V>

}

首页 fragment
class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>() {

override fun setupUI(){

binding.postList.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)

//here data must be an instance of the class MarsDataProvider

viewModel.errorMessage.observe(this, Observer { errorMessage ->
if (errorMessage != null) showError(errorMessage) else hideError()
})

}

override fun getContentView(): Int {
return R.layout.fragment_home
}


private var errorSnackbar: Snackbar? = null


private fun showError(@StringRes errorMessage:Int){
Log.d("anton","showError")
errorSnackbar = Snackbar.make(binding.root, errorMessage, Snackbar.LENGTH_INDEFINITE)
errorSnackbar?.setAction(R.string.retry, viewModel.errorClickListener)
errorSnackbar?.show()
}

private fun hideError(){
Log.d("anton","hideError")
errorSnackbar?.dismiss()
}

override fun getViewModelClass(): Class<HomeViewModel> {
return HomeViewModel::class.java
}

}

最佳答案

数据绑定(bind)类的通用类型是 查看数据绑定(bind) .所以你可以得到你的绑定(bind)实例:

val binding = DataBindingUtil.inflate<ViewDataBinding>(
inflater, getContentView(), container, false)

但是你不能像 binding.viewModel 那样设置 viewModel,因为泛型绑定(bind)类的实例不会有一个名为 setViewModel 的 setter 。您可以使用通用的 setter setVariable:
binding.setVariable(BR.viewModel, viewModel)

BR 是一个生成的类,其中包含您用于数据绑定(bind)的所有变量。上面的这个方法不是类型安全的,它不会检查提到的 BR 变量是否确实位于特定的绑定(bind)类中。这就是为什么通常最好使用特定的 setter 。但是,当您不知道特定的绑定(bind)类时,就像您的情况一样,这就是要走的路。

您还可以使用类似的方法来编写可重用的 recyclerview 适配器。

关于android - android绑定(bind)数据的通用形式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57763355/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com