当 Kotlin 中的数据发生变化时,如何在 RecyclerView 中重新绑定项目?

Posted

技术标签:

【中文标题】当 Kotlin 中的数据发生变化时,如何在 RecyclerView 中重新绑定项目?【英文标题】:How to rebind item in RecyclerView when the data changed in Kotlin? 【发布时间】:2018-09-13 15:36:30 【问题描述】:

我自定义了一个 RecyclerView 类,它将在代码 B 的 Kotlin 中显示 val backupItemList: List<MSetting> 的内容

现在我在RecyclerView类外面修改backupItemList的数据,我认为Code D会在UI中显示最新的数据,但是我失败了,UI仍然是显示旧的数据。 我必须使用 Code C 来显示最新数据。

代码 D 有什么问题?

代码 A

class UIMain : AppCompatActivity() 
   override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.layout_main)                       

        allList= SettingHandler().getListAllSetting()            

        mRecyclerView.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL, false)
        mCustomAdapter= CustomAdapter(allList)
        mRecyclerView.adapter= mCustomAdapter


   


   public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) 
       //Code C          
       if (resultCode == RESULT_OK) 
           allList=SettingHandler().getListAllSetting()
           mCustomAdapter= CustomAdapter(allList)
           mRecyclerView.adapter= mCustomAdapter
           mCustomAdapter.notifyDataSetChanged()
           mCustomAdapter.setSelectedItem(selectedBackupItem)  
       


       //Code D     
       if (resultCode == RESULT_OK) 
          allList=SettingHandler().getListAllSetting()                
          mCustomAdapter.setSelectedItem(selectedBackupItem)                
                

           


代码 B

  class CustomAdapter (val backupItemList: List<MSetting>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() 

        val noRecord=-1
        private var mSelectedItem = noRecord

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomAdapter.ViewHolder 
            val v = LayoutInflater.from(parent.context).inflate(R.layout.item_recyclerview, parent, false)
            return ViewHolder(v)
        

        fun getSelectedItem():Int
            return  mSelectedItem
        

        fun setSelectedItem(index:Int)
            if (index in 0..(backupItemList.size-1) )
                mSelectedItem=index
                notifyDataSetChanged();
            

        

        override fun onBindViewHolder(holder: CustomAdapter.ViewHolder, position: Int) 
            holder.bindItems(backupItemList[position])
        

        override fun getItemCount(): Int 
            return backupItemList.size
        

        inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) 

            fun bindItems(aMSetting: MSetting) 
                itemView.tvSubject.text=aMSetting.name
                itemView.tvCreatedDate.text=aMSetting.createdDate.toDateString()
                itemView.tvDescription.text=aMSetting.description   
                itemView.radioButton.setOnClickListener 
                mSelectedItem=adapterPosition
                notifyDataSetChanged();
            

            if(adapterPosition == 0 && mSelectedItem == noRecord)              
                itemView.radioButton.isChecked = true
                mSelectedItem=adapterPosition
            
            else 
                itemView.radioButton.isChecked =(adapterPosition == mSelectedItem)
                   
            

        

    

致 civic.LiLister:

如果我使用代码 E(我将 val 替换为 var),代码 C 和代码 D 都会得到相同的结果,为什么?

代码 E

  class CustomAdapter (var backupItemList: List<MSetting>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() 

        val noRecord=-1
        private var mSelectedItem = noRecord

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomAdapter.ViewHolder 
            val v = LayoutInflater.from(parent.context).inflate(R.layout.item_recyclerview, parent, false)
            return ViewHolder(v)
        

        fun getSelectedItem():Int
            return  mSelectedItem
        

        fun setSelectedItem(index:Int)
            if (index in 0..(backupItemList.size-1) )
                mSelectedItem=index
                notifyDataSetChanged();
            

        

        override fun onBindViewHolder(holder: CustomAdapter.ViewHolder, position: Int) 
            holder.bindItems(backupItemList[position])
        

        override fun getItemCount(): Int 
            return backupItemList.size
        

        inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) 

            fun bindItems(aMSetting: MSetting) 
                itemView.tvSubject.text=aMSetting.name
                itemView.tvCreatedDate.text=aMSetting.createdDate.toDateString()
                itemView.tvDescription.text=aMSetting.description   
                itemView.radioButton.setOnClickListener 
                mSelectedItem=adapterPosition
                notifyDataSetChanged();
            

            if(adapterPosition == 0 && mSelectedItem == noRecord)              
                itemView.radioButton.isChecked = true
                mSelectedItem=adapterPosition
            
            else 
                itemView.radioButton.isChecked =(adapterPosition == mSelectedItem)
                   
            

        

    

【问题讨论】:

请明确您的观点,您在哪里更改来自服务器的数据? 从 UI,我通过 UI 编辑 allList 来自另一个活动? 是的。从另一个活动,我使用public override fun onActivityResult 来处理结果 清除一个点,您想在列表中添加更多项目还是编辑之前添加的项目? 【参考方案1】:

秘密可能隐藏在这里:

类 CustomAdapter (val backupItemList: List)

当您初始化 CustomAdapter 的实例时,该值被复制到属性 backupItemList,而不是分配引用。因此,当您更改 UIMain 的 allList 属性时,backupItemList 不会像您预期的那样发生变化。

解决方案很简单,正如 Ganesh Tikone 所写:添加一个方法来更新 backupItemList。

