使用 Room 更新 PagingLibrary 中的列表项(仅限网络) [英] Update list items in PagingLibrary w/o using Room (Network only)

本文介绍了使用 Room 更新 PagingLibrary 中的列表项(仅限网络)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用分页库从使用ItemKeyedDataSource的网络加载数据.获取用户可以编辑的项目后,此更新在内存缓存中完成(不使用 Room 之类的数据库).

现在由于 PagedList 本身无法更新(讨论 here) 我必须重新创建 PagedList 并将其传递给 PagedListAdapter.

更新本身没问题,但是在用新的PagedList更新recyclerView后,列表跳转到列表的开头,破坏了previous 滚动位置.无论如何要在保持滚动位置的同时更新 PagedList(例如它如何与 Room 一起使用)?

DataSource 是这样实现的:

 public class MentionKeyedDataSource extends ItemKeyedDataSource{私有存储库;...私人名单<提及>缓存项;public MentionKeyedDataSource(Repository repository, ..., List cachedItems){极好的();this.repository = 存储库;this.teamId = teamId;this.inboxId = inboxId;this.filter = 过滤器;this.cachedItems = new ArrayList<>(cachedItems);}@覆盖public void loadInitial(@NonNull LoadInitialParams params, final @NonNull ItemKeyedDataSource.LoadInitialCallback callback) {Observable.just(cachedItems).filter(() -> return cachedItems != null && !cachedItems.isEmpty()).switchIfEmpty(repository.getItems(..., params.requestedLoadSize).map(...)).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(response -> callback.onResult(response.data.list));}@覆盖public void loadAfter(@NonNull LoadParams params, final @NonNull ItemKeyedDataSource.LoadCallback callback) {repository.getOlderItems(..., params.key, params.requestedLoadSize).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(response -> callback.onResult(response.data.list));}@覆盖public void loadBefore(@NonNull LoadParams params, final @NonNull ItemKeyedDataSource.LoadCallback callback) {repository.getNewerItems(..., params.key, params.requestedLoadSize).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(response -> callback.onResult(response.data.list));}@非空@覆盖public Long getKey(@NonNull 提及项) {返回 item.id;}}

PagedList 是这样创建的:

PagedList.Config config = new PagedList.Config.Builder().setPageSize(PAGE_SIZE).setInitialLoadSizeHint(preFetchedItems != null && !preFetchedItems.isEmpty()?preFetchedItems.size():PAGE_SIZE * 2).建造();pagedMentionsList = new PagedList.Builder<>(new MentionKeyedDataSource(mRepository, team.id, inbox.id, mCurrentFilter, preFetchedItems), 配置).setFetchExecutor(ApplicationThreadPool.getBackgroundThreadExecutor()).setNotifyExecutor(ApplicationThreadPool.getUIThreadExecutor()).建造();

PagedListAdapter 是这样创建的:

public class ItemAdapter extends PagedListAdapter{//来自谷歌指南的适配器,这里没什么特别的.. }mAdapter = new ItemAdapter(new DiffUtil.ItemCallback() {@覆盖公共布尔 areItemsTheSame(Item oldItem, Item newItem) {返回 oldItem.id == newItem.id;}@覆盖公共布尔 areContentsTheSame(Item oldItem, Item newItem) {返回 oldItem.equals(newItem);}});

,并更新如下:

mAdapter.submitList(pagedList);

P.S:如果有更好的方法可以在不使用 Room 的情况下更新列表项,请分享.

解决方案

您需要做的就是在每次更新数据时使当前的 DataSource 失效.

我会做什么:

  • 将网络逻辑从 MentionKeyedDataSource 移动到扩展 PagedList.BoundaryCallback 的新类,并在构建 PagedList 时设置它.p>

  • 将所有数据移动到某个 DataProvider 中,该DataProvider 包含所有下载的数据并引用 DataSource.每次在 DataProvider 中更新数据都会使当前的 DataSource

  • 失效

应该是这样的

 val dataProvider = PagedDataProvider()val dataSourceFactory = InMemoryDataSourceFactory(dataProvider = dataProvider)

哪里

class PagedDataProvider : DataProvider{private val dataCache = mutableMapOf>()覆盖 val sourceLiveData = MutableLiveData>()//在这里实现数据添加/删除/更新//并且在每次修改调用之后//sourceLiveData.value?.invalidate()}

class InMemoryDataSourceFactory(私有 val dataProvider: DataProvider) : DataSource.Factory() {override fun create(): DataSource{val source = InMemoryDataSource(dataProvider = dataProvider)dataProvider.sourceLiveData.postValue(source)返回源}}

这种方法与 Room 所做的非常相似 - 每次更新表行 - 当前 DataSource 无效并且 DataSourceFactory 创建新数据来源.

I'm using Paging Library to load data from network using ItemKeyedDataSource. After fetching items user can edit them, this updates are done inside in Memory cache (no database like Room is used).

Now since the PagedList itself cannot be updated (discussed here) I have to recreate PagedList and pass it to the PagedListAdapter.

The update itself is no problem but after updating the recyclerView with the new PagedList, the list jumps to the beginning of the list destroying previous scroll position. Is there anyway to update PagedList while keeping scroll position (like how it works with Room)?

DataSource is implemented this way:

public class MentionKeyedDataSource extends ItemKeyedDataSource<Long, Mention> {

