由NotifyDateSetChanged无法生效产生的思考
最近在使用notifyDataSetChanged方法时,发现数据并没有按照想象中的刷新,经过查看文档以及搜索,得知了产生这种现象的原因。
if (mCountDownAdapter == null) {
mCountDowns = LitePal.findAll(CountDown.class);
mCountDownAdapter = new CountDownAdapter(mCountDowns);
mRecyclerView.setAdapter(mCountDownAdapter);
}
首先Litepal.findAll方法最终调用了如下onFindAll方法
public <T> List<T> onFindAll(Class<T> modelClass, boolean isEager, long... ids) {
List<T> dataList;
if (isAffectAllLines(ids)) {
dataList = query(modelClass, null, null, null, null, null, "id", null,
getForeignKeyAssociations(modelClass.getName(), isEager));
} else {
dataList = query(modelClass, null, getWhereOfIdsWithOr(ids), null, null, null, "id",
null, getForeignKeyAssociations(modelClass.getName(), isEager));
}
return dataList;
}
可以看出Litepal.findAll最终返回的List是根据查询的数据添加到List中返回的,也就是说如果数据库发生了变化,那么这个List是不会变化的。这就为我们理解RecycleView为什么不能正确刷新打下了基础。
mCountDownAdapter = new CountDownAdapter(mCountDowns);
在这里,Adapter是和mCountDowns连接起来的,mCountdowns就是返回的List。因此在Adapter没有重建的时候,它绑定的都是mCountdowns列表。
在用户输入数据的时候,我是进行下面处理的:
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
mCountDown.setName(s.toString());
mCountDown.save(); //将CountDown实例产生的变化存储到数据库中
}
因此,我处理数据变化是方式是直接在数据库中进行修改,而与Adapter绑定的List却不会发生变化。所以Adapter调用NotifyDateSetChanged的时候就不会刷新数据了,因为其绑定的List数据没有刷新。
因此,有两个方法可以解决这个问题,一种是在修改数据到数据库的同时,对Adapter相关联的List也进行修改。一种是将关联的List重新清空,获取数据。从对性能的损失上来看,第一种方法较好。
有时候修改的只是一个item中的数据,而调用NotifyDateSetChanged的时候确实把全部可见项的数据都刷新了,造成了不必要的性能损失。
使用NotifyItemChanged(int position)方法就能够很好地避免这种损失,可以创建一个全局变量来储存点击的位置。
@Override
public void onClick(View v) {
int countdown_id = mCountDown.getId();
Intent intent = DetailActivity.newIntent(getActivity(), countdown_id);
startActivity(intent);
position = getAdapterPosition(); //获取该位置
}
然而到这里还不算完成,NotifyItemChanged(int position)方法刷新的是这个item中所有的View,不管其有没有被修改。这就导致一个问题,如果这个item中存在很多耗时的动画图片等,刷新这些view就会耗费大量的时间。
举个例子,我刷微博的时候,看到一个博主发的gif太搞笑了,点了一个赞,如果整个item都刷新的话,这个gif也要重新刷新,这就太浪费时间了。
因此使用notifyItemChanged(int position, Object payloads)方法,首先需要重写如下方法
@Override
public void onBindViewHolder(ViewHolder holder, int position, List<Object> payloads) {
//因为payloads为空时,默认刷新item中全部的view,所以需要这样写
if(!payloads.isEmpty()) {
if (payloads.get(0) instanceof Integer) {
holder.tvScore.setText(String.valueOf((Integer)payloads.get(0)))
}
}else {
super.onBindViewHolder(holder,position, payloads);
}
}
最后在需要刷新数据的时候调用notifyItemChanged(int position, Object payloads)就行了。