android Jetpack Compose将参数传递到viewModel

xvw2m8pv  于 2023-10-14  发布在  Android
关注(0)|答案(7)|浏览(365)

如何在Jetpack Compose中将参数传递给viewModel
这是我的组合

@Composable
    fun UsersList() {
      val myViewModel: MyViewModel = viewModel("db2name") // pass param like this
    }

这是viewModel

class MyViewModel(private val dbname) : ViewModel() {
        private val users: MutableLiveData<List<User>> by lazy {
            MutableLiveData<List<User>>().also {
                loadUsers()
            }
        }
    
        fun getUsers(): LiveData<List<User>> {
            return users
        }
    
        private fun loadUsers() {
            // Do an asynchronous operation to fetch users.
        }
    }
xeufq47z

xeufq47z1#

你需要创建一个工厂来传递动态参数给ViewModel,像这样:

class MyViewModelFactory(private val dbname: String) :
    ViewModelProvider.NewInstanceFactory() {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T = MyViewModel(dbname) as T
}

然后在可组合函数中像这样使用工厂:

@Composable
fun UsersList() {
    val myViewModel: MyViewModel =
        viewModel(factory = MyViewModelFactory("db2name")) // pass param like this
}

现在您可以访问ViewModel中的dbname参数:

class MyViewModel(private val dbname:String) : ViewModel() {
    // ...rest of the viewModel logics here
}
fcg9iug3

fcg9iug32#

如果你使用导航组件,你可以在SavedStateHandle for view模型中免费获得它。
将参数传递给调用视图模型的组合对象,并从已保存的状态句柄中检索与视图模型同名的参数。
就像这样:
在NavHost上:

NavHost(
(...)
    composable(
            route = [route string like this $[route]/{$[argument name]}],
            arguments = listOf(
                navArgument([argument name]) { type = NavType.[type: Int/String/Boolean/etc.] }
            )
        ) {
            // access your viewModel in your composable
            val viewModel: MyViewModel = viewModel()
        }
    )
)

在视图模型上:

class ViewModel(savedStateHandle: SavedStateHandle) {

    private val argument = checkNotNull(savedStateHandle.get<[type]>([argument name]))
}

您的参数将神奇地出现,而无需视图模型工厂。

cld4siwp

cld4siwp3#

其他的解决方案也可以,但是你必须为每个ViewModel创建一个工厂,这似乎有点过分了。
更普遍的解决方案是这样的:

inline fun <VM : ViewModel> viewModelFactory(crossinline f: () -> VM) =
    object : ViewModelProvider.Factory {
        override fun <T : ViewModel> create(aClass: Class<T>):T = f() as T
    }

然后像这样使用它:

@Composable
fun MainScreen() {
    val viewModel: MyViewModel = viewModel(factory = viewModelFactory {
        MyViewModel("Test Name")
    })
}

对于这样的ViewModel:

class MyViewModel(
  val name: String
):ViewModel() {}
s2j5cfk0

s2j5cfk04#

通常情况下,你不需要这样做。在Android中,MVVM视图模型通过依赖注入从存储库获取数据。
以下是推荐的Android架构的官方文档:https://developer.android.com/jetpack/guide#recommended-app-arch

y1aodyip

y1aodyip5#

下面是一些Jetpack Compose/Kotlin特定的语法,用于实现相同的功能:

  • ui/settings/SettingsViewModel.kt*
class SettingsViewModel(
    private val settingsRepository: SettingsRepository
) : ViewModel() {
    /* Your implementation */
}

class SettingsViewModelFactory(
    private val settingsRepository: SettingsRepository
) : ViewModelProvider.Factory {

    override fun <T : ViewModel> create( modelClass: Class<T> ): T {
        if( modelClass.isAssignableFrom( SettingsViewModel::class.java ) ) {
            @Suppress( "UNCHECKED_CAST" )
            return SettingsViewModel( settingsRepository ) as T
        }
        throw IllegalArgumentException( "Unknown ViewModel Class" )
    }    

}

然后又道:

  • MainActivity.kt*
/* dataStore by preferencesDataStore */

class MainActivity : ComponentActivity() {
    private lateinit var settingsRepository: SettingsRepository
    
    // Here we instantiate our ViewModel leveraging delegates and
    // a trailing lambda
    private val settingsViewModel by viewModels<SettingsViewModel> {
        SettingsViewModelFactory(
            settingsRepository
        )
    }

    /* onCreate -> setContent -> etc */
}
kninwzqo

kninwzqo6#

没有必要再自己创建viewModelFactory
现在它包含在androidx.lifecycle.viewmodel

/**
 * Creates an [InitializerViewModelFactory] with the initializers provided in the builder.
 */
public inline fun viewModelFactory(
    builder: InitializerViewModelFactoryBuilder.() -> Unit
): ViewModelProvider.Factory = InitializerViewModelFactoryBuilder().apply(builder).build()

并且可以用作

val viewModel: MyViewModel = viewModel(factory = viewModelFactory {
            MyViewModel(AuthRepoImpl(apiService), HomeRepoImpl(apiService))
        })
11dmarpk

11dmarpk7#

正如@Secret Keeper提到的,你需要创建工厂。
如果您的ViewModel有依赖项,则viewModel()接受可选的ViewModelProvider.Factory作为参数。

class MyViewModelFactory(
    private val dbname: String
) : ViewModelProvider.Factory {
    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(MyViewModel::class.java)) {
            return MyViewModel(dbname) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

要创建viewModel,您需要传递可选参数。在你的Composable中,你可以做这样的事情。

val viewModel: MyViewModel = viewModel(
factory = MyViewModelFactory(
    dbname = "myDbName"
)

相关问题