5.21

3. 更新界面状态

在此任务中,您将向应用添加一个 LazyColumn 来显示存储在数据库中的数据。

显示商品目录中商品的手机屏幕

HomeScreen 可组合函数演示

  • 打开 ui/home/HomeScreen.kt 文件并查看 HomeScreen() 可组合项。
 
@Composable
fun HomeScreen(
    navigateToItemEntry: () -> Unit,
    navigateToItemUpdate: (Int) -> Unit,
    modifier: Modifier = Modifier,
) {
    val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()

    Scaffold(
        topBar = {
            // Top app with app title
        },
        floatingActionButton = {
            FloatingActionButton(
                // onClick details
            ) {
                Icon(
                    // Icon details
                )
            }
        },
    ) { innerPadding ->

       // Display List header and List of Items
        HomeBody(
            itemList = listOf(),  // Empty list is being passed in for itemList
            onItemClick = navigateToItemUpdate,
            modifier = modifier.padding(innerPadding)
                              .fillMaxSize()
        )
    }

此可组合函数会显示以下各项:

  • 带有应用名称的顶部应用栏
  • 用于向商品目录中添加新商品的悬浮操作按钮 (FAB) 7b1535d90ee957fa.png
  • HomeBody() 可组合函数

HomeBody() 可组合函数会根据传入的列表显示商品目录商品。在起始代码实现中,我们将空列表 (listOf()) 传递给了 HomeBody() 可组合函数。如需将商品目录列表传递给此可组合函数,您必须从存储库中检索商品目录数据,并将其传入 HomeViewModel

在 HomeViewModel 中发出界面状态

当您向 ItemDao 添加用于获取商品的 getItem() 和 getAllItems() 方法时,您将 Flow 指定为返回值类型。回想一下,Flow 代表通用数据流。通过返回 Flow,您只需在指定生命周期内明确调用 DAO 中的方法一次即可。Room 以异步方式处理底层数据的更新。

从数据流中获取数据的过程称为收集数据流。从界面层中的数据流收集数据时,需要考虑一些事项。

  • 配置更改等生命周期事件(例如旋转设备)会导致重新创建 activity,进而导致重组,并从您的 Flow 重新收集数据。
  • 建议您将值缓存为状态,这样现有数据就不会在生命周期事件之间丢失。
  • 如果没有任何观察器(例如在可组合项的生命周期结束后),则应取消数据流。

如需从 ViewModel 公开 Flow,推荐使用 StateFlow。无论界面生命周期如何,使用 StateFlow 均可保存和观察数据。如需将 Flow 转换为 StateFlow,您可以使用 stateIn 操作符。

stateIn 操作符有三个参数,如下所述:

  • scope - viewModelScope 定义了 StateFlow 的生命周期。取消 viewModelScope 后,StateFlow 也会取消。
  • started - 仅当界面可见时,流水线才应有效。为此,请使用 SharingStarted.WhileSubscribed()。如需配置从最后一个订阅者消失到停止共享协程之间的延迟时间(以毫秒为单位),请将 TIMEOUT_MILLIS 传递给 SharingStarted.WhileSubscribed() 方法。
  • initialValue - 将状态流的初始值设置为 HomeUiState()

将 Flow 转换为 StateFlow 后,您可以使用 collectAsState() 方法对其进行收集,并将其数据转换为相同类型的 State

在此步骤中,您将检索 Room 数据库中的所有商品,作为界面状态的 StateFlow 可观察 API。当 Room Inventory 数据发生更改时,界面会自动更新。

  1. 打开 ui/home/HomeViewModel.kt 文件,其中包含一个 TIMEOUT_MILLIS 常量和一个 HomeUiState 数据类,该类将商品列表作为构造函数参数。
 
// No need to copy over, this code is part of starter code

class HomeViewModel : ViewModel() {

    companion object {
        private const val TIMEOUT_MILLIS = 5_000L
    }
}

data class HomeUiState(val itemList: List<Item> = listOf())
  1. 在 HomeViewModel 类中,声明一个名为 homeUiState 且类型为 StateFlow<HomeUiState> 的 val。您很快就要解决初始化错误。
 
val homeUiState: StateFlow<HomeUiState>
  1. 对 itemsRepository 调用 getAllItemsStream(),并将其分配给您刚刚声明的 homeUiState
 
val homeUiState: StateFlow<HomeUiState> =
    itemsRepository.getAllItemsStream()

您现在会收到一个“Unresolved reference: itemsRepository”错误。如需解决“Unresolved reference”错误,您需要将 ItemsRepository 对象传递给 HomeViewModel

  1. 将类型为 ItemsRepository 的构造函数参数添加到 HomeViewModel 类中。
 
import com.example.inventory.data.ItemsRepository

class HomeViewModel(itemsRepository: ItemsRepository): ViewModel() {
  1. 在 ui/AppViewModelProvider.kt 文件的 HomeViewModel 初始化程序中,传递 ItemsRepository 对象,如下所示。
 
initializer {
    HomeViewModel(inventoryApplication().container.itemsRepository)
}
  1. 返回至 HomeViewModel.kt 文件。请注意类型不匹配错误。如需解决此问题,请添加转换映射,如下所示。
 
val homeUiState: StateFlow<HomeUiState> =
    itemsRepository.getAllItemsStream().map { HomeUiState(it) }

Android Studio 仍会显示类型不匹配错误。导致此错误的原因在于,homeUiState 的类型为 StateFlow,而 getAllItemsStream() 却返回了 Flow

  1. 使用 stateIn 操作符将 Flow 转换为 StateFlowStateFlow 是界面状态的可观察 API,可让界面自行更新。
 
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

val homeUiState: StateFlow<HomeUiState> =
    itemsRepository.getAllItemsStream().map { HomeUiState(it) }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS),
            initialValue = HomeUiState()
        )
  1. 构建应用,确保代码中没有错误。请注意,用户界面不会有任何变化。
posted @ 2024-06-19 22:16  混沌武士丞  阅读(2)  评论(0编辑  收藏  举报