首页 > 解决方案 > 如何每 5 秒更新一次 ViewPager2

问题描述

我需要不断更新 Viewpager2 中的数据我这样做,每五秒调用一次

adapter?.updateData(it)      

fun updateData(items: List<TestDataObject>) {
        list = items
        notifyDataSetChanged()
    }

问题是在 notifyDataSetChange 之后页面显示错误的文本我认为问题出在 notifyDataSetChange 中,但我不知道如何解决。

主要活动

    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.view.View
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.lifecycleScope
    import by.lwo.viewpagertest.databinding.ActivityMainBinding
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.launch
    
    class MainActivity : AppCompatActivity() {
    
        lateinit var binding: ActivityMainBinding
        private var adapter: ViewPagerAdapter? = null
        val testList: MutableList<TestDataObject> = emptyList<TestDataObject>().toMutableList()
    
        private val _testLiveData = MutableLiveData<Event<List<TestDataObject>>>()
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(binding.root)
            setDataToList()
            setAdapter()
            constantlyUpdateList()
    
            _testLiveData.observeEvent(this) {
                adapter?.updateData(it)
                constantlyUpdateList()
           }
    
        }
    
        private fun constantlyUpdateList() {
            lifecycleScope.launch {
                delay(5000)
                setDataToList()
                _testLiveData.value = Event(testList)
            }
        }
    
        private fun setDataToList() {
            testList.clear()
            repeat(6) {
                testList.add(
                    TestDataObject(
                        titleText = "title for page $it",
                        infoText = "infoText for page $it"
                    )
                )
            }
        }
    
        private fun setAdapter(){
            binding.apply {
                adapter = ViewPagerAdapter(testList)
                viewPager.adapter = adapter
                viewPager.offscreenPageLimit = testList.size
            }
        }
    }

适配器

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import by.lwo.viewpagertest.databinding.HolderTestBinding

class ViewPagerAdapter(var list: List<TestDataObject>) : RecyclerView.Adapter<ViewPagerAdapter.TestHolder>() {

    lateinit var binding: HolderTestBinding

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewPagerAdapter.TestHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.holder_test, parent, false)
        binding = DataBindingUtil.bind(view)!!
        return TestHolder(view)
    }

    override fun onBindViewHolder(holder: ViewPagerAdapter.TestHolder, position: Int) {
        holder.bind(list[position])
    }

    override fun getItemCount(): Int = list.size

    fun updateData(items: List<TestDataObject>) {
        list = items
        notifyDataSetChanged()
    }

    inner class TestHolder(containerView: View) : RecyclerView.ViewHolder(containerView) {
        fun bind(testDataObject: TestDataObject) {
            binding.apply {
                titleTv.text = testDataObject.titleText
                textTv.text = testDataObject.infoText
            }
        }
    }

}

data class TestDataObject(val titleText: String = "", val infoText: String = "")

标签: androidandroid-recyclerviewkotlin-coroutinesandroid-viewpager2

解决方案


我看到两个主要问题:

  1. notifyDataSetChanged方法将强制您的适配器重新渲染您在活动中拥有的整套项目。您的列表与新列表仅在一个元素上有所不同并不重要:它将全部重新渲染。
    我建议使用,它将使用该方法ListAdapter为您设置。submitList它针对您将经常更新列表的用例进行了优化。

  2. 最重要的是,您的适配器的构建方式。您拥有以下财产:

lateinit var binding: HolderTestBinding

在每次onCreateViewHolder通话中都会设置。但是,它是一个类字段,每次设置时,旧持有者的引用都会丢失,并且在每种TestHolder情况下,您都将引用最后一个膨胀的绑定,因此,您的所有视图持有者都将引用完全相同的视图持有者,而不是他们自己的。
更好的方法是将绑定传递给每个持有者的构造函数:

class ViewPagerAdapter(...) : ListAdapter(...) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewPagerAdapter.TestHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.holder_test, parent, false)
        val holder = DataBindingUtil.bind(view)?.let {
            TestHolder(it)
        }
        return holder!!
    }

    // 'inner' modifier is not needed
    class TestHolder(private val binding: TestHolderBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(testDataObject: TestDataObject) {
            // this 'binding' will reference their own binding, and not the 'global' one :)
            binding.apply {
                titleTv.text = testDataObject.titleText
                textTv.text = testDataObject.infoText
            }
        }
    }
} 

推荐阅读