LiveData setValue 应该触发了 Activity 中的 onChanged 方法,但是它仅在第一次调用,在我尝试进行分页之后,它会中断并且不再调用 onChanged,尽管我的响应确实成功并且我在日志。setValue/postValue 有什么问题?这是一个错误吗?我应该自己实现观察者模式吗?那么使用 LiveData 有什么意义呢?我的寻呼不仅仅适用于这已经 2-3 天了.....
主活动类
public class MainActivity extends AppCompatActivity
private MutableLiveData<List<Photo>> mLivePhotos;
// some code...
@Override
protected void onCreate(Bundle savedInstanceState) {
mLivePhotos = loadData();
mLivePhotos.observe(this, photos -> {
Log.d(TAG, "onChanged!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
mProgressBar.setVisibility(View.GONE);
mPhotos = photos;
if (mIsInitialCall) {
initiateAdapter();
mIsInitialCall = false;
} else {
mAdapter.updateList(mPhotos.subList(mPageNumber, mPageNumber + 10));
}
});
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
int lastPosition =
mLayoutManager.findLastCompletelyVisibleItemPosition();
Log.d(TAG, "onScrolled - …Run Code Online (Sandbox Code Playgroud)我创建了一个示例项目来展示我在使用 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);
}
});
Run Code Online (Sandbox Code Playgroud)
为了测试,我用 1 000 个键/值对填充表。键从 1 开始,到 1 000 结束,所有值都以 INITIAL 开头。
我在每个显示的元素中包含一个切换按钮;单击它会将值从 INITIAL 切换到 FINAL 并反转。
在第 155 个元素上按下切换按钮时,显示值从 INITIAL 变为 FINAL 没有任何问题。
在第 243 个元素上执行相同操作时,按下按钮会导致 RecyclerView 向下滚动,这是不期望的。
每次在此位置周围按下按钮时,问题都会重复出现。
我拍摄了视频,以便可以观察到问题。
我一直在为这个问题苦苦挣扎,感到有点惭愧,因为这似乎是架构组件的基本用法,所以我很乐意得到任何帮助。
android android-recyclerview android-room android-architecture-components android-paging
我正在使用 Room 和 Paging 库来显示类别。
我的实体:
@Entity(tableName = Database.Table.CATEGORIES)
data class Category(
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = ID) var id: Long = 0,
@ColumnInfo(name = NAME) var name: String = "",
@ColumnInfo(name = ICON_ID) var iconId: Int = 0,
@ColumnInfo(name = COLOR) @ColorInt var color: Int = DEFAULT_COLOR
)
Run Code Online (Sandbox Code Playgroud)
我的道:
@Query("SELECT * FROM $CATEGORIES")
fun getPagedCategories(): DataSource.Factory<Int, Category>
@Update
fun update(category: Category)
Run Code Online (Sandbox Code Playgroud)
我的回购:
val pagedCategoriesList: LiveData<PagedList<Category>> = categoryDao.getPagedCategories().toLiveData(Config(CATEGORIES_LIST_PAGE_SIZE))
Run Code Online (Sandbox Code Playgroud)
我的视图模型:
val pagedCategoriesList: LiveData<PagedList<Category>>
get() = repository.pagedCategoriesList
Run Code Online (Sandbox Code Playgroud)
我的适配器:
class CategoriesAdapter(val context: Context) …Run Code Online (Sandbox Code Playgroud) android android-adapter kotlin android-recyclerview android-paging
我有多个数据源。但只有一个DataSourceFactory。因此,所有来源都共享一个工厂。我需要每个数据源一个 DataSourceFactory。
在我的应用程序中,我有多个 RecyclerViews 视图,因此有多个自定义数据源。那么,您最终是否会为每个数据源创建 DataSource.Factory 的多个实现,还是有更通用的解决方案?
已编辑
我在我的案例中找到了解决方案。在我之前的版本中,我仅对所有数据源使用一个数据源工厂。现在,每当从 ViewModel 调用方法时,我都会创建数据源工厂对象,例如
private void initData(String id) {
executor = Executors.newFixedThreadPool(5);
factory = new EvaluationDataSourceFactory(compositeDisposable, api, id);
networkState = Transformations.switchMap(factory.getDataSource(), source -> {
Timber.d("network status get");
return source.getNetworkState();
}
);
PagedList.Config pagedListConfig =
(new PagedList.Config.Builder())
.setEnablePlaceholders(false)
.setInitialLoadSizeHint(5)
.setPrefetchDistance(5)
.setPageSize(5).build();
error = Transformations.switchMap(factory.getDataSource(), source -> {
return source.getError();
});
assessments = (new LivePagedListBuilder(factory, pagedListConfig)).setFetchExecutor(executor).build();
}
public void getAssessments(String id) {
initData(id);
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以从您的活动或片段中调用此方法,例如
viewModel.getAssessments(id);
Run Code Online (Sandbox Code Playgroud)
这对我有用。
我正在使用分页库,当我直接DataSource.Factory<Int, Data>从 Dao 返回然后像这样设置分页列表时,一切都运行良好
pagedList = LivePagedListBuilder(dao().getAllData(), config).build()
当我单击列表项时,它会发生变化,更新房间数据库,并通过分页自动更新 UI。但我需要使用自定义数据源。所以我创建了一个,现在我像这样设置分页列表
pagedList = LivePagedListBuilder(customDatasourceFactory, config).build()。
问题是,现在当我更新房间表中的一行时,UI 不会自动更新。invalidate()当我的数据集更改时我也会使用。但在这种情况下,当我只想更新单个项目时,我无法使用它,因为我的位置数据源随后加载第一页,当我位于列表中间时,更改列表项,它会跳到顶部。
所以基本上我想要的是实现与我从 Dao 直接返回 Datasource Factory 时相同的行为,但现在使用我自己的数据源。
android android-room android-architecture-components android-jetpack android-paging
我正在使用带有 Room livedata 的分页库,在某些设备中,有时我会收到 IndexOutOfBoundsException 索引越界 - 传递位置 = 75,旧列表大小 = 75(总是大小为 75,我的数据库都没有固定大小为 75)
问题是日志显示的更像是内部错误而不是我的应用程序错误
致命异常:java.lang.IndexOutOfBoundsException:索引越界 - 传递位置 = 75,旧列表大小 = 75
在androidx.recyclerview.widget.DiffUtil $ DiffResult.convertOldPositionToNew(DiffUtil.java:672)
在androidx.paging.PagedStorageDiffHelper.transformAnchorIndex(PagedStorageDiffHelper.java:215)
在androidx.paging.AsyncPagedListDiffer.latchPagedList(AsyncPagedListDiffer.java:382)
在androidx.paging.AsyncPagedListDiffer$2$1.run(AsyncPagedListDiffer.java:345)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os .Looper.loop(Looper.java:164)
在 android.app.ActivityThread.main(ActivityThread.java:6543)
在 java.lang.reflect.Method.invoke(Method.java)
在 com.android.internal.os。 RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:440)
在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:810)
有解决方法吗?还是与我的应用程序有关?
我们正在使用PagedListEpoxyController, RxPagedListBuilder,ItemKeyedDataSource和一个运行时Mockgenerator,它只根据生成的数据索引创建一个用户,例如。用户 1、用户 100、用户 500等
val pageSize = 50
val initialLoadSizeHint = 80
val prefetchDistance = 16
Run Code Online (Sandbox Code Playgroud)
每当我们向下滚动到第400 个项目并返回到第300 个项目并通过单击“关注/取消关注”使列表无效时,UI 就会跳转。单击按钮时,PagedList 会像这样失效:
followers.dataSource.invalidate()
loadInitial()insideItemKeyedDataSource应该使用 的值调用,params.key以便我们可以重新显示之前显示的相同页面。例如,如果user300在屏幕中可见,则该项目requestedInitialKey应在[user221,user280] 范围内。
last item that was fetched in the pagedList失效前的分页库轨道but does not提供了一种方法来知道哪个 …
android android-recyclerview android-jetpack android-paging epoxy
我进行了搜索,但没有找到适合我的情况的任何好的解决方案,即:我需要过滤List<Article>通过分页库生成的现有内容,但无法弄清楚如何从FeedActivity的FeedDataSource类中的 api 请求中检索实际的articleList 。我需要在adapter.submitList(pagedList);之前过滤实际列表
FeedDataSurce 类
public class FeedDataSource extends PageKeyedDataSource<Long, Article>{
private static final String QUERY = "movies";
private static final String API_KEY = "079dac74a5f94ebdb990ecf61c8854b7";
private static final String TAG = FeedDataSource.class.getSimpleName();
private RestApi restApi;
private MutableLiveData networkState;
public FeedDataSource() {
restApi = RestApiFactory.feedRequest();
networkState = new MutableLiveData();
}
public MutableLiveData getNetworkState() {
return networkState;
}
@Override
public void loadInitial(@NonNull LoadInitialParams<Long> params,
@NonNull LoadInitialCallback<Long, Article> callback) {
networkState.postValue(NetworkState.LOADING);
restApi.fetchFeed(QUERY, API_KEY, 1, params.requestedLoadSize)
.enqueue(new …Run Code Online (Sandbox Code Playgroud) 我正在使用paging3并且有两个不同的分页源。问题是Coroutine Scope只发出第一个分页流
在ViewModel我有两个分页流程
val pagingFlow1 = Pager(PagingConfig(pageSize = 50, prefetchDistance = 1)) {
pagingSource
}.flow.cachedIn(viewModelScope)
val pagingFlow2 = Pager(PagingConfig(pageSize = 50, prefetchDistance = 1)) {
pagingSource2
}.flow.cachedIn(viewModelScope)
Run Code Online (Sandbox Code Playgroud)
在活动中收集它们
lifecycleScope.launch(Dispatchers.IO) {
viewModel.pagingFlow1.collectLatest { pagingData ->
pagingAdapter.submitData(pagingData)
}
viewModel.pagingFlow2.collectLatest { pagingData ->
pagingAdapter2.submitData(pagingData)
}
}
Run Code Online (Sandbox Code Playgroud)
但lifecycleScope只能发出,pagingFlow1换句话说,分页只能在第一个 recyclerView 中起作用。
当我更改订单时,这次仅适用于pagingFlow2
lifecycleScope.launch(Dispatchers.IO) {
viewModel.pagingFlow2.collectLatest { pagingData ->
pagingAdapter.submitData(pagingData)
}
viewModel.pagingFlow1.collectLatest { pagingData ->
pagingAdapter2.submitData(pagingData)
}
}
Run Code Online (Sandbox Code Playgroud)
为了确保我用基本流程对其进行了测试并正常工作
// Flows in ViewModel
val testFlow1 …Run Code Online (Sandbox Code Playgroud) 我正在使用 paging 3 在回收器视图中加载一些数据。我从服务器获取数据,然后将它们存储在本地数据库中。这是我的 DAO 界面:
@Dao
interface TicketDAO {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(model: TicketListModel)
@Query("SELECT * FROM ticket_list ORDER BY lastModifyDate DESC , lastModifyTime DESC")
fun getTickets(): PagingSource<Int, TicketListModel>
@Query("delete from ticket_list")
fun deleteAll()
}
Run Code Online (Sandbox Code Playgroud)
我假设这个接口作为我的数据源类。因为我也从服务器获取数据,所以我使用了这样的远程中介(顺便说一句,我使用 volley 从服务器获取数据):
@ExperimentalPagingApi
class TicketRemoteMediator(val ticketDatabase: TicketDAO, val context: Context) :
RemoteMediator<Int, TicketListModel>() {
private val scope = CoroutineScope(Dispatchers.Default)
private var page = 0
private var reachedEnd = false
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, TicketListModel>
): MediatorResult {
return …Run Code Online (Sandbox Code Playgroud) android ×10
android-paging ×10
kotlin ×4
android-room ×3
android-architecture-components ×2
java ×2
epoxy ×1
mvvm ×1