5.16
6. 显示商品详情
在此任务中,您将读取并在 Item Details 界面上显示实体详情。您将使用 Inventory 应用数据库中的商品界面状态(例如名称、价格和数量),并使用 ItemDetailsScreen
可组合项在 Item Details 界面上显示这些信息。我们为您预先编写了 ItemDetailsScreen
可组合函数,其中包含三个用于显示商品详情的 Text 可组合项。
ui/item/ItemDetailsScreen.kt
此屏幕是起始代码的一部分,显示了相应商品的详细信息,您会在后续 Codelab 中看到这些商品。您在此 Codelab 中不会处理此界面。ItemDetailsViewModel.kt
是此界面的对应 ViewModel
。
- 在
HomeScreen
可组合函数中,请注意HomeBody()
函数调用。navigateToItemUpdate
将传递给onItemClick
参数,当您点击列表中的任一商品时,此参数会被调用。
// No need to copy over
HomeBody(
itemList = homeUiState.itemList,
onItemClick = navigateToItemUpdate,
modifier = modifier
.padding(innerPadding)
.fillMaxSize()
)
- 打开
ui/navigation/InventoryNavGraph.kt
并注意HomeScreen
可组合项中的navigateToItemUpdate
参数。此参数将导航目的地指定为“Item Details”界面。
// No need to copy over
HomeScreen(
navigateToItemEntry = { navController.navigate(ItemEntryDestination.route) },
navigateToItemUpdate = {
navController.navigate("${ItemDetailsDestination.route}/${it}")
}
我们已为您实现 onItemClick
功能的这一部分。点击列表项后,应用会转到“Item Details”界面。
- 点击商品目录列表中的任意商品,即可查看包含空白字段的“Item Details”界面。
若要使用商品详情填充文本字段,您需要在 ItemDetailsScreen()
中收集界面状态。
- 在
UI/Item/ItemDetailsScreen.kt
中,向ItemDetailsScreen
可组合项添加一个ItemDetailsViewModel
类型的新参数,并使用工厂方法对其进行初始化。
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.inventory.ui.AppViewModelProvider
@Composable
fun ItemDetailsScreen(
navigateToEditItem: (Int) -> Unit,
navigateBack: () -> Unit,
modifier: Modifier = Modifier,
viewModel: ItemDetailsViewModel = viewModel(factory = AppViewModelProvider.Factory)
)
- 在
ItemDetailsScreen()
可组合项内,创建一个名为uiState
的val
来收集界面状态。使用collectAsState()
收集uiState
StateFlow
并通过State
表示其最新值。Android Studio 会显示未解决的引用错误。
import androidx.compose.runtime.collectAsState
val uiState = viewModel.uiState.collectAsState()
- 若要解决此错误,请在
ItemDetailsViewModel
类中创建一个名为uiState
且类型为StateFlow<ItemDetailsUiState>
的val
。 - 从商品存储库中检索数据,并使用扩展函数
toItemDetails()
将其映射到ItemDetailsUiState
。我们已经在起始代码中为您编写了扩展函数Item.toItemDetails()
。
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
val uiState: StateFlow<ItemDetailsUiState> =
itemsRepository.getItemStream(itemId)
.filterNotNull()
.map {
ItemDetailsUiState(itemDetails = it.toItemDetails())
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS),
initialValue = ItemDetailsUiState()
)
- 将
ItemsRepository
传入ItemDetailsViewModel
中以解决Unresolved reference: itemsRepository
错误。
class ItemDetailsViewModel(
savedStateHandle: SavedStateHandle,
private val itemsRepository: ItemsRepository
) : ViewModel() {
- 在
ui/AppViewModelProvider.kt
中,更新ItemDetailsViewModel
的初始化程序,如以下代码段所示:
initializer {
ItemDetailsViewModel(
this.createSavedStateHandle(),
inventoryApplication().container.itemsRepository
)
}
- 返回
ItemDetailsScreen.kt
,您会发现ItemDetailsScreen()
可组合项中的错误已解决。 - 在
ItemDetailsScreen()
可组合项中,更新ItemDetailsBody()
函数调用并将uiState.value
传入itemUiState
实参。
ItemDetailsBody(
itemUiState = uiState.value,
onSellItem = { },
onDelete = { },
modifier = modifier.padding(innerPadding)
)
- 观察
ItemDetailsBody()
和ItemInputForm()
的实现。您将当前选定的item
从ItemDetailsBody()
传递到ItemDetails()
。
// No need to copy over
@Composable
private fun ItemDetailsBody(
itemUiState: ItemUiState,
onSellItem: () -> Unit,
onDelete: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
//...
) {
var deleteConfirmationRequired by rememberSaveable { mutableStateOf(false) }
ItemDetails(
item = itemDetailsUiState.itemDetails.toItem(), modifier = Modifier.fillMaxWidth()
)
//...
}
- 运行应用。当您点击 Inventory 界面上的任何列表元素时,系统会显示 Item Details 界面。
- 请注意,界面已不再空白。该界面会显示从商品目录数据库中检索到的实体详情。
- 点按 Sell 按钮。毫无反应!
在下一部分中,您将实现 Sell 按钮的功能。