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語句到底是什么用?調與不調有什么區別?