写在前面:本文适合熟练使用过RecyclerView,或者正在使用RecyclerView实现需求的开发人员阅读,没用过的话,读起来会比较难理解。
首先背景介绍,我用RecyclerView,在对应Adapter的 onCreateViewHolder会做一些初始化操作,代码如下,对添加进RecyclerView的每一个Item做客制化,所以每次添加新Item,这里的初始化操作对我的需求来说必不可少。
@NonNull
@Override
public T onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
myparent = parent;
Log.d("MyNotificationService", " 走到onCreateViewHolder");
Log.d("notification_xu_su ", "4、 onCreateViewHolder " + String.valueOf(viewType));
if(list.size() != 0) {
notification_center = LayoutInflater.from(mycontext).inflate(R.layout.notification_center, myparent, false);
center_viewHolder = new Center_ViewHolder(notification_center);
StaticVariableUtils.onCreate_To_onBind = true;
return (T) center_viewHolder;
}
return null;
}
在我的理解中,不管是往RecyclerView中插入一个Item,还是从0开始往RecyclerView中添加Item,函数调用顺序都应是这样:先调用onCreateViewHolder ,之后调用onBindViewHolder 。 但是事实却不是这样,有些情况下会没有调用到onCreateViewHolder 导致我的代码异常,下面分两种异常情况来说明:
1、清空RecyclerView之后,从0添加新Item,不会调用onCreateViewHolder
我用下面的代码清空RecyclerView:
STATIC_INSTANCE_UTILS.notificationCenterAdapter.list.clear();
STATIC_INSTANCE_UTILS.notificationCenterAdapter.notifyDataSetChanged();
这是网上大多推荐的方法,但是后续如果再往RecyclerView中添加新Item,不会调用到onCreateViewHolder,我自己推测是RecyclerView 中对于列表大小的缓存没有清掉导致。
正确清空RecyclerView的操作如下:
if (StaticVariableUtils.recyclerView.getChildCount() > 0 ) {
StaticVariableUtils.recyclerView.removeAllViews();
StaticVariableUtils.recyclerView.getRecycledViewPool().clear();//清除缓存
STATIC_INSTANCE_UTILS.notificationCenterAdapter.list.clear();
STATIC_INSTANCE_UTILS.notificationCenterAdapter.notifyDataSetChanged();
}
2、调用 notifyItemInserted 方法往RecyclerView 的已有列表中插入一个Item
注意,这里不是往RecyclerView末尾插值,末尾插值是不会出错的。我调用如下方法往某个postion插值:
private void unfold(Center_ViewHolder center_viewHolder) {
int mypostion = list.indexOf(center_viewHolder.notification_item);
for(String string : list.get(list.indexOf(center_viewHolder.notification_item)).multiple_content) {
//mypostion后面添加multiple_content个Item
Log.d(TAG," unfold展开 "+String.valueOf(mypostion));
Notification_Item notification_item = new Notification_Item();
notification_item.appName = list.get(mypostion).appName;
notification_item.Icon = list.get(mypostion).Icon;
notification_item.content = string;
list.add(mypostion ,notification_item);
notifyItemInserted(mypostion);
}
}
结果插值操作直接执行onBindViewHolder,跳过了onCreateViewHolder。针对这个问题,我各种网搜了一下,得到的最初结论如下:
//notification_center_adapter.notifyItemInserted(list.size() - 1);
//注意,notifyItemInserted有个比较恶心的特性:插入的位置在list末尾会按照 onCreateViewHolder ——> onBindViewHolder 的顺序调用
//插入的位置如果在list中间,则会直接调用onBindViewHolder
//
其实这种结论也是不正确的,因为我发现有些时候插在列表中间也会调用到onCreateViewHolder,正确的结论如下:
按照 onCreateViewHolder ——> onBindViewHolder
这个顺序调用的前提是,每次新增Item之后,整个list的大小都得比上一次大,如果小于或者等于上一次的大小,notifyItemInserted
方法会直接调用onBindViewHolder ,复用之前的RecyclerView.ViewHolder。举个例子,你先删除三条Item,又插入三条Item,不管你是在list中间、开头或者是末尾插入,onCreateViewHolder 都不会被调用到,因为前后两次list的size比较起来没有变大。而且 RecyclerView缓存中记录的list大小,应该是RecyclerView曾经到达过的最大值(这里没看过源码,存粹从使用表现上来看),举个例子,如果你往RecyclerView中曾经最多插入了20个Item,并且没有清理过缓存,那么就是说只有你往RecyclerView中插入第21个值才会触发调用onCreateViewHolder。
那么问题来了,怎么在list没有增大的时候,进行插值操作,并且调用到onCreateViewHolder呢?答案和上述第一点清空异常的解决方法一样,notifyItemInserted之前,先清空RecyclerView的缓存。
修改方法如下:
private void unfold(Center_ViewHolder center_viewHolder) {
int mypostion = list.indexOf(center_viewHolder.notification_item);
for(String string : list.get(list.indexOf(center_viewHolder.notification_item)).multiple_content) {
//mypostion后面添加multiple_content个Item
Log.d(TAG," unfold展开 "+String.valueOf(mypostion));
Notification_Item notification_item = new Notification_Item();
notification_item.appName = list.get(mypostion).appName;
notification_item.Icon = list.get(mypostion).Icon;
notification_item.content = string;
+ StaticVariableUtils.recyclerView.getRecycledViewPool().clear();
list.add(mypostion ,notification_item);
notifyItemInserted(mypostion);
}
}
StaticVariableUtils.recyclerView.getRecycledViewPool().clear();
写在末尾:如果你的需求每次都必须在onCreateViewHolder中初始化一些功能才行,上述的处理方式就很有用。网上没有解决此种问题的文章,我就想着有必要记录一下。