简体   繁体   English

使用 Paging v3 时,在回收器视图中填充的多次列表项

[英]Multiple times list item populated in recycler-view, when using Paging v3

I have implemented android-paging v3 following https://proandroiddev.com/paging-3-easier-way-to-pagination-part-1-584cad1f4f61 & https://proandroiddev.com/how-to-use-the-paging-3-library-in-android-part-2-e2011070a37d .我已经在https://proandroiddev.com/paging-3-easier-way-to-pagination-part-1-584cad1f4f61 & https://proandroiddev.com/how-to-use之后实现了 android-paging v3 paging-3-library-in-android-part-2-e2011070a37d But I see the data is populated multiple times even when there are just 3 records in the local database.但是我看到数据被多次填充,即使本地数据库中只有 3 条记录。

Notifications list screen通知列表屏幕

Can anyone suggest what I am doing wrong?谁能建议我做错了什么? Thanks in advance.提前致谢。

My code is as follows:我的代码如下:

NotificationsFragment通知片段

class NotificationsFragment : Fragment() {

    private lateinit var binding: FragmentNotificationsBinding
    private val alertViewModel: NotificationsViewModel by viewModel()

    private val pagingAdapter by lazy { AlertsPagingAdapter() }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentNotificationsBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onResume() {
        super.onResume()
        (activity as MainActivity).setUpCustomToolbar(
            getString(R.string.alerts),
            ""
        )
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.lifecycleOwner = viewLifecycleOwner
        initRecyclerView()
    }

    private fun initRecyclerView() {
        binding.rvAlerts.apply {
            adapter = pagingAdapter.withLoadStateFooter(AlertLoadStateAdapter {})
            layoutManager = LinearLayoutManager(requireContext())
        }

        lifecycleScope.launch {
            alertViewModel.alertListFlow.collectLatest { pagingData ->
                pagingAdapter.submitData(
                    pagingData
                )
            }
        }
    }

}

NotificationsViewModel通知视图模型

class NotificationsViewModel(private val useCase: NotificationsUseCase) : BaseViewModel() {

    val alertListFlow = Pager(PagingConfig(1)) { NotificationsPagingSource(useCase) }
        .flow
        .cachedIn(viewModelScope)
}

NotificationsPagingSource NotificationsPagingSource

import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.example.demo.model.entity.Notifications
import com.example.demo.NotificationsUseCase

class NotificationsPagingSource(private val useCase: NotificationsUseCase) : PagingSource<Int, Notifications>() {

