标签: android-paging

如何为测试创建对象的PagedList?

我一直在与谷歌的拱库一起工作,但是让测试变得困难的一件事就是合作PagedList.

对于此示例,我使用存储库模式并从API或网络返回详细信息.

所以在ViewModel中我调用了这个接口方法:

override fun getFoos(): Observable<PagedList<Foo>>
Run Code Online (Sandbox Code Playgroud)

然后,存储库将RxPagedListBuilder用于创建Observable类型为PagedList的类型:

 override fun getFoos(): Observable<PagedList<Foo>> =
            RxPagedListBuilder(database.fooDao().selectAll(), PAGED_LIST_CONFIG).buildObservable()
Run Code Online (Sandbox Code Playgroud)

我希望能够通过这些返回a的方法设置返回测试PagedList<Foo>.类似的东西

when(repository.getFoos()).thenReturn(Observable.just(TEST_PAGED_LIST_OF_FOOS)
Run Code Online (Sandbox Code Playgroud)

两个问题:

  1. 这可能吗?
  2. 我该如何创建PagedList<Foo>

我的目标是以更加端到端的方式进行验证(例如确保在屏幕上显示正确的Foos列表).片段/活动/视图是PagedList<Foo>从ViewModel 观察的片段/活动/视图.

android android-architecture-components android-paging

14
推荐指数
3
解决办法
2591
查看次数

修改Android Paging Architecture库中的PagedList

我目前正在考虑将Paging Architecture库(2.1.0-beta01编写本文时的版本)整合到我的应用程序中.一个组件是允许用户从中删除单个项目的列表.此列表仅限网络,并且使用Room缓存localy没有意义.

PagedList是不可变的,不支持修改.我已经读过有一个列表的副本,该列表被修改并返回,因为新的列表是要走的路.文档说明了相同的内容:

如果您有更精细的更新信号,例如网络API发信号通知对列表中单个项目的更新,则建议将数据从网络加载到内存中.然后通过包装内存中快照的DataSource将该数据呈现给PagedList.每次内存中副本更改时,都会使先前的DataSource无效,并且可以创建包装快照新状态的新数据.

我目前有基本推荐的实现来显示一个简单的列表.我DataSource看起来像这样:

class MyDataSource<SomeItem> : PageKeyedDataSource<Int, SomeItem>() {

    override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, SomeItem>) {
        // Simple load from API and notification of `callback`.
    }

    override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, SomeItem>) {
        // Simple load from API and notification of `callback`.
    }

    override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, SomeItem>) {
        // Simple load from API and notification of `callback`.
    }
}
Run Code Online (Sandbox Code Playgroud)

如何在文档中引用的内存缓存(没有Room并且没有使整个数据集无效)的具体实现如何?

android kotlin android-architecture-components android-paging

13
推荐指数
2
解决办法
3075
查看次数

如何在插入到PagedListAdapter中的项目后正确滚动

我正在使用Android Arch PagedListAdapter类.我遇到的问题是,由于我的应用程序是聊天风格的应用程序,我需要在插入项目时滚动到位置0.但是PagedListAdapter在后台线程上找到差异并调用必要的方法,所以似乎没有可靠的方法来调用layoutManager.scrollToPosition(0)

如果有人对我如何在合适的时间滚动有任何想法,我将不胜感激.谢谢!

这是我的房间道:

@Dao
abstract class MessagesDao {

    @Query("SELECT ...")
    abstract fun getConversation(threadId: String): DataSource.Factory<Int, Conversation>

}
Run Code Online (Sandbox Code Playgroud)

然后是LivePagedListBuilder:

LivePagedListBuilder(database.messagesDao.getConversation(threadId), PagedList.Config.Builder()
            .setInitialLoadSizeHint(20)
            .setPageSize(12)
            .setEnablePlaceholders(true)
            .build()).build()
Run Code Online (Sandbox Code Playgroud)

android-paging

12
推荐指数
1
解决办法
2636
查看次数

Android Paging Library如何知道加载更多数据?

我是开发Android应用程序的新手,我正在尝试以"正确的方式"做所有事情.所以现在,我正在将新的Android Paging Library实现到我的项目中,我需要从网络服务器加载文章列表.

我有一个ArticlesRepository类,它返回一个ArticleList包含ArticleListItem我想在RecyclerView中显示的实例的类.文章列表已经分页的服务器上,这样仓库发送对第一页的请求,并返回一个ArticleListpage属性设置为1articles包含属性List<ArticleListItem>的请求页面上的文章.我不知道一页上有多少篇文章.

现在,我能够实现一个PageKeyedDataSource<Integer, ArticleListItem>,但它只获取第一页:

@Override
public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, ArticleListItem> callback) {
    ArticleList list = load(1);
    if (list != null) {
        callback.onResult(list.articles, null, next(list));
    }
}

