MVVM中RecyclerView中的分页重复值

Posted

技术标签:

【中文标题】MVVM中RecyclerView中的分页重复值【英文标题】:Pagination Duplicating Values in RecyclerView in MVVM 【发布时间】:2021-12-15 17:33:18 【问题描述】:

我也是 Kotlin MVVM 的新手,我尝试使用旧方法实现分页,但遇到了 RecyclerView 的问题,每当我滚动它时,数据重复,我尝试了 DiffUtils 但没有帮助。

我在 VIEWMODEL 类中记录了数据,数据不重复 但是,当我登录我观察到的 Activity 时,它显示重复值

搜索结果.KT

class SearchResultActivity : AppCompatActivity() 
private lateinit var layoutManager: LinearLayoutManager
private lateinit var recyclerView: RecyclerView
private lateinit var pullAdapter: CustomAdapter
private var pageNumber = 1
private var totalItemsCount = 0
private var firstVisibleItemsCount = 0
private var visibleItemsCount = 0
private var previousTotal = 0
private var loading = true
private var fillPullList: ArrayList<RepoPull> = ArrayList()
private var userName: String = ""
private var repoName: String = ""
private var isEnd = false


override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)
    initialize()

    getDataPull(userName, repoName)

    loadNextData()



private fun initialize() 
    setContentView(R.layout.activity_search_result)
    recyclerView = findViewById(R.id.repoRecView)
    layoutManager = LinearLayoutManager(this)

    getSearchQuery()



private fun getSearchQuery() 
    userName = intent.getStringExtra("owner").toString()
    repoName = intent.getStringExtra("repo").toString()
    populatePullRv()



private fun populatePullRv() 
    recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
    recyclerView.layoutManager = layoutManager
    pullAdapter = CustomAdapter(this, fillPullList)
    recyclerView.adapter = pullAdapter
    progressBar.visibility = View.VISIBLE


private fun loadNextData() 
    recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() 

        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) 
            super.onScrolled(recyclerView, dx, dy)

            val mLayoutManger = recyclerView.layoutManager as LinearLayoutManager
            visibleItemsCount = mLayoutManger.childCount
            totalItemsCount = mLayoutManger.itemCount
            firstVisibleItemsCount = mLayoutManger.findFirstVisibleItemPosition()

            if (loading) 
                if (totalItemsCount > previousTotal) 
                    previousTotal = totalItemsCount
                    pageNumber++
                    loading = false
                    progressBar.visibility = View.GONE
                
            
            if (!loading && (firstVisibleItemsCount + visibleItemsCount) >= totalItemsCount) 
                getDataPull(userName, repoName)
                loading = true
                Log.d("PAGE", pageNumber.toString())
            


        

    )


private fun getDataPull(username: String?, reponame: String?) 
    val myViewModel = ViewModelProviders.of(this).get(PullVM::class.java)

    myViewModel.endofList.observe(this, 
        if (it == true) 
            isEnd = true
            progressBar.visibility = View.GONE
            Toast.makeText(this@SearchResultActivity, "All PR Fetched", Toast.LENGTH_SHORT)
                .show()

        

    )

    myViewModel.status.observe(this, 
        if (it == false) 
            showError(getString(R.string.no_net))

        
    )

    myViewModel.getPullDataFromVM().observe(this, 

        if (it != null) 
           listRepos(it)  **//DUPLICATE VALUE COMING**
         else 

            showError(getString(R.string.nothing_found))
        
    )



    myViewModel.getPullList(username.toString(), reponame.toString(), pageNumber)



private fun showError(s: String) 
    progressBar.visibility = View.GONE
    val theView =
        this@SearchResultActivity.findViewById<View>(android.R.id.content)
    Snackbar.make(
        theView,
        s,
        Snackbar.LENGTH_LONG
    ).show()



@SuppressLint("NotifyDataSetChanged")
fun listRepos(repos: List<RepoPull>) 
    if (!isEnd) 
        progressBar.visibility = View.GONE
        fillPullList.addAll(repos)
        pullAdapter.notifyDataSetChanged()
    

PULLVM(查看模型).kt

 class PullVM : ViewModel() 
var pullList: MutableLiveData<List<RepoPull>>
var status = MutableLiveData<Boolean?>()
var endofList = MutableLiveData<Boolean?>()

init 

    pullList = MutableLiveData()


fun getPullDataFromVM(): MutableLiveData<List<RepoPull>> 

    return pullList


fun getPullList(ownerName: String, repoName: String, pgNo: Int) 

    val retriever = GitHubRetriever


    val callback = object : Callback<List<RepoPull>> 
        override fun onFailure(call: Call<List<RepoPull>>, t: Throwable) 
            status.value = false
        

        override fun onResponse(
            call: Call<List<RepoPull>>,
            response: Response<List<RepoPull>>
        ) 
            if (response.body()?.size == 0) 
                endofList.value = true
            
            if (response.code() == 404) 
                pullList.postValue(null)

             else 
                status.value = true
                val repos = response.body()
                if (repos != null) 
                    pullList.postValue(repos) 
                
            
        

    

    retriever.userRepos(
        callback,
        ownerName,
        repoName,
        pgNo
    )

【问题讨论】:

【参考方案1】:

尝试将您的 viewModel 实例化和观察者设置移动到 onCreate,这样您就不必创建一个新的 viewModel 实例并为您的 LiveDatas 设置一个新的 observable。

将 myViewModel 声明为 Activity 的 lateinit 属性并将这部分移至 onCreate

myViewModel = ViewModelProviders.of(this).get(PullVM::class.java)

    myViewModel.endofList.observe(this, 
        if (it == true) 
            isEnd = true
            progressBar.visibility = View.GONE
            Toast.makeText(this@SearchResultActivity, "All PR Fetched", Toast.LENGTH_SHORT)
                .show()

        

    )

    myViewModel.status.observe(this, 
        if (it == false) 
            showError(getString(R.string.no_net))

        
    )

    myViewModel.getPullDataFromVM().observe(this, 

        if (it != null) 
           listRepos(it)  **//DUPLICATE VALUE COMING**
         else 

            showError(getString(R.string.nothing_found))
        
    )

private fun getDataPull(username: String?, reponame: String?)

应该只包含

myViewModel.getPullList(username.toString(), reponame.toString(), pageNumber)

【讨论】:

太棒了!它有效,感谢您的友好回答。能否请您参考链接,以便我可以更有效地学习基础知识 @Xay 这是liveData developer.android.com/topic/libraries/architecture/livedata 的文档和一些示例以及基本示例代码journaldev.com/22561/android-mvvm-livedata-data-binding @Xay 你能把它标记为答案吗?欣赏它。

以上是关于MVVM中RecyclerView中的分页重复值的主要内容,如果未能解决你的问题,请参考以下文章

分页不适用于 NestedScrollView 中的 RecyclerView

HTML表格中的分页符[重复]

使用 Google 的分页库更新 PagedList 中的单个项目

WPF 中的分页集合视图

在分页库 3 中恢复滚动位置

oracle mysql sqlserver数据库中的分页