    private companion object {
        const val INITIAL_PAGE_INDEX = 0
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Notifications> {
        val position = params.key ?: INITIAL_PAGE_INDEX
        val randomNotifications : List<Notifications> = useCase.fetchNotifications(params.loadSize)
        return LoadResult.Page(
            data = randomNotifications ,
            prevKey = if (position == INITIAL_PAGE_INDEX) null else position - 1,
            nextKey = if (randomAlerts.isNullOrEmpty()) null else position + 1
        )
    }

    override fun getRefreshKey(state: PagingState<Int, Notifications>): Int? {

        // We need to get the previous key (or next key if previous is null) of the page
        // that was closest to the most recently accessed index.
        // Anchor position is the most recently accessed index
        return state.anchorPosition?.let { anchorPosition ->
            state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
                ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
        }
    }
}

PagingAdapter分页适配器

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView


class NotificationsPagingAdapter :
    PagingDataAdapter<Notifications, NotificationsPagingAdapter.ItemNotificationsViewHolder>(NotificationsEntityDiff()) {

    override fun onBindViewHolder(holder: ItemNotificationsViewHolder, position: Int) {
        getItem(position)?.let { userPostEntity -> holder.bind(userPostEntity) }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemNotificationsViewHolder {
        return ItemNotificationsViewHolder(
            ItemLayoutNotificationsBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
            )
        )
    }

    /**
     * Viewholder for each Notifications layout item
     */
    inner class ItemNotificationsViewHolder(private val binding: ItemLayoutNotificationsBinding) :
        RecyclerView.ViewHolder(binding.root) {
        fun bind(alert: Notifications) {
            binding.tvMessage.text = alert.title
        }
    }

    class NotificationsEntityDiff : DiffUtil.ItemCallback<Notifications>() {
        override fun areItemsTheSame(oldItem: Notifications, newItem: Notifications): Boolean =
            oldItem.id == newItem.id

        override fun areContentsTheSame(oldItem: Alert, newItem: Notifications): Boolean =
            oldItem == newItem
    }
}

NotificationsLoadStateAdapter通知加载状态适配器

class NotificationsLoadStateAdapter(
    private val retry: () -> Unit
) : LoadStateAdapter<NotificationsLoadStateAdapter.LoadStateViewHolder>() {

    override fun onBindViewHolder(holder: LoadStateViewHolder, loadState: LoadState) {

        val progress = holder.itemView.load_state_progress
        val btnRetry = holder.itemView.load_state_retry
        val txtErrorMessage = holder.itemView.load_state_errorMessage

        btnRetry.isVisible = loadState !is LoadState.Loading
        // txtErrorMessage.isVisible = loadState !is LoadState.Loading
        progress.isVisible = loadState is LoadState.Loading

        if (loadState is LoadState.Error) {
           // txtErrorMessage.text = loadState.error.localizedMessage
        }

        btnRetry.setOnClickListener {
            retry.invoke()
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): LoadStateViewHolder {
        return LoadStateViewHolder(
            LayoutInflater.from(parent.context)
                .inflate(R.layout.layout_load_state_view, parent, false)
        )
    }

    class LoadStateViewHolder(private val view: View) : RecyclerView.ViewHolder(view)
}

Dao

  @Query("SELECT * FROM notifications ORDER BY createdAt DESC LIMIT :size")
    fun fetchNotifications(size: Int): List<Notifications>

The issue is fixed.问题已解决。 Actually, the Dao query was wrong and the Paging attributes were wrong.其实是查询错了,分页属性错了。

Dao

  @Query("SELECT * FROM notifications ORDER By createdAt DESC  LIMIT :size OFFSET (:page * :size)")
fun fetchAlerts(page: Int, size: Int): List<Notifications>

NotificationsViewModel通知视图模型

class NotificationsViewModel(private val useCase: NotificationUseCase) : BaseViewModel() {

// If you want to load at a time one page keep pageSize 1
val alertListFlow =
    Pager(PagingConfig(pageSize = 1)) { createPagingSource() }
        .flow
        .cachedIn(viewModelScope)

/**
 * Set up the paging source and the initial page should be 1
 */
private fun createPagingSource(): BasePagingSource<Notifications> {
    return BasePagingSource() { page, pageSize, _ ->
        useCase.fetchNotifications(page, pageSize)
    }
}

} }

and use BasePagingSource instead of NotificationsPagingSource并使用BasePagingSource而不是 NotificationsPagingSource

    /**
 * This is the base class for a custom [PagingSource]
 *
 * @param T the data expected to process
 * @property pageSize
 * @property initialPage
 * @property fetchDataCallback where the concrete API call is executed to fetch the data
 *
 */
class BasePagingSource<T : Any>(
    private val pageSize: Int = 10,
    private val initialPage: Int = 0,
    private val fetchDataCallback: suspend (Int, Int, Boolean) -> List<T>
) : PagingSource<Int, T>() {

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, T> {
        val page = params.key ?: initialPage
        val data = fetchDataCallback.invoke(page, pageSize, page == initialPage)

        // https://android-developers.googleblog.com/2020/07/getting-on-same-page-with-paging-3.html
        val prevKey = null
        val nextKey = if (data.isNullOrEmpty() || data.size < pageSize) {
            null
        } else {
            page + 1
        }

        MyLogger.d("page=$page, params.key=${params.key}, pageSize=$pageSize, prevKey=$prevKey, nextKey=$nextKey, resultSize=${data.size}")
        return LoadResult.Page(data, prevKey, nextKey)
    }

    override fun getRefreshKey(state: PagingState<Int, T>): Int? {
        // just return null and eventually it will use the passed initialPage
        return null
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 滚动在回收器视图列表项中不起作用 - Scrolling is not working inside recycler-view list item 为什么当点击它的项目时,adnroidx recycler-view 点击不起作用? - Why adnroidx recycler-view click not working when click it's item? Android多个水平回收站视图一起滚动 - Android multiple horizontal recycler-view scroll together 在Recycler-View Viewholder中以相同的布局使用不同的xml - Using different xml in same layouts in recycler-view viewholder 单击行时的回收站视图数据绑定 - Recycler-view data binding on click of row 如何在android中的项目下方的recycler-view中减少修复阴影效果 - how to reduce fix shadow effect in recycler-view below item in android 在nestedscrollview中滚动多个水平回收器视图,它们之间带有视图分隔符 - scroll multiple horizonal recycler-view with view-separator between them inside nestedscrollview 如何在 Recycler-view 中添加分区 - How to add section divider in Recycler-view 在Android中的Recycler-View上添加图像时,滚动视图无法正确滚动 - Scroll-view not scrolling properly when add images on Recycler-View in android 分页库 - 一个具有多种视图类型的回收站 - Paging library - one recycler with multiple view types
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM