我从另一个应用程序复制粘贴的代码,它执行完全相同的操作并且有效。我的代码中的 log.i() 语句显示信息发生了变化,但我的谷歌地图可组合项不会使用从 viewModel.getRestaurantList() 收到的新列表重新组合。我发现唯一可疑的是按钮内的 Log.i() 始终为空,即使视图模型中的函数内的另一个 Log.i() 显示列表已由函数 getRestaurantList 更新视图模型。这是相关代码:
充当屏幕的可组合项:
@Composable
fun SearchScreen(
myName: String,
navController: NavController,
context: Context = LocalContext.current,
) {
val viewModel: LocationViewModel = LocationViewModel(context)
var user: User = User(myName, 1, 0.0, 0.0)
...
}
搜索屏幕中的可组合项可以工作但不会更新(地图加载并且我可以导航,但是单击按钮后基于收到的列表的标记不会出现):
ComposeMapDemo(
restaurantList = viewModel.restaurantListResponse,
myName,
onMarkerClick = { marker, restaurant -> navController.navigate(route = ... },
)
调用函数更新餐厅列表的按钮:
Button(onClick = {
//this log statement is always empty, no matter how often I click on the button
Log.i("Tag", "restaurantList inside button" + viewModel.restaurantListResponse.toString())
viewModel.getRestaurantList(restaurant)
}) {
Text(text = "Scan")
}
视图模型:
class LocationViewModel(val context: Context) : ViewModel() {
var restaurantListResponse: List<Restaurant> by mutableStateOf(listOf())
fun getRestaurantList(restaurant: Restaurant) {
viewModelScope.launch {
try {
restaurantListResponse = getRestaurantsListRequest(restaurant)
//this Log.i shows that restaurantListResponse updates
Log.i(
"Tag",
"inside the getRestaurantList function of viewmodel" + restaurantListResponse.toString()
)
} catch (e: Exception) {
errorMessage = e.message.toString()
}
}
}
}
谷歌地图可组合项的详细信息(我认为不相关):
@Composable
fun ComposeMapDemo(
restaurantList: List<Restaurant>,
myName: String?,
onMarkerClick: (Marker, restaurant) -> Boolean,
) {
val currentOnMarkerClick by rememberUpdatedState(onMarkerClick)
val cameraPositionState = rememberCameraPositionState()
val markerState = rememberMarkerState()
SideEffect {
if (restaurantList.isEmpty()) return@SideEffect
val restaurant = restaurantList[0]
val lat = restaurant?.latitude ?: return@SideEffect
val lng = restaurant?.longitude ?: return@SideEffect
val latlng = LatLng(lat, lng)
markerState.position = latlng
cameraPositionState.position = CameraPosition.fromLatLngZoom(latlng, 500f)
}
GoogleMap(
modifier = Modifier
.fillMaxHeight(0.5F)
.fillMaxWidth(0.75F),
cameraPositionState = cameraPositionState
) {
for (restaurant in restaurantList) {
if (restaurant.open == false) {
continue
} else {
Marker(
icon = BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_AZURE),
state = MarkerState(restaurant.position),
title = "${restaurant.name}",
onClick = { currentOnMarkerClick(it, restaurant) },
)
}
}
}
}
在您的视图模型中,您可以使用:
虽然在可组合项中使用委托
by
来简化对变量的访问是一种很好的做法,但在视图模型中执行此操作将从 Compose 中隐藏 MutableState。由于 Compose 只看到一个普通的 List,不知道底层的 State,因此它无法观察 State 的变化,因此当 State 发生变化时,它永远不会触发重组。如果您更改该变量以直接保存状态,它应该可以工作:
在任何访问该变量的地方都需要添加
.value
它。不过,这样做有几个缺点。首先,该状态现在可以从视图模型外部写入。您可能应该将其设为私有,并添加另一个变量来将其公开为 a
State
而不是MutableState
.MutableState
但是,您一开始就不应该在视图模型中使用s。许多旧文档和示例代码仍然建议这样做,但最佳实践已经随着时间的推移而发生变化,为了避免这种方法的一些令人讨厌的边缘情况,现在的首选方法是使用MutableStateFlow
:在视图模型中,您可以像这样更改值(同时更新日志语句):
在您的撰写代码中,您必须先将 Flow 转换为 State,然后再将其传递给
ComposeMapDemo
:请注意,这需要 gradle 依赖
androidx.lifecycle:lifecycle-runtime-compose
。