安卓喷气背包组合分页:分页不适用于交错布局的喷气背包组合



android提供的分页(https://developer.android.com/topic/libraries/architecture/paging/v3-overview)使用Column、Row、lazy Column和lazy rows可以很好地工作。当我试图在交错布局中实现分页时,就会出现问题(回答如何使用Jetpack compose实现交错网格布局?非常有用(。

问题陈述是,当我滚动到列表底部时,并没有进一步的网络呼叫。根据文档,没有对下一个项目进行分页调用的方法,只要我们将输入列表作为itemList.collectAsLazyMappingItems((并将其传递给lazycolumn/lazyrow,它就会自动执行。但对于上述交错布局,这种情况并不是自动发生的。

我正在测试的一个解决方案是手动观察可见项目的索引,以及它们是否接近列表的末尾,并手动调用网络请求。(请参阅此代码实验室的启动代码(https://developer.android.com/codelabs/android-paging#0(

交错布局在某种程度上是在内部创建和使用多个COLUMNS并将项分配给它们列的实现的本质。这里的挑战是我们如何知道我们正在接近名单的末尾。

交错布局的代码是这样的(我不完全理解这是如何工作的(

@Composable
private fun CustomStaggeredVerticalGrid(
// on below line we are specifying
// parameters as modifier, num of columns
modifier: Modifier = Modifier,
numColumns: Int = 2,
content: @Composable () -> Unit
) {
// inside this grid we are creating
// a layout on below line.
Layout(
// on below line we are specifying
// content for our layout.
content = content,
// on below line we are adding modifier.
modifier = modifier
) { measurable, constraints ->
// on below line we are creating a variable for our column width.
val columnWidth = (constraints.maxWidth / numColumns)
// on the below line we are creating and initializing our items 
constraint widget.
val itemConstraints = constraints.copy(maxWidth = columnWidth)
// on below line we are creating and initializing our column height
val columnHeights = IntArray(numColumns) { 0 }
// on below line we are creating and initializing placebles
val placeables = measurable.map { measurable ->
// inside placeble we are creating
// variables as column and placebles.
val column = testColumn(columnHeights)
val placeable = measurable.measure(itemConstraints)
// on below line we are increasing our column height/
columnHeights[column] += placeable.height
placeable
}
// on below line we are creating a variable for
// our height and specifying height for it.
val height =
columnHeights.maxOrNull()?.coerceIn(constraints.minHeight, 
constraints.maxHeight)
?: constraints.minHeight
// on below line we are specifying height and width for our layout.
layout(
width = constraints.maxWidth,
height = height
) {
// on below line we are creating a variable for column y pointer.
val columnYPointers = IntArray(numColumns) { 0 }
// on below line we are setting x and y for each placeable item
placeables.forEach { placeable ->
// on below line we are calling test
// column method to get our column index
val column = testColumn(columnYPointers)
placeable.place(
x = columnWidth * column,
y = columnYPointers[column]
)
// on below line we are setting
// column y pointer and incrementing it.
columnYPointers[column] += placeable.height
}
}
}

}

调用上面的代码作为下面的

Column(
// for this column we are adding a
// modifier to it to fill max size.
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.then(layoutModifier)
) {
// on below line we are creating a column
// for each item of our staggered grid.
CustomStaggeredVerticalGrid(
// on below line we are specifying
// number of columns for our grid view.
numColumns = numColumns,
) {
// inside staggered grid view we are
// adding images for each item of grid.
itemList.forEachIndexed { index,  singleItem ->
// on below line inside our grid
// item we are adding card.
SomesingleItemCompose(singleItem , singleItemModifier ,index) // this one single grid item Ui as per requirement
}
}
}

如上所述,我正在测试组合交错布局中的分页数据加载。它起作用了。技巧是使用和调整一点"提前分页开始"代码实验室代码(手动分页数据处理(,并将其移动到组合中。(仍然没有办法使用分页库(

解决方案:https://github.com/rishikumr/stackoverflow_code_sharing/tree/main/staggered-layout-compose-with_manual_pagination

工作视频:https://drive.google.com/file/d/1IsKy0wzbyqI3dme3x7rzrZ6uHZZE9jrL/view?usp=sharing

它是如何工作的:

  1. 提出网络请求,将其提供给UI自定义交错布局(如何使用Jetpack compose实现交错网格布局?(

  2. 听手动滚动,当滚动到最后时发出网络请求(下一页(。(https://github.com/rishikumr/stackoverflow_code_sharing/blob/main/staggered-layout-compose-with_manual_pagination/app/src/main/java/com/example/android/codelabs/paging/ui/SearchRepositoriesActivity.kt)

    val itemList = remember { mutableStateListOf<Repo>() }
    var lastListSize by remember { mutableStateOf(0) }
    LaunchedEffect(Unit) {
    viewModel.fetchContent()
    .collect { result ->
    when (result) {
    is RepoSearchResult.Success -> {
    Log.d("GithubRepository", "result.data ${result.data.size}")
    itemList.clear()
    itemList.addAll(result.data)
    }
    is RepoSearchResult.Error -> {
    Toast.makeText(
    this@SearchRepositoriesActivity,
    "uD83DuDE28 Wooops $result.message}",
    Toast.LENGTH_LONG
    ).show()
    }
    }
    }
    }
    val scrollState = rememberScrollState()
    val endReached = remember {
    derivedStateOf {
    (scrollState.value == scrollState.maxValue) && (lastListSize != itemList.size) && (scrollState.isScrollInProgress)
    }
    }
    Column(Modifier.verticalScroll(scrollState), horizontalAlignment = Alignment.CenterHorizontally) {
    Box(modifier = Modifier.size(100.dp)) {
    Text("Other Top composable")
    }
    StaggeredVerticalScreen(
    itemList = itemList,
    numColumns = 2,
    layoutModifier = Modifier.padding(
    start = 12.dp,
    bottom = 12.dp
    ),
    singleItemModifier = Modifier.padding(
    end = 12.dp,
    top = 12.dp
    )
    ) { singleGridItem, singleItemModifier, index ->
    SingleArticleItem(singleGridItem  , index)
    }
    if (endReached.value) {
    lastListSize = itemList.size
    Log.d("SearchRepositoriesActivity", "End of scroll lazyItems.itemCount=${itemList.size}")
    viewModel.accept(UiAction.FetchMore)
    }
    }
    
  3. 你必须保持当前的页码和其他一些东西。(https://github.com/rishikumr/stackoverflow_code_sharing/blob/main/staggered-layout-compose-with_manual_pagination/app/src/main/java/com/example/android/codelabs/paging/data/GithubRepository.kt)

A。通过代码实验室提前分页开始代码,您将了解它是如何工作的。(我删除了文本更改api调用的部分代码,因为我不需要它们(B.这个目前只在内存中缓存,我正在处理Room数据库存储。我相信这应该不难。

如果您必须使用分页库,那么我们需要将视图嵌入到我们的compose

选项2:另一个选项是使用"androidx.compose.ui:ui viewbinding"库在父compose中扩展视图交错布局xml。我也试过这个,效果非常好。Beaware所有thigs都需要wrt视图、适配器和所有

setContent {
// get the view model
val viewModel = ViewModelProvider(
this, Injection.provideViewModelFactory(
context = this,
owner = this
)
)[SearchRepositoriesViewModel::class.java]
AndroidViewBinding(ActivitySearchRepositoriesBinding::inflate) {
val repoAdapter = ReposAdapter()
val header = ReposLoadStateAdapter { repoAdapter.retry() }
list.adapter = repoAdapter.withLoadStateHeaderAndFooter(
header = header,
footer = ReposLoadStateAdapter { repoAdapter.retry() }
)
val staggeredGridLayoutManager =
StaggeredGridLayoutManager(2, LinearLayoutManager.VERTICAL)
this.list.apply {
layoutManager = staggeredGridLayoutManager
setHasFixedSize(true)
adapter = repoAdapter
}
lifecycleScope.launch {
viewModel.pagingDataFlow.collectLatest { movies ->
repoAdapter.submitData(movies)
}
}
retryButton.setOnClickListener { repoAdapter.retry() }
lifecycleScope.launch {
viewModel.pagingDataFlow.collectLatest(repoAdapter::submitData)
}
lifecycleScope.launch {
repoAdapter.loadStateFlow.collect { loadState ->
// Show a retry header if there was an error refreshing, and items were previously
// cached OR default to the default prepend state
header.loadState = loadState.mediator
?.refresh
?.takeIf { it is LoadState.Error && repoAdapter.itemCount > 0 }
?: loadState.prepend
val isListEmpty =
loadState.refresh is LoadState.NotLoading && repoAdapter.itemCount == 0
// show empty list
emptyList.isVisible = isListEmpty
// Only show the list if refresh succeeds, either from the the local db or the remote.
list.isVisible =
loadState.source.refresh is LoadState.NotLoading || loadState.mediator?.refresh is LoadState.NotLoading
// Show loading spinner during initial load or refresh.
progressBar.isVisible = loadState.mediator?.refresh is LoadState.Loading
// Show the retry state if initial load or refresh fails.
retryButton.isVisible =
loadState.mediator?.refresh is LoadState.Error && repoAdapter.itemCount == 0
// Toast on any error, regardless of whether it came from RemoteMediator or PagingSource
val errorState = loadState.source.append as? LoadState.Error
?: loadState.source.prepend as? LoadState.Error
?: loadState.append as? LoadState.Error
?: loadState.prepend as? LoadState.Error
errorState?.let {
Toast.makeText(
this@SearchRepositoriesActivity,
"uD83DuDE28 Wooops ${it.error}",
Toast.LENGTH_LONG
).show()
}
}
}
}
}

我有两个样本(选项1和选项2(工作和测试。到目前为止,没有闪烁,也没有突然的行为。这两种选择都很有效。如果有什么我可以帮忙的,请告诉我。(这也是我的第一个答案是的…!!(

最新更新