列表的数据更新,通常有两种更新事件,一种是item change event,条目改变事件,就是item中有数据变化了;一种是structural change event,结构改变事件,即插入、删除或者移动了一些item。
这些变化在RecycleView.Adapter中就可以单独更新,提高效率,也更方便一点。RecyclerView.Adapter中有多个notify方法,用户可以有选择的使用。
在列表显示中,可以分为两个部分,一个是list,是数据的存储信息,我们从list取得要显示的数据然后显示出来;另一个是items,是已经在屏幕中显示出来的条目信息。通常,我们在 onBindViewHolder方法里面去绑定
一、
RecyclerView.Adapter的notify主要有以下几个:
1.
void notifyDataSetChanged ()
notifyDataSetChanged是与BaseAdapter中的更新方法相同的。通知用户有数据都改变了,但是并未指定改变的内容,所以我们只能对所有的数据进行更新,包括item和structural 。
2.
void notifyItemChanged (int position) //单形参
void notifyItemChanged (int position, Object payload) // //两个形参
notifyItemChanged属于item change event。通知用户某一项item中的数据改变了,要进行更新,更新之后,索引不变。关于payload的用法,请往下看。
以下四个调用时等价的:
notifyItemchange(position) == notifyItemchange(position,null)
== notifyItemRangechange(position,1) == notifyItemRangechange(position,1,null)
这是因为在源码中,notifyItemchange是调用notifyItemRangechange来实现的。
3.
void notifyItemInserted (int position)
notifyItemInserted属于structural change event。这个方法通知用户有新的item插入进来了,并且由动画,可以使用默认的,也可以自定义。position位置上的是新插入的item,而从原来的position位置开始往后的item,索引全部都+1.
用法一般如下:
list.add(i, data); //list的插入
adapter.notifyItemInserted(i); //item的插入,但是i后面的item的position没有变
adapter.notifyItemRangeChanged(i, adapter.getItemCount()-i); // 更新从i开始的item的position
为什么要刷新position为2以后的数据呢?因为,在position为2的位置插入了一条数据后,新数据的position变成了2,那原来的position为2的应该变成了3,3的应该变成了4,所以2以后的所有数据的position都发生了改变,所以需要把position2以后的数据都要刷新。
如果这里使用notifyDataSetChanged()来刷新屏幕上显示的所有item可以吗?结果不会出错,但是会有一个问题,前面调用了notifyItemInserted()方法后会在执行动画,如果你调用notifyDataSetChanged()刷新屏幕上显示的所有item的话,必然也会刷新当前正在执行动画的那个item,这样导致的结果是,前面的动画还没执行完,它马上又被刷新了,动画就看不见了。所以只要刷新2以后的item就可以了。
(这段摘自安卓笔记侠-RecyclerView中notifyDataSetChanged刷新总结)
4.
void notifyItemMoved (int fromPosition, int toPosition)
属于structural change event。用于item移动的通知。item从fromPosition位置挪到了toPosition位置。
说明一点,moved不是将两个数据交换,而是将其中一个fromPosition位置的依次向toPosition的方向挪,直到挪到toPosition为止。调用notifyItemMoved方法后,fromPosition和toPosition之间的item的position全部都发生了变化。可以用下面的方法刷新:
if (toPosition == fromPosition)return;
notifyItemMoved(fromPosition, toPosition);
notifyItemRangeChanged(Math.min(fromPosition, toPosition), Math.abs(fromPosition - toPosition) +1);
5.
void notifyItemRemoved (int position)
属于structural change event。通知用户有在position位置上的item被删除了。后面的item索引全部-1。
用法一般为:
public void remove(int position) {
mDataList.remove(position); //数组长度-1,但是item的长度还没有变
notifyItemRemoved(position); // 会有删除的动画效果,但是没有调用onBindViewHolder方法没有调用,position没有刷新
notifyItemRangeChanged(position, mDataList.size() - position); //对position进行刷新
}
6.
void notifyItemRangeChanged (int positionStart, int itemCount, Object payload)
void notifyItemRangeChanged (int positionStart, int itemCount)
属于item change event,并且是一个范围性的更新。从positionStart开始,有itemCount个item需要更新。更新之后,索引不变。这里的刷新,是直接调用onBindViewHolder方法,对范围内的每个item的position进行了刷新。关于payload的用法,请往下看。
以下调用等价:
notifyItemRangechange(position,1) == notifyItemRangechange(position,1,null)
7.
void notifyItemRangeInserted (int positionStart, int itemCount)
属于structural change event,并且是一个范围性的更新。这个方法通知用户有itemCount个新的item插入进来了。position位置上的是新插入的item,而从原来的position位置开始往后的item,索引全部都+itemCount。
8.
void notifyItemRangeRemoved (int positionStart, int itemCount)
属于structural change event,并且是一个范围性的更新。通知用户有itemCount个item被删除了。后面的item索引全部-itemCount。
二、
上面的方法中,有的方法含有payload参数。用法详解请参考云在千峰-使用 Payload 提高 RecyclerView 渲染效率
简单来说,就是进一步的细化了更新的部分,提高了效率。
上面的方法,当payload为null时,都是在更新整个item,系统回调
void onBindViewHolder(@NonNull ViewHolder viewHolder, int position)
方法进行item的更新;当payload不为null时(只要不为null就行,可以是任何值),系统会回调
void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads)
方法,在这个方法中,我们就可以通过判断payload是否为空,有选择的对item中的任何控件进行更新。
具体用法如下:
@Override
public void onBindViewHolder(ViewHolder holder, int position, List<Object> payloads) {
if (payloads.isEmpty()) {
// payloads 为 空,说明是更新整个 ViewHolder
onBindViewHolder(holder, position);
} else {
// payloads 不为空,这只更新需要更新的 View 即可。
ViewHolder viewHolder = (ViewHolder) holder;
holder.tv.setText("Hello World!");
}
}