【问题标题】:How to observe PagedList data?如何观察 PagedList 数据?
【发布时间】:2017-11-06 05:00:10
【问题描述】:

我正在使用 Paging Library 和 Android 架构组件。我只是想观察 pagedlist livedata 并在有变化时更新我的​​ RecyclerView。

我正在观察 isLoadingLiveData、isEmptyLiveData 和 errorLiveData 对象,它们是在我的 ViewModel 中创建并在我的片段中观察到的 MediatorLiveData 对象。并观察 resultLiveData 从远程返回获取的 Gist 列表。

在我的 ViewModel 中,我创建了一个 PagedList LiveData,每当它的数据发生变化时,我都想更新 isLoadingLiveData、isEmptyLiveData、errorLiveData 和 PagedListAdapter。因此,我将 isLoadingLiveData、isEmptyLiveData、errorLiveData 和 resultLiveData 定义为 MediatorLiveData 对象。我添加了 resultLiveData 作为这些对象的来源。所以当 resultLiveData 发生变化时,这些对象的 onChanged 方法就会被调用。并且 resultLiveData 依赖于 userNameLiveData,所以当 userNameLiveData 发生变化时,会调用 allGistsLiveData 并获取数据。例如,当用户滑动列表时,我正在设置 userNameLiveData 并再次进行网络调用。

我的视图模型:

private val userNameLiveData = MutableLiveData<String>()
private var gists: LiveData<PagedList<Gist>>? = null

val allGistsLiveData: LiveData<PagedList<Gist>>
    get() {
        if (null == gists) {
            gists = GistModel(NetManager(getApplication()), getApplication()).getYourGists(userNameLiveData.value!!).create(0,
                    PagedList.Config.Builder()
                            .setPageSize(PAGED_LIST_PAGE_SIZE)
                            .setInitialLoadSizeHint(PAGED_LIST_PAGE_SIZE)
                            .setEnablePlaceholders(PAGED_LIST_ENABLE_PLACEHOLDERS)
                            .build())
        }
        return gists!!
    }

val resultLiveData = MediatorLiveData<LiveData<PagedList<Gist>>>().apply {
    this.addSource(userNameLiveData) {
        gists = null
        this.value = allGistsLiveData
    }
}


val isLoadingLiveData = MediatorLiveData<Boolean>().apply {
    this.addSource(resultLiveData) { this.value = false }
}

val isEmptyLiveData = MediatorLiveData<Boolean>().apply {
    this.addSource(resultLiveData) { this.value = false }
}

val errorLiveData = MediatorLiveData<Boolean>().apply {
    this.addSource(resultLiveData) {
        if (it == null) {
            this.value = true
        }
    }
}

fun setUserName(userName: String) {
    userNameLiveData.value = userName
}

还有我的片段:

override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    viewModel.isEmptyLiveData.observe(this@YourGistsFragment, Observer<Boolean> { isVisible ->
        emptyView.visibility = if (isVisible!!) View.VISIBLE else View.GONE
    })

    viewModel.isLoadingLiveData.observe(this@YourGistsFragment, Observer<Boolean> {
        it?.let {
            swipeRefreshLayout.isRefreshing = it
        }
    })

    viewModel.errorLiveData.observe(this@YourGistsFragment, Observer<Boolean> {
        it?.let {
            showSnackBar(context.getString(R.string.unknown_error))
        }
    })

    viewModel.setUserName("melomg")
    viewModel.resultLiveData.observe(this@YourGistsFragment, Observer { it -> gistAdapter.setList(it?.value) })
}


override fun onRefresh() {
    viewModel.setUserName("melomg")
}

我的仓库:

fun getYourGists(userName: String): LivePagedListProvider<Int, Gist> {
    return remoteDataSource.getYourGists(userName, GitHubApi.getGitHubService(context))
}

我的远程数据源:

fun getYourGists(username: String, dataProvider: GitHubService): LivePagedListProvider<Int, Gist> {
    return object : LivePagedListProvider<Int, Gist>() {
        override fun createDataSource(): GistTiledRemoteDataSource<Gist> = object : GistTiledRemoteDataSource<Gist>(username, dataProvider) {
            override fun convertToItems(items: ArrayList<Gist>?): ArrayList<Gist>? {
                return items
            }
        }
    }
}

