Search code examples
androidandroid-jetpack-composeandroid-paging-3

paging3 pagingdata insertHeaderItem method cannot insert multiple pieces of data


Like the following, I add multiple elements to the headerList when needed, and refresh, and the headerList will display as many elements as there are.

val pageFlow = Pager(
    config = PagingConfig(
        pageSize = PAGE_SIZE,          
        initialLoadSize = INITIAL_LOAD_SIZE,   
        enablePlaceholders = false,  
        prefetchDistance = PREFETCH_DISTANCE,   
    ),
    initialKey = 1,   
    pagingSourceFactory = { HiPagingSourceFactory() }  
).flow.map { pagingData ->

    headerList.fold(pagingData) { acc, item ->
        acc.insertHeaderItem(TerminalSeparatorType.FULLY_COMPLETE, item)
    }
}.cachedIn(viewModelScope)  

val headerList = mutableListOf<T>()

pagingViewModel.headerList.add(VideoCommentInfo(
    videoId = videoId,
    userName = user?.userName.toString(),
    avatar = user?.avatar.toString(),
    content = comment,
    createdAt = System.currentTimeMillis() / 1000,
    resource = if (filePath == null) null else ResourceInfo(0, filePath),
    type = VideoCommentType.TYPE_ROOT
))
adapter.refresh()

But I didn’t want to call the refresh method, so I cached the pagingdata object and called the following method.

    lifecycleScope.launch {
        pagingViewModel.pageFlow.collect {
            adapter.submitPagingData(it)
        }
    }

    adapter.insertHeaderItem(
        VideoCommentInfo(
            videoId = videoId,
            userName = user?.userName.toString(),
            avatar = user?.avatar.toString(),
            content = comment,
            createdAt = System.currentTimeMillis() / 1000,
            resource = if (filePath == null) null else ResourceInfo(0, filePath),
            type = VideoCommentType.TYPE_ROOT
        )
    )

var pagingData: PagingData<T>? = null

fun insertHeaderItem(item: T) {
    val newPagingData = pagingData?.insertHeaderItem(item = item) ?: return
    submitPagingData(newPagingData)
}

fun submitPagingData(pagingData: PagingData<T>) {
    this.pagingData = pagingData
    if (lifecycleOwner != null) {
        submitData(lifecycleOwner.lifecycle, pagingData)
        return
    }
}

But when I call adapter.insertHeaderItem(VideoCommentInfo() ) method multiple times, only the last piece of data can be displayed.

In fact, I prefer to modify PagingData in the adapter. I don't know why the header cannot be accumulated, so I save the elements that need to be inserted, and submit the full amount to the adapter for each insertion.

private val headerItems = mutableListOf<T>()

fun insertHeaderItem(item: T) {
    headerItems.add(0, item)

    var newPagingData = pagingData ?: return
    headerItems.asReversed().forEach { header ->
        newPagingData = newPagingData.insertHeaderItem(item = header)
    }

    submitPagingData(newPagingData)
}

Solution

  • You can update the headerList without calling adapter.refresh() and keep the logic completely in your ViewModel with only small changes in your code:

    private val pageFlow = Pager(...).flow.cachedIn(viewModelScope)
    private val headerListFlow = MutableStateFlow(emptyList())
    
    val pageFlowWithHeaders = combine(
      pageFlow,
      headerListFlow,
    ) { pagingData, headerList ->
      headerList.fold(pagingData) { acc, item ->
        acc.insertHeaderItem(item)
      }
    }
    
    fun addHeader(header: VideoCommentInfo) {
      headerListFlow.update { it + header }
    }
    

    This makes use of the combine flow operator to combine the paging flow and flow of the headers. Every time you add new header, new PagingData are creating without the need to call refresh and the original paging flow is still cached in your viewModelScope.