Android-分页库-滚动时列表跳跃

时间:2019-08-28 07:26:20

标签: android kotlin android-room android-paging

我正在尝试使用Android Paging库显示条目列表。为此,我使用的是自定义PageDataSource,它从Room数据库读取数据,并在需要时从网络更新数据。为了用新数据更新本地数据库,我不使用边界回调,因为它没有提供每页更新数据的选项,而是仅在用户滚动到本地列表的底部时才提供。这意味着除非本地数据是第一页或最后一页的一部分,否则永远不会更新。

这是我的数据源:

class EntryPageDataSource(
    private val entryDao: EntryDao,
    private val service: AppService
) : PageKeyedDataSource<Int, EntryEntity>() {
    companion object {
        val lastSync = SparseArray<Instant>()
    }
arrives
    private val invalidationTracker =
        object : InvalidationTracker.Observer(EntryEntity.TABLE_NAME) {
            override fun onInvalidated(tables: MutableSet<String>) {
                this@EntryPageDataSource.invalidate()
            }
        }

    init {        
    //Track database updates in order to invalidate DataSource when new data 

  AppDatabase.getInstance().invalidationTracker.addObserver(invalidationTracker)
    }

    override fun invalidate() {
        //Stop observing database updates as DataSource is invalid
        GlobalScope.launch {
           AppDatabase.getInstance().invalidationTracker.removeObserver(invalidationTracker)
        }
        super.invalidate()
    }


    override fun loadInitial(
        params: LoadInitialParams<Int>,
        callback: LoadInitialCallback<Int, EntryEntity>
    ) {
        //Get first page from DB and notify callback
        val data = entryDao.getPagedEntries(params.requestedLoadSize, 0)
        callback.onResult(data, null, 1)

        //Update data from network if needed
        refreshPageData(params.requestedLoadSize, 0)
    }

    override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, EntryEntity>) {            
        //Get new page from DB and notify callback
        callback.onResult(
            entryDao.getPagedEntries(
                params.requestedLoadSize,
                params.key * params.requestedLoadSize
            ),
            params.key + 1
        )
        //Update data from network if needed
        refreshPageData(params.requestedLoadSize, params.key)
    }

    override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, EntryEntity>) {
    }

    private fun refreshPageData(size: Int, page: Int) {
        //Do not request new data if we recently updated it
        if (lastSync[page]?.isBefore(Instant.now().minusMillis(10 * 1000L)) == false) return
        GlobalScope.launch {
            runCatching {
                service.getPagedEntries(page + 1, size)
            }.onSuccess {
                lastSync.put(page, Instant.now())
                entryDao.upsert(it.map { it.toEntity() })
            }.onFailure {
                it.log()
            }
        }
    }
}

对应的数据库功能:

    @Query("SELECT * FROM ${EntryEntity.TABLE_NAME} ORDER BY ${EntryEntity.COLUMN_DATE} DESC LIMIT :limit OFFSET :offset")
    abstract fun getPagedEntries(limit: Int, offset: Int): List<EntryEntity>

在ViewModel中,我正在使用以下代码生成此DataSource的实时数据:

LivePagedListBuilder(
            entriesFactory.map { it.toEntry() },
            Config(pageSize = 10, enablePlaceholders = false, initialLoadSizeHint = 10)
        )
            .setInitialLoadKey(0)
            .build()

我遇到的问题是,在滚动列表时(由于从网络中获取了新页面,并且当前的DataSource无效),用户返回了列表的开头,而不是最后一个可见的页面。因此,没有流畅的滚动体验。如果使用PositionalDataSource,则会发生类似的行为。区别在于,对于PositionalDataSource,列表视图将自动滚动到最后加载的页面。基本上,这意味着列表将自动滚动到列表的最后一个元素。

如何在保持每页从网络更新本地数据的可能性的同时获得流畅的滚动体验?

0 个答案:

没有答案