fragment的onCreateView中设置RecyclerView adapter概率不生效

一般的,我们的RecyclerView是用来展示网络(后台)数据。遇到的情况,都是这样的:
先初始化好RecyclerView,然后给他绑定好LayoutManager,然后进行网络请求,最后再到UI线程去设置Adapter。
这么一套下来,很少遇到setAdapter导致的不显示问题。

对于这种常规的RecyclerView不显示的原因主要有3个:

  1. 忘记设置LayoutManager,比如:
recyclerview.setLayoutManager(LinearLayoutManager(this));
  1. 有些场景变化数据需要调用notifyDataSetChanged(), 但是如果你是调用setAdapter重新设置,其实是不需要Notify的。而且,如果DataList的地址没有变化也可能是不生效的。现在一般都使用增量更新。
    参考如下:
        val insertIndex = datas.count() //现有数据长度
        datas.addAll(insertIndex, addedDatas) //增量
        if (notify) {
            notifyItemRangeInserted(insertIndex, data.count()) //通知增量更新
        }
  1. 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又存在一些可能被吞掉的问题。