前言:在学习文章“RecyclerView调用notifyDataSetChanged()不起作用”时对作者最后的分析没看懂。后面研究了一下终于搞懂了,所以以自己的理解方式进行补充一下。
Recyclerview和Adapter之间采用了观察者模式,Adapter是被观察者Observable, Recyclerview是观察者Observer。
在适配器基类RecyclerView.Adapter类中有一个已初始化的Observable对象:
private final AdapterDataObservable mObservable = new AdapterDataObservable();
在Recyclerview类中有一个已初始化Observer对象:
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
Recyclerview通过setAdapter()方法来使得观察者和被观察者产生联系:
public void setAdapter(@Nullable Adapter adapter) {
// bail out if layout is frozen
setLayoutFrozen(false);
setAdapterInternal(adapter, false, true);
processDataSetCompletelyChanged(false);
requestLayout();
}
private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews) {
...
adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this);
...
}
下面讲下为什么Adapter对象有时候调用notifyDataSetChanged()后数据没有生效呢?其实一句话概括:Adapter中的数据引用对象A和外部的数据对象B不是一个对象。当我们在外部改变了B的引用后,A指向的还是之前的数据对象,B指向的是新数据对象。但是Adapter上绑定的是A而不是B,所以B变化了,再调用notifyDataSetChanged()不会有作用。
文字不好描述,我们来看代码就一目了然了。
Adapter类
public class testRecyclerviewAdapter1 extends RecyclerView.Adapter<testRecyclerviewAdapter1.MyViewHolder> {
private List<String> mDatas;
public testRecyclerviewAdapter1(Context context, List<String> mDatas) {
this.mContext = context;
this.mDatas = mDatas;
}
...
}
Acitivity类
class TestRecyclerviewActivity : AppCompatActivity() {
private var mDatas: MutableList<String>? = null
private var mDatas2: MutableList<String>? = null
private var mRecyclerView: RecyclerView? = null
override fun onCreate(savedInstanceState: Bundle?) {
...
mAdapter = testRecyclerviewAdapter1(this, mDatas)
mRecyclerView?.setAdapter(mAdapter)
swipe.setOnRefreshListener {
initData2()
mDatas = mDatas2
mAdapter?.notifyDataSetChanged()
swipe.setRefreshing(false)
}
}
private fun initData() {
mDatas = ArrayList()
var i = 'A'.toInt()
while (i < 'F'.toInt()) {
mDatas?.add("" + i.toChar())
i++
}
}
private fun initData2() {
mDatas2 = ArrayList()
var i = 1
while (i < 5) {
mDatas2?.add("$i")
i++
}
}
}
1、注意Adapter的构造函数,Adapter内部定义了一个数据对象mDatas,在构造函数中把外部Activity传入的数据对象赋值给Adapter的mDatas对象,这里是地址赋值,所以这两个对象目前指向的堆内存是一致的。
public testRecyclerviewAdapter1(Context context, List<String> mDatas) {
this.mContext = context;
this.mDatas = mDatas;
}
2、在Activity中也有一个数据对象mDatas。假如我们尝试更改Activity中mDatas,然后调用adapter.notifyDataSetChanged()会发现Recyclerview没有发生任何变化。 这是因为你改变的是Activity中mDatas,但是Adapter实际用的却是Adapter自身的mDatas作为被观察者对象。在你把Activity中mDatas指向不同的数据对象时,Activity中mDatas和Adapter中mDatas其实已经不是一个对象了。所以Adapter中mDatas是没有变化的,调用adapter.notifyDataSetChanged()没有任何变化。
3、只有更改的是Adapter中mDatas指向的数据,再调用adapter.notifyDataSetChanged()才会有效果!!