fragment的onCreateView中设置RecyclerView adapter概率不生效
一般的,我们的RecyclerView是用来展示网络(后台)数据。遇到的情况,都是这样的:
先初始化好RecyclerView,然后给他绑定好LayoutManager,然后进行网络请求,最后再到UI线程去设置Adapter。
这么一套下来,很少遇到setAdapter导致的不显示问题。
对于这种常规的RecyclerView不显示的原因主要有3个:
- 忘记设置LayoutManager,比如:
recyclerview.setLayoutManager(LinearLayoutManager(this));
- 有些场景变化数据需要调用notifyDataSetChanged(), 但是如果你是调用setAdapter重新设置,其实是不需要Notify的。而且,如果DataList的地址没有变化也可能是不生效的。现在一般都使用增量更新。
参考如下:
val insertIndex = datas.count() //现有数据长度
datas.addAll(insertIndex, addedDatas) //增量
if (notify) {
notifyItemRangeInserted(insertIndex, data.count()) //通知增量更新
}
- createView方法错误
不要使用//错误 View view=View.inflate(parent.getContext(),R.layout.cell_normal,null); view1 = LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_card,parent,false);
而最近,我遇到一个问题,在Fragment的onCreteView里面去设置静态数据,绑定Adapter不生效。
这个原因就与常规的原因不同了。
这里涉及到起手渲染的问题。
public void setAdapter(@Nullable Adapter adapter) {
...
requestLayout();
}
@Override
public void requestLayout() {
if (mInterceptRequestLayoutDepth == 0 && !mLayoutSuppressed) {
super.requestLayout();
} else {
mLayoutWasDefered = true;
}
}
mInterceptRequestLayoutDepth,通过startInterceptRequestLayout()和stopInterceptRequestLayout()来成对出现控制++和–。
即,dispatchLayoutStep1,dispatchLayoutStep2,dispatchLayoutStep3三大步骤。
来源是归属于dispatchLayout()中的三个步骤。
进而分析来看:跟dispatchLayout()的完成执行,mFirstLayoutComplete置为true有很大关系。
private void dispatchLayoutStep1/2() {
...
mAdapter.getItemCount()
...
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
dispatchLayout();
mFirstLayoutComplete = true;
}
其实以上,并未能准确的分析到根本原因。从实践来看,
如果setAdapter,快于onLayout()。则有可能会出现,根本无法显示的情况。
而我曾经遇到过,通过LayoutInspector查看,布局已经存在,但是没有高度,即可能LayoutParams出现错误。
所以推测来讲,主要的原因是需要让RecyclerView走完onLayout才能确保LayoutParams能够被真实的测量和显示完成,根据onMeasure->onLayout->onDraw的顺序。
因此,最终我将逻辑放在onDraw里面,更为保险。
参考如下:
var isDrawn = -1
var initedAdapter = false
fun setAdapterWrap(adapter) {
postSetAdapter(adapter)
}
override fun onDraw(c: Canvas?) {
super.onDraw(c)
if(isDrawn == -1) isDrawn = 0
postSetAdapter()
}
private fun postSetAdapter() {
if (isDrawn == 0) {
isDrawn = 1
postMainHandler {
if (!initedAdapter) {
mRecyclerView.setAdapter(adapter)
initedAdapter = true
}
}
}
}
大概的逻辑就是,调用设置adapter的时候,确认是否已经onDraw过了。直到onDraw过才能设置。而且还要经历一次post。
另外,也建议使用全局的MainHandler来post,而不是调用RecyclerView自身的View的post。
因为View的post又存在一些可能被吞掉的问题。