RecycleView原理分析

在面试中经常会遇见面试RecycleView的,RecycleView源代码及其复杂,想要回答好真的很困难,下面我就说一下我的理解。
先说RecycleView的用法。
1、设置一个LayoutManager 用于设置布局相关。
2、设置Adapter 用于将布局和数据进行绑定。
3、可以设置显示动画(这个是非必选)。
4、可以设置RecycleView的间隔(这个也是非必选)。

RecycleView说到底也只是一个自定义ViewGroup。所以先看一下onMeasure()是如何测量的。
onMeasure
首先会判断是否传入LayoutManager,如果没有则直接测量自身大小,如果自身大小确定,则显示自身大小否则不会显示大小因为它没有测量子view。
如果大小确定直接调用mLayoutManager的onMeasure方法去测量RecycleView的高,否则dispatchLayoutStep2方法进行测量并且布局。
onLayout
在这里会调用dispathLayout方法,这个里面仍然会dispathLayoutStep2这个方法。
在dispathLayoutStep2会接着调用 mLayout.onLayoutChildren()方法,不同的LayoutManager有不同的onLayoutChildren方法,这里分析LinearLayoutManager的onLayoutChildren()方法。
onLayoutChildren方法会调用fill方法对子view布局测量,在fill里面会先调用layoutChunk方法对子view进行布局和测量,然后fill会计算剩余的空间。layoutChunk会从缓存里获取到一个view,然后测量这个itemview的大小,并且确定itemview位置。
onDraw
会调用itemDecoration的onDraw方法画item间隔。

关于RecycleView的缓存
RecycleView之所以这么厉害,Recycler了。
RV主要实现了ViewHolder的缓存以及复用。核心代码在Recycler中完成。
里面主要分为四个部分。
第一个部分是scrap部分,当调用notifyXXX相关的代码的时候会将当前屏幕的ViewHolder缓存到scrap List数组中。

第二部分cacheViews数组,默认是2个,如果数组满了,按照FIFO的规则移除ViewHolder,通常情况下刚移除屏幕的viewholder并不会马上删除。而是存在cacheViews数组中。

第三部分ViewCacheExtsension 自定义的缓存策略一般用的比较少,也不建议用。

第四部分RecycleViewPool 也是缓存屏幕外的ViewHolder,当CachedView数据已满,则会将CachedView抛弃的ViewHolder交给RecycleViewPool,但是ViewHolder的数据会被删除,再次调用的话需要调用onBinderViewHolder绑定数据,这和ListView使用ViewHolder复用convertView道理一致。

取出缓存
从Recycler里面按照顺序先获取Scrap里面的缓存,之后是cacheview里面的 在之后是cacheViewExtension里面的,最后是从RecycleViewPool里获取数据。如果都获取不到,则会调用Adapter的createViewHolder()方法生成一个ViewHolder。

当然也可以自定义ItemView的动画,以及添加ItemDecortion每一个item的间隔。

 rv.addItemDecoration(new RecyclerView.ItemDecoration() {
            private int mDividerHeight = 10;
            @Override
            public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
                super.onDraw(c, parent, state);
                int childCount = parent.getChildCount();
                for ( int i = 0; i < childCount; i++ ) {
                    View view = parent.getChildAt(i);

                    int index = parent.getChildAdapterPosition(view);
                    //第一个ItemView不需要绘制
                    if ( index == 0 ) {
                        continue;
                    }

                    float dividerTop = view.getTop() - mDividerHeight;
                    float dividerLeft = parent.getPaddingLeft();
                    float dividerBottom = view.getTop();
                    float dividerRight = parent.getWidth() - parent.getPaddingRight();

                    c.drawRect(dividerLeft,dividerTop,dividerRight,dividerBottom,mPaint);
                }
            }

            /**
             *
             * @param outRect
             * @param view
             * @param parent
             * @param state
             *
             * 可以通过 outRect.set(l,t,r,b)  来指定 itemview的paddingLeft,paddingTop, paddingRight, paddingBottom
             */
            @Override
            public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
                // TODO Auto-generated method stub
                super.getItemOffsets(outRect, view, parent, state);

                //第一个ItemView不需要在上面绘制分割线
                if (parent.getChildAdapterPosition(view) != 0){
                    //这里直接硬编码为1px
                    outRect.top = mDividerHeight;
                }
            }
        });

        //添加动画,我们可以模拟DefaultItemAnimator 定义自己的动画
        rv.setItemAnimator(new DefaultItemAnimator());
        findViewById(R.id.btn_add).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                list.add(2,"add");
                rvAdapter.notifyItemInserted(2);
            }
        });

        findViewById(R.id.btn_delete).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                list.remove(2);
                rvAdapter.notifyItemRemoved(2);
            }
        });

你可能感兴趣的:(RecycleView)