When something takes longer than expected, write it down.
问题产生:
最近做项目遇到RecyclerView使用databinding时,出现数据闪烁,老大说,这问题不解决就不要用databinding。。。
闪烁图效果如下,点击Refresh,数据闪了一次:
明明数据没变,但数据却刷了一遍。
代码如下:
class TestDBAdapter extends RecyclerView.Adapter<TestDBViewHolder> { private List<TestData> list; public TestDBAdapter(List<TestData> list) { this.list = list; } @Override public TestDBViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { ItemTestDbRecyclerViewBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_test_db_recycler_view, parent, false); return new TestDBViewHolder(binding); } @Override public void onBindViewHolder(TestDBViewHolder holder, int position) { holder.binding.setData(list.get(position)); // holder.binding.executePendingBindings(); } @Override public int getItemCount() { return list.size(); } } class TestDBViewHolder extends RecyclerView.ViewHolder { public ItemTestDbRecyclerViewBinding binding; public TestDBViewHolder(ItemTestDbRecyclerViewBinding binding) { super(binding.getRoot()); this.binding = binding; } public TestDBViewHolder(View itemView) { super(itemView); } } }
问题追踪:
从图中可以看到item从浙江-->广东-->浙江,这个简单,因为recycler view会重用item,刷新时,第一个元素使用了第四个元素的ViewHolder。
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { if (holder instanceof TaskItemViewHolder) { final TaskItemViewHolder taskItemViewHolder = (TaskItemViewHolder) holder; LogUtils.d("before: "+taskItemViewHolder.getBinding().getData());//打印bind前holder中的数据 taskItemViewHolder.bindTo(mList.get(position), position); LogUtils.d("after: "+mList.get(position)+" position:"+position);//打印当前item的数据 } }
打印结果:
before: 广东
after: 浙江
数据是肯定变了,难道数据闪烁是正常的,那为什么不用databinding时,没有出现数据闪烁现象?有人说全局刷新时layout重置。。。
网上不是有好多demo吗?去看看别人怎么用的,看看别人的代码是否有问题,于是下了个demo https://github.com/jredthree/AllRecyclerDemo, 安装,运行,refresh,卧槽,居然没有闪烁!!!马上追踪代码,activity->adapter->onCreateViewHolder->onBindViewHolder。。。嗯?多了一行? holder.binding.executePendingBindings();这是what?没遇到过。加上这一行后,问题居然解决了。。。
/**
* Evaluates the pending bindings, updating any Views that have expressions bound to
* modified variables. This <b>must</b> be run on the UI thread.
*/
bindData后立即刷新!
难道要立刻刷新?还没有搞清楚。。。
问题解决:
@Override public void onBindViewHolder(TestDBViewHolder holder, int position) { holder.binding.setData(list.get(position)); holder.binding.executePendingBindings();//加一行,问题解决 }
遗留问题:
1. RecyclerView notifyDataChanged时,明明item的数据变化了,但为什么没有闪烁呢?
2. executePendingBindings语句到底是什么用?调与不调有什么区别?