You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Jetpack Compose:保存数据后ScreenLearn页面无显示问题

问题描述

我有一个名为ScreenPlay的页面,其中的卡片带有保存选项。通过Logcat确认数据已成功保存,但导航至ScreenLearn页面时,页面为空,没有显示任何内容。以下是我的相关代码:

ScreenPlay.kt

@Composable
fun ScreenPlay(navController: NavController){

    // Mutable state to hold the dynamically updated saved cards
    var savedCards by remember { mutableStateOf(emptyList<SavedCard>()) }

    lateinit var playList:List<ImageModel>
    playList = ArrayList()
    playList = listOf(
        ImageModel("Tejas",R.drawable.television,R.drawable.save),
                   ImageModel("Nikita",R.drawable.food,R.drawable.save),
                   ImageModel("xyz",R.drawable.dish,R.drawable.save),
                   ImageModel("gggg",R.drawable.bird,R.drawable.save)
    )
    val context = LocalContext.current

    val cardViewmodel: ScreenViewModel = viewModel(
        factory = ScreenViewmodelFactory((context.applicationContext as MyApp).repository)
    )


  //  val navController = rememberNavController()
//For using the object on ArrowBack Icon
    val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
    Scaffold(topBar = {
        TopAppBar(title = { Text(text = "Days") },
            navigationIcon = {
                IconButton(onClick = { navController.popBackStack() }) {

                    Icon(
                        Icons.Default.ArrowBack,
                        contentDescription = "Back",
                        modifier = Modifier
                            .padding(16.dp)
                            .clickable { onBackPressedDispatcher?.onBackPressed() }
                    )

                }
            }

        )
    },
        floatingActionButton = {
            FloatingActionButton(onClick = {
                      navController.navigate(Destinations.ScreenLearn)
            },
                modifier = Modifier.padding(bottom = 50.dp, end = 40.dp)
            ) {
                Icon(
                    Icons.Default.Add,
                    contentDescription = "Save"
                )
            }
        },
        floatingActionButtonPosition = FabPosition.End
    ) {
        LazyColumn(
            modifier = Modifier
                .padding(10.dp)
                .padding(bottom = 56.dp)
                .navigationBarsPadding()
        ) {
            items(playList.size) {
                Card(
                    shape = RoundedCornerShape(20.dp),
                    modifier = Modifier
                        .padding(8.dp)
                        .height(150.dp),
                    elevation = 6.dp,
                ) {
                    Box{
                        Column(
                            Modifier
                                .fillMaxSize()
                                .padding(5.dp)
                                .background(Color(153, 50, 204))
                                .clip(RoundedCornerShape(20.dp)),
                            horizontalAlignment = Alignment.CenterHorizontally,
                            verticalArrangement = Arrangement.Center,
                        ) {
                            Image(
                                painter = painterResource(id = playList[it].imageId),
                                contentDescription = "Images"
                            )

                            Spacer(modifier = Modifier.padding(bottom = 5.dp))
                            Text(
                                text = playList[it].imageName,
                                color = Color.White,
                                fontWeight = FontWeight.Bold
                            )

                        }
                        Image(painter = painterResource(id = playList[it].imageSaved), contentDescription = "Save data",
                            modifier = Modifier
                                .padding(8.dp)
                                .padding(top = 10.dp)
                                .align(Alignment.TopEnd)
                                .clickable {

                                    val imagename = playList[it].imageName
                                    val imageResource = playList[it].imageId

                                    val newSaved =
                                        SavedCard(imageId =imageResource, imageName = imagename)
                                    savedCards += newSaved
                                    cardViewmodel.insertCards(newSaved)
                                    Log.d("Database", "Value saved: $newSaved")
                                    Toast.makeText(context,"Image saved", Toast.LENGTH_SHORT).show()


                                }
                        )
                    }

                }
            }
        }
    }

    }

ScreenLearn.kt

@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@Composable
fun ScreenLearn(savedCards: List<SavedCard>) {

    Scaffold(
        topBar = {
            TopAppBar(title = { Text(text = "Screen Learn") })
        }
    ) {
        LazyColumn(modifier = Modifier.padding(16.dp)) {
            items(savedCards) { savedCard ->
                Card(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(bottom = 16.dp),
                    elevation = 4.dp,
                    shape = RoundedCornerShape(8.dp)
                ) {
                    Column(modifier = Modifier.padding(16.dp)) {
                        Text(text = savedCard.imageName, style = MaterialTheme.typography.h6)
                        Image(
                            painter = painterResource(id = savedCard.imageId),
                            contentDescription = savedCard.imageName,

                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(top = 8.dp)
                                .height(200.dp)
                                .clip(shape = RoundedCornerShape(8.dp))
                                .background(Color.LightGray)
                        )

                    }
                }
            }
        }
    }
}

ScreenViewModel.kt

class ScreenViewModel(private val savedCardRepository:SavedCardRepository):ViewModel() {


    // Using LiveData and caching what allWords returns has several benefits:
    // - We can put an observer on the data (instead of polling for changes) and only update the
    //   the UI when the data actually changes.
    // - Repository is completely separated from the UI through the ViewModel.
    var allWords: LiveData<List<SavedCard>> = savedCardRepository.savedCards.asLiveData()

    /**
     * Launching a new coroutine to insert the data in a non-blocking way
     */
    fun insertCards(savedCard: SavedCard) = viewModelScope.launch {
        savedCardRepository.saveCard(savedCard)
    }

    fun deleteCards(savedCard: SavedCard) = viewModelScope.launch {
        savedCardRepository.deleteCard(savedCard)
    }

}

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

App.kt(导航配置)