@Override
public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, ArticleListItem> callback) {
    ArticleList list = load(previous(params.key));
    if (list != null) {
        callback.onResult(list.articles, previous(list));
    }
}

@Override
public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull …
Run Code Online (Sandbox Code Playgroud)

java android android-recyclerview android-viewmodel android-paging

12
推荐指数
1
解决办法
3117
查看次数

恢复活动时恢复PagedListAdapter位置

我一直在试验,PagedListAdapter无法弄清楚如何正确恢复适配器位置.

最后一次尝试是lastKey从当前列表中保存.

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)

    val lastKey = adapter.currentList?.lastKey as Int

    outState.putInt("lastKey", lastKey)
}
Run Code Online (Sandbox Code Playgroud)

但是当恢复我的适配器并传递lastKeyPagedListBuilder我上次看到的内容以及显示的内容时,相当不同.

    val dataSourceFactory = dao.reportsDataSourceFactory()
    val builder = RxPagedListBuilder(
        dataSourceFactory,
        PagedList.Config.Builder()
            .setEnablePlaceholders(false)
            .setInitialLoadSizeHint(60)
            .setPageSize(20)
            .setPrefetchDistance(60)
            .build()
    )
        .setInitialLoadKey(initialLoadKey)
        .setBoundaryCallback(boundaryCallback)
Run Code Online (Sandbox Code Playgroud)

如果我在恢复时位于第4页中间位置 - 适配器将位于第4页开头的位置.理想情况下,适配器应该恢复到与上次看到的完全相同的位置.

各种尝试保存LayoutManager状态

outState.putParcelable("layout_manager_state", recycler_view.layoutManager.onSaveInstanceState()) 
Run Code Online (Sandbox Code Playgroud)

然后恢复它

recycler_view.layoutManager.onRestoreInstanceState(it.getParcelable("layout_manager_state"))
Run Code Online (Sandbox Code Playgroud)

悲惨地失败了.欢迎任何建议:)

android android-paging

12
推荐指数
1
解决办法
1218
查看次数

PagedListAdapter.submitList()在更新现有项目时表现得很奇怪

这个主题的小故事:应用程序只是在确认后用对话框更新点击行的值.在房间数据库上使用分页方案.

添加或删除项目时,将获取最新数据集并将其传递给submitList方法,然后可以看到所有更改并运行良好.

问题从那里开始,如果现有项目更新,则再次正确获取最新数据集并传递给submitList,但这次似乎没有变化.

当我调试DIFF_CALLBACK并抓住我的项目时areItemsTheSame,newHistoryoldHistory值是一样的!(怎么样!)

submitList方法中可能有任何错误?

  • 房间v.:2.1.0-alpha02
  • Paging v.:2.1.0-beta01

初始化后,observe从房间中提取列表并传递给mHistoryAdapter.submitList(it).然后,如果我更新一个项目,观察再次被触发(我在param中看到更新的值it)并传递给submitList.

不幸的是,适配器不会改变......

    mResolvedAddressViewModel = ViewModelProviders.of(this).get(ResolvedAddressViewModel::class.java)
    mResolvedAddressViewModel.getAddresses(false).observe(this, Observer {
        mHistoryAdapter.submitList(it)
    })
Run Code Online (Sandbox Code Playgroud)

所有部件

模型

