提问人:Zobaer Hossain 提问时间:11/16/2023 最后编辑:Zobaer Hossain 更新时间:11/21/2023 访问量:82
无法在 ViewModel 中过滤数据 - Android Kotlin
Unable to Filter Data in ViewModel - Android Kotlin
问:
从 API 获取数据时,我在 ViewModel 中过滤数据时遇到问题。我有一个 ViewModel (CharacterViewModel),它使用 getCharactersUseCase 检索数据。我想根据字符 ID 过滤此数据,并在可组合项 (CharacterDetailScreen) 中观察过滤后的结果。但是,我没有得到预期的结果。
CharacterView模型
@HiltViewModel
class CharacterViewModel @Inject constructor(
private val getCharactersUseCase: GetCharactersUseCase
) : ViewModel() {
private val _characterState: MutableStateFlow<PagingData<Character>> =
MutableStateFlow(value = PagingData.empty())
private val _characterDetailsState: MutableStateFlow<Character?> = MutableStateFlow(null)
val charactersState: MutableStateFlow<PagingData<Character>> get() = _characterState
val characterDetailsState: MutableStateFlow<Character?> get() = _characterDetailsState
init {
getCharacters()
}
private fun getCharacters() {
viewModelScope.launch {
getCharactersUseCase.execute(Unit)
.distinctUntilChanged()
.cachedIn(viewModelScope)
.collect {
_characterState.value = it
}
}
}
fun getCharacterDetails(characterID: String) {
Log.i("character"," character from viewmodel received id= ${characterID}")
// Collect the latest charactersState and then filter the character
viewModelScope.launch {
charactersState.collect { characters ->
characters
.filter { it.id == characterID }
.map { character ->
Log.i("character"," character from viewmodel id= ${character.id}")
_characterDetailsState.value = character
}
}
}
}
}
CharacterDetailScreen(字符详细信息屏幕)
@Composable
fun CharacterDetailScreen(
navController: NavController,
viewModel: CharacterViewModel = hiltViewModel(),
characterId: String
)
{
// Collect character details state
val characterDetailsState by viewModel.characterDetailsState.collectAsState()
LaunchedEffect(characterDetailsState) {
viewModel.getCharacterDetails(characterId)
}
// Use LaunchedEffect to trigger the getCharacterDetails call when the Composable is first launched
CharacterDetailsItem(characterDetailsState, characterId)
}
CharacterDetailsItem
@Composable
fun CharacterDetailsItem(character_: Character?, characterId: String){
Log.i("character"," character from details item ${character_?.name}")
val character by remember { mutableStateOf(character_) }
Box(modifier = Modifier.padding(top = 8.dp)) {
Card(
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant,
),
modifier = Modifier
.fillMaxWidth()
) {
Column {
Text(
text = characterId,
modifier = Modifier
.padding(16.dp),
textAlign = TextAlign.Center,
)
character?.name?.let {
Text(
text = it,
modifier = Modifier
.padding(16.dp),
textAlign = TextAlign.Center,
)
}
character?.gender?.let {
Text(
text = it,
modifier = Modifier
.padding(16.dp),
textAlign = TextAlign.Center,
)
}
}
}
}
}
洛卡特
2023-11-17 12:01:19.709 5486-5486 character com.starwars.app I character from details item null
2023-11-17 12:01:19.740 5486-5486 character com.starwars.app I character from viewmodel received id= 631a02dc-ecf2-4636-8e59-d180766e52cf
2023-11-17 12:01:19.760 5486-5486 character com.starwars.app I character from details item null
2023-11-17 12:01:20.472 5486-5486 character com.starwars.app I character from details item null
2023-11-17 12:01:20.524 5486-5486 character com.starwars.app I character from details item null
我提供了 ViewModel (CharacterViewModel)、Composable (CharacterDetailScreen) 和用于显示详细信息的 Composable (CharacterDetailsItem) 的代码片段。我将不胜感激,了解可能导致 ViewModel 中过滤逻辑出现问题的原因。
我试过什么:
第一:
fun getCharacterDetails(characterID: String) {
Log.i("character", " character from viewmodel received id= ${characterID}")
// Collect the latest charactersState and then filter the character
viewModelScope.launch {
_characterDetailsState.value = Character("213","bday","e","g","h","hi","name","skin")
}
}
这样,我就可以设置和获取价值,但不能从 charactersState 或过滤_characterState值中获取。
第二:
@Composable
fun CharacterDetailScreen(
navController: NavController,
viewModel: CharacterViewModel = hiltViewModel(),
characterId: String
) {
// Collect character details state
val characterPagingItems: LazyPagingItems<Character> =
viewModel.charactersState.collectAsLazyPagingItems()
val selectedCharacter = characterPagingItems.itemSnapshotList
.find { it?.id == characterId }
// Call CharacterDetailsItem when selectedCharacter is not null
selectedCharacter?.let { character ->
CharacterDetailsItem(character, characterId) //this composable
//gets recomposed with null values
}
Text(text = "first") //this text stays
}
我的DI字符模块:
@Module
@InstallIn(ViewModelComponent::class)
object CharacterModule {
@Provides
@ViewModelScoped
fun providesCharacterRemoteDataSource(
api: StarWarsApi
): CharacterRemoteDataSource {
return CharacterRemoteDataSourceImpl(api)
}
@Provides
@ViewModelScoped
fun providesCharacterRepository(
characterRemoteDataSource: CharacterRemoteDataSource,
starWarsDatabase: StarWarsDatabase
): CharacterRepository {
return CharacterRepositoryImpl(characterRemoteDataSource,
starWarsDatabase)
}
@Provides
@ViewModelScoped
fun providesGetCharactersUseCase(
characterRepository: CharacterRepository
): GetCharactersUseCase {
return GetCharactersUseCase(characterRepository)
}
@Provides
@ViewModelScoped
fun providesGetCharacterDetailsUseCase(
characterRepository: CharacterRepository
): GetCharacterDetailsUseCase {
return GetCharacterDetailsUseCase(characterRepository)
}
@Provides
@ViewModelScoped
fun provideCharacterDetailsViewModel(
getCharactersUseCase: GetCharactersUseCase
): CharacterViewModel {
return CharacterViewModel(getCharactersUseCase)
}
}
答:
1赞
Nazarii Moshenskiy
11/21/2023
#1
我认为你不应该收集里面的状态.如果要按搜索查询筛选状态列表。ViewModel
您可能需要考虑类似的解决方案:https://stackoverflow.com/a/72311648/7805359
评论
0赞
Zobaer Hossain
11/21/2023
感谢您提出另一种方法。但是,在实现它时,我遇到了将过滤后的值从 传递到可组合项的问题。一段时间后,传递的值似乎丢失了。我怀疑出现这个问题是因为 ViewModel 范围随着时间的推移变得无法访问。注意:我正在将单个依赖注入 (DI) 模块和 ViewModel 用于角色屏幕和角色详细信息屏幕。characterDetailsScreen
characterItem
characterDetailsScreen
0赞
Ehsan Setayesh
11/25/2023
#2
Try this
fun getCharacterDetails(characterID: String) {
Log.i("character"," character from viewmodel received id=
${characterID}")
// Collect the latest charactersState and then filter the character
viewModelScope.launch {
charactersState.collect { characters ->
val filteredCharacters = characters.filter { it.id ==
characterID }
if (filteredCharacters.isNotEmpty()) {
_characterDetailsState.value = filteredCharacters.first()
} else {
_characterDetailsState.value = null
}
}
}
}
@Composable
fun CharacterDetailScreen(
navController: NavController,
viewModel: CharacterViewModel = hiltViewModel(),
characterId: String
) {
// Call getCharacterDetails directly
viewModel.getCharacterDetails(characterId)
// Collect character details state
val characterDetailsState by
viewModel.characterDetailsState.collectAsState()
// Use characterDetailsState in your composable
CharacterDetailsItem(characterDetailsState, characterId)
}
评论
0赞
Zobaer Hossain
11/26/2023
谢谢你的回答。试过这个,但它不起作用。
评论