如何每 5 秒更新一次 ViewPager2
How to update ViewPager2 every 5 seconds
我需要不断更新Viewpager2中的数据
我这样做,
每五秒调用一次
adapter?.updateData(it)
fun updateData(items: List<TestDataObject>) {
list = items
notifyDataSetChanged()
}
问题是在notifyDataSetChange之后页面显示错误的文本
我认为问题出在notifyDataSetChange,但我不知道如何解决它。
- 完整代码 github
- 演示问题的视频 photos.google.com
主要活动
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 = "")
我看到两个主要问题:
notifyDataSetChanged
方法将强制您的适配器重新呈现您在 activity 上拥有的整套项目。您的列表与新列表仅在一个元素上有所不同并不重要:它将整体重新呈现。
我建议使用 ListAdapter
,它将处理您使用 submitList
方法为您设置。它针对您经常更新列表的用例进行了优化。
最重要的是,适配器的构建方式。你有 属性:
lateinit var binding: HolderTestBinding
在每个 onCreateViewHolder
调用中设置。但是,它是一个 class 字段,每次设置它时,旧持有者的引用都会丢失,并且在每个 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
}
}
}
}
我需要不断更新Viewpager2中的数据 我这样做, 每五秒调用一次
adapter?.updateData(it)
fun updateData(items: List<TestDataObject>) {
list = items
notifyDataSetChanged()
}
问题是在notifyDataSetChange之后页面显示错误的文本 我认为问题出在notifyDataSetChange,但我不知道如何解决它。
- 完整代码 github
- 演示问题的视频 photos.google.com
主要活动
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 = "")
我看到两个主要问题:
notifyDataSetChanged
方法将强制您的适配器重新呈现您在 activity 上拥有的整套项目。您的列表与新列表仅在一个元素上有所不同并不重要:它将整体重新呈现。
我建议使用ListAdapter
,它将处理您使用submitList
方法为您设置。它针对您经常更新列表的用例进行了优化。最重要的是,适配器的构建方式。你有 属性:
lateinit var binding: HolderTestBinding
在每个 onCreateViewHolder
调用中设置。但是,它是一个 class 字段,每次设置它时,旧持有者的引用都会丢失,并且在每个 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
}
}
}
}