这个图画的有点丑,凑合看,意思大概就是这样的。

接下来,我就得追踪下这个问题了,开始时我几乎就一口咬定是接口返回的有问题,由于前几次后端没有日志,好像之前的反馈就那么过去了,直到后面又出现一次重复笔记的问题,这次是公司内部员工出现的,于是后端也通过这个抓到了相应的日志,发现返回的笔记的确没有重复的,这下跑不掉了,就是前端的问题。

于是,我又重新梳理了下代码流程,发现有一处比较有嫌疑:

        ...
        if (...) {
            mItems[0] = noteItem
        } else {
            mItems.add(0, noteItem)
        mAdapter.items = mItems
        mAdapter.notifyItemChanged(0)
        ...

鉴于是公司项目,我就省略掉业务逻辑了,这里的代码按照开发者的意图是当RecyclerView第一个item如果已经是noteItem这种类型的时候,我们就将这个位置item替换成最新的,如果这个位置的item不是noteItem这个数据的话,我们需要手动把它添加到第一个位置去,到这里实际上都没有什么问题。

但是,当我看到mAdapter.notifyItemChanged(0)这个方法,直觉告诉我这里好像有点问题,当上面的逻辑走到else这里的时候,会往list里add一个新的item,但是这时候调用的刷新方法却是notifyItemChanged(0)。

这个notifyItemChanged明显是刷新某个item的方法,即当这个item里的数据有变化时,调用这个方法去刷新这个item区域的UI,但是如果我们在adapter中add了一个新的item,再调用这个方法明显是不行的,这里是导致重复的原因嘛,我其实也不太确定。

于是我写了一个Demo试了下。

class RecyclerViewActivity : AppCompatActivity() {
    private val mDataList = ArrayList<Any>()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_recycler_view)
        val layoutManager = LinearLayoutManager(this)
        layoutManager.orientation = RecyclerView.VERTICAL
        recyclerView.layoutManager = layoutManager
        val customAdapter =  CustomAdapter(mDataList)
        recyclerView.adapter = customAdapter
        for (i in 0..5) {
            mDataList.add("text: $i")
        recyclerView.adapter?.notifyDataSetChanged()
    //刷新方法
    fun refresh(view: View) {
        mDataList.add(0, "add item")
        recyclerView.adapter?.notifyItemChanged(0)

构造一个普通的feed列表,每次点击刷新按钮,就会调用刷新方法,调用刷新方法的时候往index为0的位置再add一个item,然后再调用notifyItemChanged(0)方法,上下滑动后,发现数据是重复了。

下面放个gif图展示下效果。

从gif图中可以看到,点击刷新按钮,添加了”add item“,往下滑动后,出现了两个”text:5“的item,这个就是重复的item。

我们看到notifyItemChanged的文档说明:

notifyItemChanged(int position)
This is an item change event, not a structural change event.

RecyclerView中有两种不同数据改变事件,一种叫(item changes)项目改变,另一种叫(structual changes)结构改变。项目改变指的是某个单个item的数据发生变化,这个时候没有位置的改变。而structual changes则是有位置的变化发生,主要是数据源的变化会导致RecyclerView item位置发生变化。

我们这个问题是我们往数据源前面加了一个item,这个时候应该需要调用具有structual changes 的方法来刷新,而不是采用notifyItemChanged来刷新,因为notifyItemChanged是一个item changes 的方法。

问题背景前不久我们项目中由用户反馈说遇到笔记重复的问题,而且不只一次遇到类似的反馈。这种重复笔记总是出现的feed流的中间位置,如下示意图所示:这个图画的有点丑,凑合看,意思大概就是这样的。接下来,我就得追踪下这个问题了,开始时我几乎就一口咬定是接口返回的有问题,由于前几次后端没有日志,好像之前的反馈就那么过去了,直到后面又出现一次重复笔记的问题,这次是公司内部员工出现的,于是后端也通过...
随笔记录 问题描述:使用SwipeRefreshLayout控件,网络接口获取数据,adapter显示数据,在SwipeRefreshLayout刷新方法快速刷新,会导致adapter数据显示出现重复的现象 原因分析:在刷新方法时,首先请求接口获取数据,然后在回调将数据给到adapter接受显示,如果快速点击刷新方法,可能会出现先执行回调数据,然后在执行adapter。notifyDATAChange方法 解决办法: 1、设置延时触发刷新方法 2、回调数据后,将数据进行去重处理
Recycleview数据改变时,我们会调用 notifyDataSetChanged,这个时候列表会刷新,为了使 url 没变的 ImageView不重新加载(图片会一闪),我们可以用 mRecyclerViewAdapter.setHasStableIds(true); 使用这个,相当于给ImageView加了一个tag,tag不变的话,不用重新加载图片。但是加了这句话,会使得 ...
解决办法写在前面:在onBindViewHolder把需要隐藏的布局重新再写一遍setVisibility(View.GONE) 如题,最近在做一个聊天程序,使用recyleView来承载聊天界面的内容。 如图item布局为两个LinearLayout垂直布局,第一个是左边的人发送的消息,第二个是“我”发送的消息,默认都设置为GONE不显示,当传入数据的时候判断是哪一方的消息然后另对应的LinearLayout设置为VISIBILE可见 (为了发效果图设置为了Visibile) 然后聊天界面初
Android RecycleView 频繁设置数据crash问题报错Log:问题描述:解决方案: 报错Log: java.lang.IllegalArgumentException: Scrapped or attached views may not be recycled. isScrap:false isAttached:true when scrolling down 问题描述: 网上有很多相关问题的原因: 1.更新数据后adapter没有notifyDataSetChanged() 2.Recy
不知道大家有没有遇到这种情况,当你通过点击事件改变第一个item的状态之后,向下滑动到第10个item也会同样触发,然后第19、28、37、46等等,每间隔9个item就会重复之前的操作后的显示状态。 如何解决呢?其实就是一个缓存的问题,只要在recyclerview相应的activity或者fragment设置 recyclerview.setItemViewCacheSize(int) in...
当我们回到手机桌面的时候,调用onStop()。当Activity回到前台时,就会调用onRestart()->onStart(); 所以如果你是再onStart()里面加载数据就有可能出现这种情况。 方法一:我们可以在onCreate()里面加载数据,就可以解...
当我们通过用户交互改变recyclerview的某个item的显示状态后,向下滑动会发现每间隔9个item就会出现同样的显示状态,但是我们明明没有对这个item进行操作,为什么会自动显示操作后的状态呢? 其实原因很简单,因为缓存问题 只要在相应的activity或者fragment添加下面一行代码 mrecyclerview.setItemViewCacheSize(500) 其500是想要缓存的item数量,可以根据自己的需要进行调整。
在平时写代码会经常碰到要点击ListView item 修改item的布局这种做法。 我在做这个功能的时候碰到一个问题。 点击item 布局修改UI 改变的总是最后一个item的UI。 然后就一步一步排查。1.ViewHoder不要是全局变量 2.不要在getview直接修改布局。 直接贴代码吧。思路就是在onclick事件改变数据通过回调刷新Ui。 Java: private Str
在写RecyclerVIew的适配器时,我们都要重写一个方法onBindViewHolder,这个方法在何时会执行呢?只要有一个item进入了手机界面(只要有item进入了手机界面,那么就会被加载),那么这个方法就会执行,对于的position就是这个item的下标。 比如现在有界面14个测试item 我们在onBindViewHolder方法通过Log.d输出以下内容 Log.d("onBindViewHolder","item"+position); 可以看到确实输出了14个item.