0

我正在尝试根据 ViewModel 中titleTopAppBar实时数据更新 ,我在不同的屏幕上更新。看起来实时数据正在正确更新,但更新并未反映在 TopAppBar 的标题上。这是代码:

class MainActivity : ComponentActivity() {
    @ExperimentalFoundationApi
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            LazyVerticalGridActivityScreen()
        }
    }
}

@ExperimentalFoundationApi
@Composable
fun LazyVerticalGridActivityScreen(destinationViewModel: DestinationViewModel = viewModel()) {
    val navController = rememberNavController()
    var canPop by remember { mutableStateOf(false) }

    // getting the latest title value from the view model
    val title: String by destinationViewModel.title.observeAsState("")
    Log.d("MainActivity_title", title) // not getting called

    navController.addOnDestinationChangedListener { controller, _, _ ->
        canPop = controller.previousBackStackEntry != null
    }

    val navigationIcon: (@Composable () -> Unit)? =
        if (canPop) {
            {
                IconButton(onClick = { navController.popBackStack() }) {
                    Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = null)
                }
            }
        } else {
            null
        }

    Scaffold(
        topBar = {
            TopAppBar(title = { Text(title) }, navigationIcon = navigationIcon) // updating the title
        },
        content = {
            NavHost(navController = navController, startDestination = "home") {
                composable("home") { HomeScreen(navController) }
                composable("details/{listId}") { backStackEntry ->
                    backStackEntry.arguments?.getString("listId")?.let { DetailsScreen(it, navController) }
                }
            }
        }
    )
}
@ExperimentalFoundationApi
@Composable
fun HomeScreen(navController: NavHostController, destinationViewModel: DestinationViewModel = viewModel()) {
    val destinations = DestinationDataSource().loadData()

    // updating the title in the view model
    destinationViewModel.setTitle("Lazy Grid Layout")

    LazyVerticalGrid(
        cells = GridCells.Adaptive(minSize = 140.dp),
        contentPadding = PaddingValues(8.dp)
    ) {
        itemsIndexed(destinations) { index, destination ->
            Row(Modifier.padding(8.dp)) {
                ItemLayout(destination, index, navController)
            }
        }
    }
}

@Composable
fun ItemLayout(
    destination: Destination,
    index: Int,
    navController: NavHostController
) {
    Column(
        verticalArrangement = Arrangement.Center,
        modifier = Modifier
            .background(MaterialTheme.colors.primaryVariant)
            .fillMaxWidth()
            .clickable {
                navController.navigate("details/$index")
            }
    ) {
        Image(
            painter = painterResource(destination.photoId),
            contentDescription = stringResource(destination.nameId),
            modifier = Modifier.fillMaxWidth(),
            contentScale = ContentScale.Crop
        )
        Text(
            text = stringResource(destination.nameId),
            color = Color.White,
            fontSize = 14.sp,
            modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp)
        )
    }
}

@Composable
fun DetailsScreen(
    index: String,
    navController: NavController,
    destinationViewModel: DestinationViewModel = viewModel()
) {
    val dataSource = DestinationDataSource().loadData()

    val destination = dataSource[index.toInt()]
    val destinationName = stringResource(destination.nameId)
    val destinationDesc = stringResource(destination.descriptionId)
    val destinationImage = painterResource(destination.photoId)

    // updating the title in the view model
    destinationViewModel.setTitle("Destination Details")

    Column(
        modifier = Modifier
            .padding(16.dp)
            .fillMaxWidth()
            .verticalScroll(rememberScrollState())
    ) {
        Image(
            painter = destinationImage,
            contentDescription = destinationName,
            contentScale = ContentScale.Crop,
            modifier = Modifier.fillMaxWidth()
        )
        Column(modifier = Modifier.padding(horizontal = 16.dp)) {
            Text(
                text = destinationName,
                fontSize = 24.sp,
                fontWeight = FontWeight.Bold,
                modifier = Modifier.padding(vertical = 16.dp)
            )
            Text(text = destinationDesc, lineHeight = 24.sp)
            Row(horizontalArrangement = Arrangement.End, modifier = Modifier.fillMaxWidth()) {
                OutlinedButton(
                    onClick = {
                        navController.navigate("home") {
                            popUpTo("home") { inclusive = true }
                        }
                    },
                    modifier = Modifier.padding(top = 24.dp)
                ) {
                    Image(
                        imageVector = Icons.Filled.ArrowBack,
                        contentDescription = null,
                        colorFilter = ColorFilter.tint(MaterialTheme.colors.primaryVariant),
                        modifier = Modifier.size(20.dp)
                    )
                    Text("Back to Destinations", modifier = Modifier.padding(start = 16.dp))
                }
            }
        }
    }
}

编辑:视图模型

class DestinationViewModel : ViewModel() {

    private var _title = MutableLiveData("")
    val title: LiveData<String>
        get() = _title

    fun setTitle(newTitle: String) {
        _title.value = newTitle
        Log.d("ViewModel_title", _title.value.toString())
        Log.d("ViewModelTitle", title.value.toString())
    }
}

任何人都可以帮助找到错误吗?谢谢!

编辑:

这是该项目的 GitHub 链接:https ://github.com/rawhasan/compose-exercise-lazy-vertical-grid

4

2 回答 2

1

它不起作用的原因是因为这些是在不同范围内创建的不同对象。

当您使用 anavController时,每个目的地都有自己的viewModel()创建范围。根据设计,您可能对每个目的地都有一个视图模型,例如HomeViewModel,DestinationViewModel

您无法从当前目标范围访问其他目标视图模型,也无法从外部范围访问视图模型(您正在尝试这样做)

您可以做的不是尝试使用 检索它viewModel(),而是可以将外部范围视图模型传递给您的可组合:

composable("details/{listId}") { backStackEntry ->
    backStackEntry.arguments?.getString("listId")?.let { DetailsScreen(it, navController, destinationViewModel) }
}

viewModel()文档中查看更多详细信息


您的代码的另一个问题是您destinationViewModel.setTitle("Lazy Grid Layout")在可组合函数内部调用。所以这段代码会被多次调用,可能会导致重组。

要调用可组合内的任何操作,您需要使用副作用LaunchedEffect在这种情况下:

LaunchedEffect(Unit) {
    destinationViewModel.setTitle("Destination Details")
}

这只会在视图出现后调用一次。如果你需要更频繁地调用它,你需要指定 key 而不是Unit,所以每次 key 改变时都会调用它

于 2021-08-09T04:12:59.773 回答
0

你可能想看看这里https://stackoverflow.com/a/68671477/15880865

此外,您不需要粘贴问题中的所有代码。只是必要的一点。如果问题中缺少某些内容,我们将要求它。只需将您的更改LiveDatamutableStateOf,然后让我知道这是否解决了您的问题。此外,您不需要observeAsState在修改类型后调用。只需参考它包含所有信息的链接。

谢谢

于 2021-08-08T16:03:39.833 回答