fun updateData(data: List<MovieModel>) 
    backupItemList.clear()
    backupItemList.addAll(data)
    notifyDataSetChanged()

并将代码 D 更改为:

//Code D     
   if (resultCode == RESULT_OK) 
      allList=SettingHandler().getListAllSetting()  
      mCustomAdapter.updateData(allList)    
      mCustomAdapter.setSelectedItem(selectedBackupItem)                
     

试一试。

【讨论】:

谢谢!请看我修改后的问题,当我使用class CustomAdapter (var backupItemList: List)时,代码 C 和代码 D 得到相同的结果@ 当我初始化一个CustomAdapter (var backupItemList: List)的实例时,我认为会为backupItemList分配一个引用,对吧? 还有,allList 是 List 而不是 MutableList,不能调用 allList.clear() 和 allList.addAll() Replace val with var for property backupItemList: List 不是一个好习惯,我认为。虽然它适用于您当前的项目。在函数式编程中,我们应该限制可变数据的使用。我们应该尝试使函数成为纯函数。 var 类似于“全局”变量。这可能会从适配器中更改出来,并且可能会导致难以修复的奇怪错误。【参考方案2】:

在您的 Activity onCreate 方法中

    override fun onCreate(savedInstanceState: Bundle?) 
            super.onCreate(savedInstanceState)
            setContentView(R.layout.layout_main)                
    
            allList = ArrayList< MSetting>()
            mCustomAdapter = CustomAdapter(mChildList)
            mRecyclerView.layoutManager = LinearLayoutManager(context)
            mRecyclerView.adapter = mCustomAdapter
    

现在在 onActivityResult

     public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) 

           //Code C          
           if (resultCode == RESULT_OK) 
              if(data != null)
                 allList.clear()
                 allList.addAll(SettingHandler().getListAllSetting())
                 mCustomAdapter.notifyDataSetChanged()
                 mCustomAdapter.setSelectedItem(selectedBackupItem) 
               
           
        
              

【讨论】:

【参考方案3】:

在您的代码 D 中,您覆盖了 allList 的引用

CustomAdapter 中的 backupItemList 也有相同的参考。 因此,如果您重新分配 allList 的引用,则不会在回收站视图中获取更改

首先确保 allList 是可变的

val allList = mutableListOf<MSetting>()

然后在代码 D 中

allList.clear()
allList.addAll(SettingHandler().getListAllSetting())
mCustomAdapter.notifyDataSetChanged()

【讨论】:

【参考方案4】:

检查此代码,

if (resultCode == RESULT_OK) 
           allList.clear()
           allList=SettingHandler().getListAllSetting()
           mCustomAdapter= CustomAdapter(allList)
           mRecyclerView.adapter= mCustomAdapter  
           mCustomAdapter.setSelectedItem(selectedBackupItem) 
       

【讨论】:

记得在onActivityResult()的日志中查看你的列表是否有数据。 谢谢!但是您的代码就像我的代码 C,我认为也许有更好的方法 我认为你不需要运行allList.clear(),因为allList=SettingHandler().getListAllSetting()会自动清除旧数据 onActivityResult() 是否返回 RESULT_OK? 如果我在另一个活动中成功编辑了一条记录,我将分配RESULT_OK并将其返回给主活动【参考方案5】:

问题是你没有使用任何binding,这意味着当你修改list时,Adapter不会意识到这一点,因此它只是显示旧项目,但是一旦你通知adapter关于更改您的列表使用notifyDatasetChanged,它将尝试使用新数据刷新项目。如果你想用code D实现功能,你需要使用Data Binding using Observable pattern。更多信息RecyclerView and Data Binding

【讨论】:

我认为代码notifyDataSetChanged();应该重新绑定基于修改过的allList的items,但实际上系统并没有这样做 还有更多,您能否向我展示一些示例代码,以使用关于我的问题的 Observable 模式使用数据绑定?【参考方案6】:

片段代码

private var adapter: RecentMovieAdapter? = null

Adapter在fragment的onCreateView方法中初始化

adapter = RecentMovieAdapter(activity, this)

这是Fragment中的回调方法,使用线程技术从Server获取数据

override fun onDataReceived(randomDialog: List<MovieModel>) 
    shimmerView!!.stopShimmerAnimation()
    shimmerView!!.visibility = View.GONE
    adapter!!.updateData(randomDialog)

适配器类代码

private var movieList: MutableList<MovieModel> = mutableListOf<MovieModel>()

像这样在Adapter类中添加更新函数

fun updateData(data: List<MovieModel>) 
    movieList.clear()
    movieList.addAll(data)
    notifyDataSetChanged()

【讨论】:

【参考方案7】:

yourAdapter().notifyDataSetChanged

【讨论】:

以上是关于当 Kotlin 中的数据发生变化时,如何在 RecyclerView 中重新绑定项目?的主要内容,如果未能解决你的问题,请参考以下文章

当 Extjs 中的窗口大小发生变化时如何显示“分页工具栏”?

当实际数据发生变化时,结果缓存数据会发生啥变化?

当 vuex 商店的值发生变化时,如何更新组件中的状态?

当AngularJS网站中的网站内容发生变化时,如何使index.html不缓存?

当redux store中的值发生变化时,如何更新formik中的特定表单字段?

当 sql server 数据库中的数据发生变化时,从一个 android 应用程序向另一个应用程序发送推送通知