我尝试从this project 创建此解决方案。但我的问题是 resultLiveData 一直在更改而无需等待网络调用响应,因此响应的结果被忽略,我的 ui 在数据到达之前正在更新。由于 resultLiveData 在请求之前正在更改,因此还没有数据。简单来说,我如何观察 pagedlist livedata?

【问题讨论】:

  • @pskink LivePagedListProvider. GistModel().getYourGists() returns LiveData&lt;PagedList&gt; 没有错。但我想观察 LivePagedListProvider 的数据,它不是 ArrayList&lt;Gist&gt; 本身。问题是 allGistsLiveData 在初始化时会发生变化,而不是在数据从远程返回时发生变化。
  • @pskink 我添加了我的存储库和 remoteDataSource 类以显示 LivePagedListProvider 实现。

标签: android kotlin android-architecture-components android-livedata android-viewmodel


【解决方案1】:

最后,我找到了一个可行的解决方案,但我认为这不是最佳解决方案。因此,如果您有任何想法,请不要犹豫回答。这是我的解决方案:

我放弃了用MediatorLiveData 包装我的PagedList 数据。我仍然将所有其他实时数据(isLoadingLiveData, isEmptyLiveData, errorLiveData)保留为MediatorLiveData,但在我的要点 PagedList LiveData 初始化时添加了它们的源。代码如下:

private var gists: LiveData<PagedList<Gist>>? = null
private val userNameLiveData = MutableLiveData<String>()
val isLoadingLiveData = MediatorLiveData<Boolean>()
val isEmptyLiveData = MediatorLiveData<Boolean>()
val errorLiveData = MediatorLiveData<ErrorMessage>()

val allGistsLiveData: LiveData<PagedList<Gist>>
    get() {
        if (gists == null) {
                gists = GistModel(NetManager(getApplication()), getApplication()).getYourGists(userNameLiveData.value!!).create(0,
                        PagedList.Config.Builder()
                                .setPageSize(Constants.PAGED_LIST_PAGE_SIZE)
                                .setInitialLoadSizeHint(Constants.PAGED_LIST_PAGE_SIZE)
                                .setEnablePlaceholders(Constants.PAGED_LIST_ENABLE_PLACEHOLDERS)
                                .build())


                isLoadingLiveData.addSource(gists) { isLoadingLiveData.value = false }
                isEmptyLiveData.addSource(gists) {
                    if (it?.size == 0) {
                        isEmptyLiveData.value = true
                    }
                }
                errorLiveData.addSource(gists) {
                    if (it == null) {
                        errorLiveData.value = ErrorMessage(errorCode = ErrorCode.GENERAL_ERROR)
                    }
                }
            }
        }
        return gists!!
    }


fun setUserName(userName: String) {
    isLoadingLiveData.value = true
    userNameLiveData.value = userName
    gists = null
    allGistsLiveData
}

【讨论】:

  • 我希望 Android 开发人员能想出一个使用 Paging 的 MediatorLiveData 解决方案。就像一个 MediatorLivePagedListBuilder。我们应该把它列入我们的愿望清单
【解决方案2】:

有更好的方法来做到这一点。看这个例子:

https://github.com/android/architecture-components-samples/tree/master/PagingWithNetworkSample

诀窍是在您的 DataSource 中放入一个可变的实时数据,如下所示:

enum class NetworkState { LOADING, LOADED, FAILURE }

val networkState = MutableLiveData<NetworkState>()

override fun loadInitial(params: LoadInitialParams<String>, callback: LoadInitialCallback<String,  OrderService.Resource>) {
    networkState.post(NetworkState.LOADING)
    ApiClient.listMyObjects(
            onSuccess = { list ->
                networkState.post(NetworkState.LOADED)
                callback.onResult(list)
            }
            onFailure = {
                networkState.post(NetworkState.FAILURE)
            }
    )
}

然后您可以通过以下方式收听您的分页列表和网络状态:

val myDataSource = MyDataSource.Factory().create()
myDataSource.toLiveData().observe(viewLifeCycle, Observer { pagedList ->
    // Update your recycler view
    myRecyclerViewAdapter.submitList(pagedList)
})
myDataSource.networkState.observe(viewLifeCycle, Observer { state ->
    // Show errors, retry button, progress view etc. according to state  
})

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-04
    • 2020-04-28
    • 1970-01-01
    • 2020-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多