0

我正在努力在 Android Jetpack Compose 中实现 DataStore 首选项库,以便在我的应用程序中保留一些用户设置。每当我尝试从 Composable 组件访问 SettingsViewModel 时,应用程序就会崩溃并且我收到以下错误:

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.boldmethod, PID: 5415
java.lang.RuntimeException: Cannot create an instance of class com.boldmethod.Models.SettingsViewModel
    ...
 Caused by: java.lang.InstantiationException: java.lang.Class<com.packageName.Models.SettingsViewModel> has no zero argument constructor

我正在按照文档创建 DataStore 和视图模型,所以也许我在 Composable 中没有正确使用它们。这是相关的源代码:

摇篮

dependencies {

// Kotlin/Core
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.20"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'

// Compose
implementation "androidx.compose.ui:ui:1.0.0-alpha08"
implementation "androidx.compose.material:material:1.0.0-alpha08"
implementation "androidx.compose.runtime:runtime:1.0.0-alpha08"
implementation "androidx.compose.runtime:runtime-livedata:1.0.0-alpha08"
implementation "androidx.compose.runtime:runtime-rxjava2:1.0.0-alpha08"

// Navigation
def nav_compose_version = "1.0.0-alpha03"
implementation "androidx.navigation:navigation-compose:$nav_compose_version"

// Architecture
implementation "androidx.datastore:datastore-preferences:1.0.0-alpha05"
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
//implementation "android.arch.lifecycle:runtime:2.1.0"

// UI/Material
implementation 'com.google.android.material:material:1.2.1'
implementation "androidx.ui:ui-tooling:1.0.0-alpha07"

// Testing
testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

创建 DataStore 的用户首选项存储库

class UserPreferencesRepository private constructor(context: Context) {

data class UserPreferences(
    val resolution: String,
    val theme: String,
)

private val dataStore: DataStore<Preferences> =
    context.createDataStore(name = "userPreferences")

private object PreferencesKeys {
    val RESOLUTION = preferencesKey<String>("resolution")
    val THEME = preferencesKey<String>("theme")
}

// Read from the DataStore with a Flow object.
val userPreferencesFlow: Flow<UserPreferences> = dataStore.data
    .catch { exception ->
        if (exception is IOException) {
            emit(emptyPreferences())
        } else {
            throw exception
        }
    }.map { preferences ->
        // Get values or a default value.
        val resolution = preferences[PreferencesKeys.RESOLUTION] ?: "HD"
        val theme = preferences[PreferencesKeys.THEME] ?: "Auto"
        UserPreferences(resolution, theme)
    }

// Edit the DataStore.
suspend fun updateResolution(resolution: String) {
    dataStore.edit { preferences ->
        // when statement here to change more than just res.
        preferences[PreferencesKeys.RESOLUTION] = resolution
    }
}
}

设置视图模型

class SettingsViewModel(
userPreferencesRepository: UserPreferencesRepository
) : ViewModel() {

private val _resolution = MutableLiveData("HD")
val resolution: LiveData<String> = _resolution

fun onResolutionChanged(newResolution: String) {
    
    when (newResolution) {
        "540p" -> _resolution.value = "720p"
        "720p" -> _resolution.value = "HD"
        "HD" -> _resolution.value = "540p"
    }
}
}

设置组件

@Composable
fun Settings(
settingsViewModel: SettingsViewModel = viewModel(),
) {

val resolution: String by settingsViewModel.resolution.observeAsState("")

ScrollableColumn(Modifier.fillMaxSize()) {
    Column {
        ...
        Card() {
            Row() {
                Text(
                    text = "Resolution",
                )
                Text(text = resolution,
                    modifier = Modifier.clickable(
                        onClick = { settingsViewModel.onResolutionChanged(resolution) },
                        indication = null))
            }
        }
        ...
    }
}

}

试图让它与 Compose 一起工作对我来说是一场斗争,我希望得到任何帮助。非常感谢!

4

1 回答 1

4

由于您的 SettingsViewModel 使用 UserPreferencesRepository 作为构造函数参数,因此您必须提供一个工厂方法来实例化您的 ViewModel:

class SettingsViewModelFactory(private val userRepository:UserRepository) : ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            if (modelClass.isAssignableFrom(SettingsViewModel::class.java)) {
                return SettingsViewModel(userRepository) as T
            }
            throw IllegalArgumentException("Unknown ViewModel class")
        }
    }

val userRepository = UserRepository(context)
val viewModel: SettingsViewModel by viewModels { SettingsViewModelFactory(userRepository)}

如果你没有为 viewModels() 提供工厂,将使用 defaultViewModelProviderFactory,它只能实例化具有零参数构造函数的 viewModel

于 2020-12-11T22:53:39.923 回答