我创建了一个示例项目,以显示我在使用RecyclerView,Room和Paging时遇到的问题,即 RecyclerView意外地向下滚动数据更新。
https://github.com/HappyPeng2x/RoomRecyclerViewExample
该应用程序有一个Room数据库,我正在使用从PagedListAdapter派生的适配器在RecyclerView中显示其值。
将观察到查询,如下面的代码所示,以便适配器的每次更新都会反映该表。
PagedList.Config plConfig =
new PagedList.Config.Builder().setEnablePlaceholders(false)
.setPrefetchDistance(10)
.setPageSize(20).build();
new LivePagedListBuilder<>
(mDB.getMyDao().getAllPaged(), plConfig)
.build()
.observe(this, new Observer<PagedList<MyEntry>>() {
@Override
public void onChanged(PagedList<MyEntry> myList) {
adapter.submitList(myList);
}
});
为了进行测试,我在表中填充了1000个键/值对。键从1开始到1 000结束,并且所有值都以INITIAL初始化。
在每个显示的元素中都包含一个切换按钮;单击它会将值从INITIAL切换为FINAL,然后反转。
在第155个元素上按下切换按钮时,显示的值从INITIAL更改为FINAL,没有任何问题。
在243rd元素上执行相同操作时,按下按钮会使RecyclerView向下滚动,这是不期望的。
每次在此位置上按下一个按钮时,都会重复出现此问题。
我拍摄了视频,以便可以观察到该问题。
我一直在这个问题上苦苦挣扎,并且感到有点as愧,因为这似乎是对Architecture Components的基本使用,因此我非常乐意获得任何帮助。
答案 0 :(得分:2)
此问题是一个错误,已由Google解决,并且该修复程序应包含在下一版Paging中,但遗憾的是尚未发生。
问题跟踪器中的URL是https://issuetracker.google.com/issues/123834703-但是,由于必须记录下来才能看到它,因此我将在此处复制主要元素。
正如开发人员所解释的那样,问题在于该库通常会在最后访问的位置 附近触发初始加载,但是如果禁用了占位符,则会绕过此逻辑,从而导致加载在最后访问的位置。由于此位置通常是RecyclerView绑定的最后部分脱离屏幕的项目,因此这意味着屏幕上的大多数项目在初始加载时就丢失了,只能在以后分页。
尚未发布包含此修复程序的发行版,您必须使用开发版本,但是您也可以在https://github.com/HappyPeng2x/RoomRecyclerViewExample上添加开发人员为我的示例应用建议的解决方法。
1)将以下类添加到MainActivity.java:
static class RoomFactoryWrapper<T> extends DataSource.Factory<Integer, T> {
final DataSource.Factory<Integer, T> m_wrappedFactory;
RoomFactoryWrapper(@NonNull Factory<Integer, T> wrappedFactory) {
m_wrappedFactory = wrappedFactory;
}
@NonNull
@Override
public DataSource<Integer, T> create() {
return new DataSourceWrapper<>((PositionalDataSource<T>) m_wrappedFactory.create());
}
static class DataSourceWrapper<T> extends PositionalDataSource<T> {
final PositionalDataSource<T> m_wrappedSource;
DataSourceWrapper(PositionalDataSource<T> wrappedSource) {
m_wrappedSource = wrappedSource;
}
@Override
public void addInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) {
m_wrappedSource.addInvalidatedCallback(onInvalidatedCallback);
}
@Override
public void removeInvalidatedCallback(
@NonNull InvalidatedCallback onInvalidatedCallback) {
m_wrappedSource.removeInvalidatedCallback(onInvalidatedCallback);
}
@Override
public void invalidate() {
m_wrappedSource.invalidate();
}
@Override
public boolean isInvalid() {
return m_wrappedSource.isInvalid();
}
@Override
public void loadInitial(@NonNull LoadInitialParams params,
@NonNull LoadInitialCallback<T> callback) {
// Workaround for paging bug: https://issuetracker.google.com/issues/123834703
// edit initial load position to start 1/2 load ahead of requested position
int newStartPos = params.placeholdersEnabled
? params.requestedStartPosition
: Math.max(0, params.requestedStartPosition - (params.requestedLoadSize / 2));
m_wrappedSource.loadInitial(new LoadInitialParams(
newStartPos,
params.requestedLoadSize,
params.pageSize,
params.placeholdersEnabled
), callback);
}
@Override
public void loadRange(@NonNull LoadRangeParams params,
@NonNull LoadRangeCallback<T> callback) {
m_wrappedSource.loadRange(params, callback);
}
}
}
2)在dataSourceFactory上使用包装器:
new LivePagedListBuilder<>
(new RoomFactoryWrapper<>(mDB.getMyDao().getAllPaged()), plConfig)
答案 1 :(得分:0)
尽管我不确定,但这可能会对您有所帮助。
"principalId": "$authenticated"
答案 2 :(得分:0)
我设置了此配置,效果很好。
那里的主要物品是setMaxSize(pageSize + 2 * prefetchDistance)
尽可能降低价值。
val config = PagedList.Config.Builder()
.setEnablePlaceholders(true)
.setMaxSize(pageSize + 2 * prefetchDistance)
.setPrefetchDistance(prefetchDistance)
.setPageSize(pageSize)
.build()`
我尝试不使用它,RecyclerView的外观也不错,但PagedListAdapter的元素绑定错误。