@Composable
fun App() {


    val navController = rememberNavController()
    val items = listOf<String>("User","Play")
    val selectedItem = remember {
        mutableStateOf(0)
    }
    val savedCards = remember { mutableStateListOf<SavedCard>() }

    Scaffold(bottomBar = {
        BottomNavigation {
            items.forEachIndexed { index, item ->
                BottomNavigationItem(selected = selectedItem.value == index,
                    onClick = {
                        selectedItem.value = index
                        when(index){
                            0->navController.navigate(Destinations.ScreenA)
                            1->navController.navigate(Destinations.ScreenPlay)
                        }
                              },
                    icon = {
                        if (index==0){
                            Icon(Icons.Default.Person, contentDescription = item)

                        }else{
                            Icon(Icons.Default.PlayArrow, contentDescription = item)
                        }
                         },
                    label = { Text(text = item) }
                )

            }

        }
    }
    ) {
        NavHost(navController = navController, startDestination = Destinations.ScreenA) {

            composable(ScreenA) {
                ScreenA(navController)
            }
            composable(ScreenB) {
                ScreenB(navController)
            }
            composable(ScreenC) {
                ScreenC(navController)
            }

            composable(Destinations.ScreenPlay){
                ScreenPlay(navController)
            }
            composable(Destinations.ScreenLearn){

                ScreenLearn(savedCards = savedCards)
            }
        }
    }
}
排查与修复指导

核心问题

代码存在两处数据同步断层,导致ScreenLearn无法获取已保存的数据库数据:


问题1:ScreenPlay本地状态无效且未同步

ScreenPlay里维护的本地savedCards状态,既没和ViewModel的数据库数据关联,也没同步到App.kt传给ScreenLearn的列表,属于冗余无效代码。

修复:

  1. 删除ScreenPlay中的本地savedCards状态:
// 移除这一行
var savedCards by remember { mutableStateOf(emptyList<SavedCard>()) }
  1. 删除保存点击事件中对本地状态的更新:
// 移除这一行
savedCards += newSaved

问题2:ScreenLearn未订阅数据库数据

当前ScreenLearn接收的是App.kt初始化的空列表,这个列表从未从数据库拉取数据,所以一直为空。

修复:

  1. 修改ScreenLearn,让它通过ViewModel获取数据库数据:
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@Composable
fun ScreenLearn(navController: NavController) {
    val context = LocalContext.current
    val cardViewmodel: ScreenViewModel = viewModel(
        factory = ScreenViewmodelFactory((context.applicationContext as MyApp).repository)
    )
    // 订阅ViewModel中的LiveData数据
    val savedCards by cardViewmodel.allWords.observeAsState(emptyList())

    Scaffold(
        topBar = {
            TopAppBar(title = { Text(text = "Screen Learn") })
        }
    ) { padding ->
        LazyColumn(modifier = Modifier.padding(padding).padding(16.dp)) {
            items(savedCards) { savedCard ->
                Card(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(bottom = 16.dp),
                    elevation = 4.dp,
                    shape = RoundedCornerShape(8.dp)
                ) {
                    Column(modifier = Modifier.padding(16.dp)) {
                        Text(text = savedCard.imageName, style = MaterialTheme.typography.h6)
                        Image(
                            painter = painterResource(id = savedCard.imageId),
                            contentDescription = savedCard.imageName,
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(top = 8.dp)
                                .height(200.dp)
                                .clip(shape = RoundedCornerShape(8.dp))
                                .background(Color.LightGray)
                        )
                    }
                }
            }
        }
    }
}
  1. 更新App.kt中的导航配置,移除无用的savedCards状态并修改ScreenLearn的调用:
@Composable
fun App() {
    val navController = rememberNavController()
    val items = listOf<String>("User","Play")
    val selectedItem = remember { mutableStateOf(0) }

    Scaffold(bottomBar = {
        BottomNavigation {
            items.forEachIndexed { index, item ->
                BottomNavigationItem(selected = selectedItem.value == index,
                    onClick = {
                        selectedItem.value = index
                        when(index){
                            0->navController.navigate(Destinations.ScreenA)
                            1->navController.navigate(Destinations.ScreenPlay)
                        }
                    },
                    icon = {
                        if (index==0){
                            Icon(Icons.Default.Person, contentDescription = item)
                        }else{
                            Icon(Icons.Default.PlayArrow, contentDescription = item)
                        }
                    },
                    label = { Text(text = item) }
                )
            }
        }
    }
    ) { padding ->
        NavHost(navController = navController, startDestination = Destinations.ScreenA, modifier = Modifier.padding(padding)) {
            composable(ScreenA) { ScreenA(navController) }
            composable(ScreenB) { ScreenB(navController) }
            composable(ScreenC) { ScreenC(navController) }
            composable(Destinations.ScreenPlay){ ScreenPlay(navController) }
            composable(Destinations.ScreenLearn){ ScreenLearn(navController = navController) }
        }
    }
}

(注:顺便修复了Scaffold的padding未使用的问题)


额外验证点

  • 确认SavedCardRepository中的savedCards是正确的Flow(比如Room的@Query("SELECT * FROM saved_cards") fun getSavedCards(): Flow<List<SavedCard>>),这样ViewModel的allWords才能实时感知数据库变化。
  • 可以给ScreenLearn添加空状态提示:
LazyColumn(modifier = Modifier.padding(padding).padding(16.dp)) {
    if (savedCards.isEmpty()) {
        item {
            Column(
                modifier = Modifier.fillMaxSize(),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                Text(text = "暂无保存内容")
            }
        }
    } else {
        items(savedCards) { savedCard ->
            // 原有卡片代码
        }
    }
}

内容的提问来源于stack exchange,提问作者PurpleRabbit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 03:38:07