    private Repository repository;
    ...
    private List<Mention> cachedItems;

    public MentionKeyedDataSource(Repository repository, ..., List<Mention> cachedItems){
        super();

        this.repository = repository;
        this.teamId = teamId;
        this.inboxId = inboxId;
        this.filter = filter;
        this.cachedItems = new ArrayList<>(cachedItems);
    }

    @Override
    public void loadInitial(@NonNull LoadInitialParams<Long> params, final @NonNull ItemKeyedDataSource.LoadInitialCallback<Mention> callback) {
        Observable.just(cachedItems)
                .filter(() -> return cachedItems != null && !cachedItems.isEmpty())
                .switchIfEmpty(repository.getItems(..., params.requestedLoadSize).map(...))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(response -> callback.onResult(response.data.list));
    }

    @Override
    public void loadAfter(@NonNull LoadParams<Long> params, final @NonNull ItemKeyedDataSource.LoadCallback<Mention> callback) {
        repository.getOlderItems(..., params.key, params.requestedLoadSize)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(response -> callback.onResult(response.data.list));
    }

    @Override
    public void loadBefore(@NonNull LoadParams<Long> params, final @NonNull ItemKeyedDataSource.LoadCallback<Mention> callback) {
        repository.getNewerItems(..., params.key, params.requestedLoadSize)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(response -> callback.onResult(response.data.list));
    }

    @NonNull
    @Override
    public Long getKey(@NonNull Mention item) {
        return item.id;
    }
}

The PagedList created like this:

PagedList.Config config = new PagedList.Config.Builder()
        .setPageSize(PAGE_SIZE)
        .setInitialLoadSizeHint(preFetchedItems != null && !preFetchedItems.isEmpty()
                ? preFetchedItems.size()
                : PAGE_SIZE * 2
        ).build();

pagedMentionsList = new PagedList.Builder<>(new MentionKeyedDataSource(mRepository, team.id, inbox.id, mCurrentFilter, preFetchedItems)
        , config)
        .setFetchExecutor(ApplicationThreadPool.getBackgroundThreadExecutor())
        .setNotifyExecutor(ApplicationThreadPool.getUIThreadExecutor())
        .build();

The PagedListAdapter is created like this:

public class ItemAdapter extends PagedListAdapter<Item, ItemAdapter.ItemHolder> { //Adapter from google guide, Nothing special here.. }

mAdapter = new ItemAdapter(new DiffUtil.ItemCallback<Mention>() {
            @Override
            public boolean areItemsTheSame(Item oldItem, Item newItem) {
                return oldItem.id == newItem.id;
            }

            @Override
            public boolean areContentsTheSame(Item oldItem, Item newItem) {
                return oldItem.equals(newItem);
            }
        });

, and updated like this:

mAdapter.submitList(pagedList);

P.S: If there is a better way to update list items without using Room please share.

解决方案

All you need to do is to invalidate current DataSource each time you update your data.

What I would do:

  • move networking logic from MentionKeyedDataSource to new class that extends PagedList.BoundaryCallback and set it when building your PagedList.

  • move all you data to some DataProvider that holds all your downloaded data and has reference to DataSource. Each time data is updated in DataProvider invalidate current DataSource

Something like that maybe

    val dataProvider = PagedDataProvider()
    val dataSourceFactory = InMemoryDataSourceFactory(dataProvider = dataProvider)

where

class PagedDataProvider : DataProvider<Int, DataRow> {

    private val dataCache = mutableMapOf<Int, List<DataRow>>()
    override val sourceLiveData = MutableLiveData<DataSource<Int, DataRow>>()

    //implement data add/remove/update here
    //and after each modification call
    //sourceLiveData.value?.invalidate()
}

and

class InMemoryDataSourceFactory<Key, Value>(
    private val dataProvider: DataProvider<Key, Value>
) : DataSource.Factory<Key, Value>() {

    override fun create(): DataSource<Key, Value> {
        val source = InMemoryDataSource(dataProvider = dataProvider)
        dataProvider.sourceLiveData.postValue(source)
        return source
    }
}

This approach is very similar to what Room does - every time table row is updated - current DataSource is invalidated and DataSourceFactory creates new data source.

这篇关于使用 Room 更新 PagingLibrary 中的列表项(仅限网络)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