如何向 PagedList 中的每个项目添加数据

How to add data to each item in a PagedList

假设我正在使用 API 来获取汽车列表。此列表已实现,并且使用 PagedList with a PageKeyedDataSource 可以正常工作。汽车正在显示,当我向下滚动时,新汽车被加载并附加到 UI。不错!

现在我想显示每辆车的附加数据。说说汽车的价格吧。此附加数据来自另一个 API 端点。据我所知,我必须找到一种方法来使用 DataSource.map()DataSource.mapByPage() 为每辆车调用 API 以接收其价格。然后必须以某种方式将该价格添加到列表项中。

我正在使用架构组件(LiveData、数据绑定、MVVM 等)中的所有好东西。现在我没有数据库,我想保持这样。 Retrofit 正在为我做所有的缓存。

我该如何处理?

所以事实证明,唯一有意义的方法是在 DataSource 中添加来自第二个 API 调用的数据。原因是分页库认为 PagedList 是不可变的。因此,在我们调用 LoadInitialCallback.onResult()LoadCallback.onResult() 之前,所有数据都应该准备好并可用。

我的数据源现在看起来像这样:

override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, Car>) {

    val request = dataService.getCars(1) // load page 1 of all cars

    val response = request.execute()
    val items = response.body()?.values ?: emptyList()

    // we got the initial batch of cars -> now check them for prices
    fetchPrices(items, callback, nextPageKey = 2)
}

override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Repository>) {

    val request = dataService.getCars(params.key)

    request.enqueue(object : Callback<Cars> {
        override fun onResponse(call: Call<Cars>, response: Response<Cars>) {
            if (response.isSuccessful) {
                val items = response.body()?.values ?: emptyList()
                retry = null

                // we got a batch of cars -> now check them for prices
                fetchPrices(items, afterCallback = callback, nextPageKey = params.key + 1)

            } 
        }

        override fun onFailure(call: Call<Repositories>, t: Throwable) {
            ...
        }
    })
}

/**
 * This method is checking all of the given [cars] for having a price.
 * If a car has a price, this is added to the car.
 * If all cars were checked, the onResult-callback of this DataSource is called
 * to signal the completion of getting the paged data.
 */
private fun fetchPrices(
    repositories: List<Car>,
    initialCallback: LoadInitialCallback<Int, Car>? = null,
    afterCallback: LoadCallback<Int, Car>? = null,
    nextPageKey: Int
) {

    // used to count the server responses
    var checkedPrices = 0

    cars.forEach { car ->

        // enqueue the price request for this car
        val priceRequest = dataService.getPrice(car.id, 1)
        priceRequest.enqueue(object : Callback<Prices> {

            override fun onFailure(call: Call<Prices>, t: Throwable) {
                // ignore failed requests, but still check for completion
                // to increase the checkedPrices counter.
                checkForCompletion()
            }

            override fun onResponse(call: Call<Cars>, response: Response<Cars>) {
                if (!response.isSuccessful || response.body() == null) return
                val prices = (response.body() as Prices).values
                if (!prices.isNullOrEmpty()) {
                    car.price = prices.first()
                }
                checkForCompletion()
            }

            // check if we got server responses for all cars.
            // If yes, call either the initialCallback or the afterCallback
            private fun checkForCompletion() {
                checkedPrices++
                if (checkedPrices >= cars.size) {
                    initialCallback?.onResult(repositories, 0, nextPageKey)
                    afterCallback?.onResult(repositories, nextPageKey)
                }
            }
        })
    }
}