大家好,我使用 Android Jetpack Paging 库 3,我正在创建一个实现网络 + 数据库场景的新闻应用程序,我正在关注 google 的 Codelab https://codelabs.developers.google.com/codelabs/android-paging,我几乎就像在 Codelab 中一样,我几乎匹配了示例中显示的所有操作https://github.com/android/architecture-components-samples/tree/main/PagingWithNetworkSample。
它几乎按其应有的方式工作......但我的后端响应是页面键控的,我的意思是响应带有新闻列表和下一页网址,远程中介获取数据,填充数据库,设置存储库,设置视图模型。 ..
问题是:当recyclerview加载数据时,会发生以下情况:recyclerview闪烁、项目跳转、被删除、再次添加等等。我不知道为什么 recyclerview 或其 itemanimator 的行为如此,看起来如此丑陋和故障。更重要的是,当我滚动到列表末尾时,会获取新项目,并且再次发生故障和跳跃效果。
如果您能帮助我,我将非常感激,我已经坐了三天了,提前非常感谢您。这是我的代码片段:
@Entity(tableName = "blogs")
data class Blog(
@PrimaryKey(autoGenerate = true)
val databaseid:Int,
@field:SerializedName("id")
val id: Int,
@field:SerializedName("title")
val title: String,
@field:SerializedName("image")
val image: String,
@field:SerializedName("date")
val date: String,
@field:SerializedName("share_link")
val shareLink: String,
@field:SerializedName("status")
val status: Int,
@field:SerializedName("url")
val url: String
) {
var categoryId: Int? = null
var tagId: Int? = null
}
Run Code Online (Sandbox Code Playgroud)
这是 DAO
@Insert(onConflict …Run Code Online (Sandbox Code Playgroud) android android-architecture-components android-paging android-paging-library android-paging-3
我从 Paging 2 迁移到 Paging 3 库,现在我使用它PagingSource从服务器加载数据页面。但我很难理解getRefreshKey必须在那里重写的方法。我找到了一些代码示例,如何根据用于获取后续页面的密钥来实现它,但我仍然不明白。基于这些示例,我编写了以下代码:
override fun getRefreshKey(state: PagingState<Pair<String, String>, User>): Pair<String, String>? {
return state.anchorPosition?.let { anchorPosition ->
state.closestPageToPosition(anchorPosition)?.prevKey
}
}
Run Code Online (Sandbox Code Playgroud)
但如果我使它始终返回 null,它不会改变任何事情:
override fun getRefreshKey(state: PagingState<Pair<String, String>, User>): Pair<String, String>? = null
Run Code Online (Sandbox Code Playgroud)
那么我为什么不能选择最简单的解决方案呢?还是有我没有看到的用例?
我正在尝试使用 LazyVerticalGrid 显示分页项目。我正在尝试的代码如下所示。
val categories = repo.categories.collectAsLazyPagingItems()
LazyVerticalGrid(
cells = GridCells.Fixed(2),
modifier = Modifier.padding(8.dp)
) {
items(categories) { category ->
Box(Modifier.padding(8.dp)) {
CategoryView(category)
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,我已经导入了androidx.paging.compose.items和androidx.paging.compose.collectAsLazyPagingItems。也categories属于类型LazyPagingItems<Category>。
LazyColumn它可以与和完美配合LazyRow,但不能LazyVerticalGrid。我收到的错误是:
Type mismatch.
Required:
Int
Found:
LazyPagingItems<Category>
Run Code Online (Sandbox Code Playgroud) 我正在尝试使用状态流和分页 3 来实现 android MVI 架构,但是当我有一个包含分页数据的视图状态时,我感到很困惑。
\n问题是我将视图模型中的视图状态公开为状态流对象,但现在在该视图状态内我有另一个来自分页库的流对象。
\n状态流中可以有一个流吗?如果它\xe2\x80\x99s 不是我应该做什么?
\n这是我的代码,以供更多说明。
\n任务库
\noverride fun list(\npageNumber: Int,\npageSize: Int,\ngroupId: Long?,\nquery: String\n): Flow<PagingData<Task>> {\nreturn Pager(\n config = PagingConfig(\n pageSize = Consts.PageSize,\n maxSize = 200,\n enablePlaceholders = false\n ),\n remoteMediator = TaskRemoteMediator(query, groupId, db, taskApi),\n pagingSourceFactory = {\n TaskDataSource(taskApi, groupId, query)\n }\n).flow\n}\nRun Code Online (Sandbox Code Playgroud)\n任务视图模型
\nviewModelScope.launch {\ntry {\n _taskListViewState.emit(TaskListViewState.Loading)\n val tasks = taskRepo.list(1, Consts.PageSize, intent.groupId, "")\n _taskListViewState.emit(TaskListViewState.Data(tasks))\n} catch (e: Exception) {\n _taskListViewState.emit(TaskListViewState.Error(R.string.unknown_error))\n}\n}\nRun Code Online (Sandbox Code Playgroud)\n任务列表视图状态
\nsealed class TaskListViewState {\nobject Idle …Run Code Online (Sandbox Code Playgroud) 我将 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
homeViewModel.pagingDataFlow.subscribe(expensesPagingData -> {
expenseAdapter.submitData(getLifecycle(), expensesPagingData);
}, throwable -> Log.e(TAG, "onCreate: " + throwable.getMessage()));
// ViewModel
private void init() {
pagingDataFlow = homeRepository.init();
CoroutineScope coroutineScope = ViewModelKt.getViewModelScope(this);
PagingRx.cachedIn(pagingDataFlow, coroutineScope);
}
// Repository
public Flowable<PagingData<ExpensesModel>> init() {
// Define Paging Source
expensePagingSource = new ExpensePagingSource(feDataService);
// Create new Pager
Pager<Integer, ExpensesModel> pager = new Pager<Integer, ExpensesModel>(
new PagingConfig(10,
10,
false,
10,
100),
() -> expensePagingSource); // set paging source
// inti Flowable
pagingDataFlow = PagingRx.getFlowable(pager);
return pagingDataFlow;
}
I have tried to …Run Code Online (Sandbox Code Playgroud) android android-recyclerview android-paging-library android-paging-3
我正在尝试实现我正在使用 Room 的分页,我花了很长时间才意识到这一切都为我完成了,但我需要做的是能够过滤搜索并对我的数据进行排序。我想暂时将其保留为 LiveData,以便稍后交换为 Flow。\n我使用此方法来过滤搜索和排序,并且效果非常好,
\n @SuppressLint("DefaultLocale")\n private fun searchAndFilterPokemon(): LiveData<List<PokemonWithTypesAndSpecies>> {\n return Transformations.switchMap(search) { search ->\n val allPokemon = repository.searchPokemonWithTypesAndSpecies(search)\n Transformations.switchMap(filters) { filters ->\n val pokemon = when {\n filters.isNullOrEmpty() -> allPokemon\n else -> {\n Transformations.switchMap(allPokemon) { pokemonList ->\n val filteredList = pokemonList.filter { pokemon ->\n pokemon.matches = 0\n val filter = filterTypes(pokemon, filters)\n filter\n }\n maybeSortList(filters, filteredList)\n }\n }\n }\n pokemon\n }\n }\n }\nRun Code Online (Sandbox Code Playgroud)\n这里有一些切换映射,第一个是响应搜索更新
\n var search: MutableLiveData<String> = getSearchState()\nRun Code Online (Sandbox Code Playgroud)\n第二个是响应过滤器更新
\n val filters: MutableLiveData<MutableSet<String>> …Run Code Online (Sandbox Code Playgroud) 我有一个显示搜索项目列表的搜索片段。如果用户输入某些内容,我会将该字符串作为新的查询参数传递给 url,并使用 paging 3 库获取新列表。
第一个解决方案是:
//viewModel
lateinit var postListUrl: String
val postList: Flow<PagingData<Post>> = Pager(PagingConfig(pageSize = 20)) {
PostPagingSource(postRepository, postListUrl)
}.flow.cachedIn(viewModelScope)
//fragment
fun showPostList(url: String) {
postListAdapter.submitData(lifecycle, PagingData.empty())
viewModel.postListUrl = url
viewLifecycleOwner.lifecycleScope.launch {
viewModel.postList.collectLatest {
postListAdapter.submitData(it)
}
}
}
Run Code Online (Sandbox Code Playgroud)
通过此解决方案,通过更改 url ( showPostList(newUrl),列表保持不变。也许在 viewModel 中使用缓存列表。
另一种解决方案是:使用showPostList(initUrl)in onViewCreatedof 片段,然后通过更改参数使用 blow 方法:
//fragment
fun changePostList(url: String) {
viewModel.postListUrl = url
postListAdapter.refresh()
}
Run Code Online (Sandbox Code Playgroud)
这项工作有效,但如果旧列表和新列表有共同的项目,新列表将显示在最后一个共同的可见项目上。例如,如果旧列表的第 5 个位置项与新列表的第 7 个位置项相同,则在列表更改以显示新列表后,它将从第 7 个位置开始,而不是第一个项目。
我在这里找到了另一个解决方案:
//viewModel
val postListUrlFlow = MutableStateFlow("") …Run Code Online (Sandbox Code Playgroud) 我正在关注 Paging 3 的 Codelab。
分页工作正常,但尝试添加页脚似乎不起作用。
LoadStateAdapter我的代码在使用方面与 Codelab 完全一样
class ListLoadStateAdapter(
private val retry: () -> Unit,
) : LoadStateAdapter<ListLoadStateAdapter.ListLoadStateViewHolder>() {
override fun onBindViewHolder(holder: ListLoadStateViewHolder, loadState: LoadState) {
holder.bind(loadState)
}
override fun onCreateViewHolder(
parent: ViewGroup,
loadState: LoadState,
) = ListLoadStateViewHolder.create(parent, retry)
class ListLoadStateViewHolder(
private val binding: ComponentPagedListFooterBinding,
retry: () -> Unit,
) : RecyclerView.ViewHolder(binding.root) {
init {
binding.buttonRetry.setOnClickListener { retry.invoke() }
}
fun bind(loadState: LoadState) {
if (loadState is LoadState.Error) {
binding.textViewPlaceholderError.text = loadState.error.localizedMessage
}
binding.progressBar.isVisible = loadState is …Run Code Online (Sandbox Code Playgroud) 我有一个lazyColumn要使用paging3,\n我来自网络的数据,需要使用列表计数来要求服务器获取下一个数据\n但现在我需要删除一些特殊项目,如何删除它\xef\xbc\x8ci已经使用PagingSource.invalidate 引用我的lazyColumn
\nandroid-paging-3 ×10
android ×9
kotlin ×2
android-architecture-components ×1
kotlin-flow ×1
paging ×1
sorting ×1