@Parcelize
@Entity
data class ResolvedAddress(
    @PrimaryKey var id: String = UUID.randomUUID().toString(),
    var requestedLat: Double = 0.0,
    var requestedLon: Double = 0.0,
    var requestedAddress: String = "",
    var lat: Double,
    var lon: Double,
    var address: String,
    var country: String,
    var countryCode: String,
    var city: …
Run Code Online (Sandbox Code Playgroud)

android android-architecture-components android-paging androidx

12
推荐指数
1
解决办法
2252
查看次数

使用自定义DataSource的分页库不会更新Room更新中的行

我一直在使用RecyclerView实现新的Paging Library,其中应用程序构建在Architecture Components之上.

填充列表的数据来自Room数据库.实际上,它是从网络中获取的,存储在本地数据库中并提供给列表.

为了提供构建列表所需的数据,我实现了自己的自定义PageKeyedDataSource.一切都按预期工作,除了一个小细节.显示列表后,如果列表行元素的数据发生任何更改,则不会自动更新.因此,例如,如果我的列表显示了具有字段名称的项目列表,并且突然,该字段在本地Room数据库中针对特定行项目更新,则列表不会自动更新行UI.

仅当使用自定义DataSource时才会发生此行为,这与通过直接返回DataSource FactoryDAO自动获取DataSource不同.但是,我需要实现自定义DataSource.

我知道可以通过调用DataSource上的invalidate()方法来重新更新列表来更新它.但是,如果应用程序一次显示2个列表(例如每个半屏),并且此项目出现在两个列表中,则需要分别为两个列表调用invalidate().

我想过一个解决方案,其中,不是使用item的类的实例来填充每个ViewHolder,而是使用它的LiveData包装版本,使每行观察其自己项目的更改并在必要时更新该行UI .不过,我看到这种方法有一些缺点:

  1. 必须将LifeCycleOwner(例如包含RecyclerView的Fragment)传递给PagedListAdapter,然后将其转发到ViewHolder以观察LiveData包装的项目.
  2. 将为每个列表的新行注册一个新的观察者,所以我根本不知道它是否有过多的计算和内存成本,考虑到它将在应用程序中的每个列表中完成,其中包含很多列表.
  3. 例如,观察LiveData包装项的LifeCycleOwner将是包含RecyclerView的Fragment,而不是ViewHolder本身,每当对该项发生更改时,即使包含该项的行不均匀,也会通知观察者.在那一刻可见,因为列表已滚动,这在我看来像浪费资源,可能会不必要地增加计算成本.

我根本不知道,即使考虑到这些缺点,它看起来似乎是一种体面的方法,或者,如果你们中的任何人知道任何其他更清洁和更好的方法来管理它.

先感谢您.

android list android-livedata android-architecture-components android-paging

11
推荐指数
1
解决办法
886
查看次数

如何从Android分页组件中的PagedListAdapter中删除项目

我使用Paging with Retrofit从REST API加载Notifications数据列表.

当我按下通知项目的删除按钮时,我调用删除API删除它.PagedListAdapter删除API响应成功后删除它的正确方法是什么?PagedListPagedListAdapter不支持通过索引或对象删除项目.

我试着打电话DataSource.validate()但它从开始重新加载,而不是从当前页面重新加载.

android android-architecture-components android-paging

11
推荐指数
1
解决办法
2597
查看次数

Paging 3 - PagingDataAdapter 完成刷新并且 DiffUtil 完成比较后如何滚动到 RecyclerView 顶部?

我将 Paging 3 与 RemoteMediator 结合使用,它在从网络获取新数据的同时显示缓存的数据。当我刷新我的PagingDataAdapter(通过调用refresh()它)时,我希望我的 RecyclerView 在刷新完成后滚动到顶部。在代码实验室loadStateFlow中,他们尝试通过以下方式处理这个问题:

lifecycleScope.launch {
    adapter.loadStateFlow
            // Only emit when REFRESH LoadState for RemoteMediator changes.
            .distinctUntilChangedBy { it.refresh }
            // Only react to cases where Remote REFRESH completes i.e., NotLoading.
            .filter { it.refresh is LoadState.NotLoading }
            .collect { binding.list.scrollToPosition(0) }
    }
Run Code Online (Sandbox Code Playgroud)

这确实会向上滚动,但在 DiffUtil 完成之前。这意味着如果顶部确实插入了新数据,RecyclerView将不会一直向上滚动。

我知道 RecyclerView 适配器有一个AdapterDataObserver回调,当 DiffUtil 完成比较时我们可以收到通知。但这会导致适配器的各种竞争条件和PREPEND加载APPEND状态,这也会导致 DiffUtil 运行(但这里我们不想滚动到顶部)。

一种可行的解决方案是传递PagingData.empty()PagingDataAdapter并重新运行相同的查询(仅调用refresh是行不通的,因为PagingData现在是空的并且没有任何内容可以刷新),但我更愿意保持旧数据可见,直到我知道刷新实际上成功了。

paging android android-paging android-paging-library android-paging-3

11
推荐指数
1
解决办法
8549
查看次数

如何禁用更新项目时发生的 RecyclerView (ListAdapter) 自动滚动?

背景

我有一个用户界面,显示用户全名列表,每个项目都有一个喜欢/不喜欢按钮。我正在使用一个ListAdapter底层用途DiffUtilAsyncListDifferAPI。用户列表作为 LiveData 从 Room 数据库接收,并按"isLiked" 排序

问题

每当点击“like”按钮时,Room(因为我正在使用 LiveData)会将新数据重新提交到适配器。问题是,由于列表按“isLiked”排序,喜欢的用户将更改其位置,并且 RecyclerView 将始终滚动到新位置。

我不想看到更新项目的新位置。那么,如何禁用自动滚动行为?

我尝试过的

MainActivity.kt

    ..
    val userAdapter = UsersAdapter(this)
    val ll = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
   
    recyclerView.apply {
        layoutManager = ll
        adapter = userAdapter
        itemAnimator = null
        setHasFixedSize(true)
    }
    
    viewModel.users.observe(this, {
        // This will force the recycler view to scroll back to the previous position
        // But it's more of a workaround than a clean solution.
        val pos = ll.findFirstVisibleItemPosition()
        userAdapter.submitList(it) { …
Run Code Online (Sandbox Code Playgroud)

android android-recyclerview android-paging android-diffutils android-listadapter

11
推荐指数
0
解决办法
1461
查看次数