android浅析recyclerview回收复用机制及实战(仿探探效果)(代码片段)

android超级兵 android超级兵     2023-01-29     503

关键词:

还是老套路,先来看看实现的效果!

浅析RecyclerView回收复用机制

在写这个效果之前,需要熟悉Rv的回收复用机制,因为实现这个效果,需要自定义LayoutManager()

众所周知,RecyclerView 是一个可滑动的View,那么他的回收/复用入口一定是在onTouchEvent()事件中

滑动过程中响应的是MotionEvent.ACTION_MOVE事件,所以直接来这里找找看!!

缓存机制-onTouchEvent()入口

#RecyclerView.java

 @Override
public boolean onTouchEvent(MotionEvent e) 

	final int action = e.getActionMasked();
	 switch (action) 
			........................................
           	........只展示代码思路,细节请自行查看........
           	........................................
        
            case MotionEvent.ACTION_MOVE: 
            if (mScrollState == SCROLL_STATE_DRAGGING) 
                    mLastTouchX = x - mScrollOffset[0];
                    mLastTouchY = y - mScrollOffset[1];

                    // 关键代码1
                    if (scrollByInternal(
                            canScrollHorizontally ? dx : 0,
                            canScrollVertically ? dy : 0,
                            vtev)) 
                        getParent().requestDisallowInterceptTouchEvent(true);
                    
                    if (mGapWorker != null && (dx != 0 || dy != 0)) 
                        mGapWorker.postFromTraversal(this, dx, dy);
                    
                
            
			break;
	

接着找scrollByInternal(int x, int y, MotionEvent ev)方法

#RecyclerView.java

boolean scrollByInternal(int x, int y, MotionEvent ev) 
	 if (mAdapter != null) 
            ........................................
           	........只展示代码思路,细节请自行查看........
           	........................................
            if (x != 0) 
            	// 关键代码2 去到 LinearLayoutManager 执行fill方法
                consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
                unconsumedX = x - consumedX;
            
            if (y != 0) 
                // 关键代码2 去到LinearLayoutManager 执行fill方法
                consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
                unconsumedY = y - consumedY;
            
        
        ....

现在走到了mLayout.scrollHorizontallyBy(x, mRecycler, mState);

接着去LinearLayoutManager() 中去找scrollHorizontallyBy() 方法

#LinearLayoutManager.java

	@Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
                                  RecyclerView.State state) 
        if (mOrientation == HORIZONTAL) 
            return 0;
        
        // 关键代码3
        return scrollBy(dy, recycler, state);
    

scrollBy()->

#LinearLayoutManager.java

 int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) 
  	 ........................................
     ........只展示代码思路,细节请自行查看........
     ........................................
	 final int consumed = mLayoutState.mScrollingOffset
                // 关键代码4
                + fill(recycler, mLayoutState, state, false);

接着找到fill()方法

#LinearLayoutManager.java

int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
             RecyclerView.State state, boolean stopOnFocusable) 
       
      
        if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) 
           
            // 关键代码19 缓存ViewHolder
            recycleByLayoutState(recycler, layoutState);
        

        // 循环调用
        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) 
            
           // 关键代码5 [用来4级复用]
            layoutChunk(recycler, state, layoutState, layoutChunkResult);
            
             	 ........................................
   				 ........只展示代码思路,细节请自行查看........
     			 ........................................
        
		
    

看到这里只需要记住以下两点即可:

  • recycleByLayoutState(recycler, layoutState); 缓存ViewHolder
  • layoutChunk(recycler, state, layoutState, layoutChunkResult); 四级复用

有人可能会问,这里为什么是四级?不是说的三级嘛?
其实三级和四级都无所谓,知识点是不会变的,只是层级越多,理解就越深刻,越细罢了

直接进入到缓存的代码:

#LinearLayoutManager.java

 private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) 
        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) 
            // 关键代码21 缓存底部
            recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
         else 
            // 关键代码20 缓存头部
            recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
        
    

这里如果是向下滑动,就会缓存头部那么就会执行到recycleViewsFromStart()

如果是向上滑动,就会缓存尾部那么就会执行到recycleViewsFromEnd()

recycleViewsFromStart()recycleViewsFromEnd() 随便点开一个看看,里面代码都差不多一样.

#LinearLayoutManager.java

 private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) 
       
        if (mShouldReverseLayout) 
            for (int i = childCount - 1; i >= 0; i--) 
            ...
                    // 关键代码22
                    recycleChildren(recycler, childCount - 1, i);
                    return;
            
         else 
            for (int i = 0; i < childCount; i++) 
            ...
                    // 关键代码23
                    recycleChildren(recycler, 0, i);
                    return;
            
        
    

这里无论走哪一个if() 都会走到recycleChildren()方法

#LinearLayoutManager.java

private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) 
        if (startIndex == endIndex) 
            return;
        
        if (endIndex > startIndex) 
            for (int i = endIndex - 1; i >= startIndex; i--) 
                // 移除View  关键代码23 [执行到RecyclerView.removeAndRecycleViewAt()]
                removeAndRecycleViewAt(i, recycler);
            
         else 
            for (int i = startIndex; i > endIndex; i--) 
                removeAndRecycleViewAt(i, recycler);
            
        
    

接着这里会执行到RecyclerViewremoveAndRecycleViewAt()方法

#RecyclerView.java

	    // 关键代码24
        public void removeAndRecycleViewAt(int index, Recycler recycler) 
            final View view = getChildAt(index);
            removeViewAt(index);
            // 关键代码25
            recycler.recycleView(view);
        

继续往下执行

#RecyclerView.java

 public void recycleView(View view) 
            .......
            ViewHolder holder = getChildViewHolderInt(view);
            // 缓存
            recycleViewHolderInternal(holder);
        

接着继续执行recycleViewHolderInternal()

#RecyclerView.java

void recycleViewHolderInternal(ViewHolder holder) 
            ........................................
            ........只展示代码思路,细节请自行查看........
            ........................................
             boolean cached = false;
             
            if (forceRecycle || holder.isRecyclable()) 
                // mViewCacheMax = 缓存的最大值 
                // mViewCacheMax = 2
                // 如果viewHolder是无效、未被移除、未被标记的
                if (mViewCacheMax > 0
                        && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
                        | ViewHolder.FLAG_REMOVED
                        | ViewHolder.FLAG_UPDATE
                        | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) 
                    int cachedViewSize = mCachedViews.size();

                    // 关键代码24
                    // mViewCacheMax = 2
                    if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) 
                        // 如果viewholder存满2个则移除第0个位置 
                        // 保证mCachedViews 最多能缓存2个ViewHolder
                        recycleCachedViewAt(0);
                        cachedViewSize--;
                    
                    ....
                    // 保存ViewHolder数据 [mCachedViews数据不会超过2个]
                    mCachedViews.add(targetCacheIndex, holder);
                    cached = true;
                
                 if (!cached) 
                    // 当ViewHolder不改变时候(只有一个ViewHolder) 就会直接存到缓存池中
                    addViewHolderToRecycledViewPool(holder, true);
                    recycled = true;
                
                ........................................
           		........只展示代码思路,细节请自行查看........
            	........................................
        

通过 关键代码24 可知,mCachedViews 最多能保存2个ViewHolder

如果第三个ViewHolder来临的时候,就会先删除掉第0个,然后在 mCachedViews.add(targetCacheIndex, holder);

然后再来看看 recycleCachedViewAt(0)的细节!

#RecyclerView.java

	void recycleCachedViewAt(int cachedViewIndex) 
            ...
            ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
            
            // 关键代码25
            // 添加到ViewPool到缓存里面取
            addViewHolderToRecycledViewPool(viewHolder, true);

            // 将第0个ViewHolder移除
            mCachedViews.remove(cachedViewIndex);
        

继续执行到 addViewHolderToRecycledViewPool()方法

mCachedViews.get(0)中的ViewHolder获取出来,添加到缓存池中,并删除

#RecyclerView.java

void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) 
            .....
            // 向缓存池中 保存ViewHolder 关键代码28
            getRecycledViewPool().putRecycledView(holder);
        

点进来看看putRecycledView()方法

#RecyclerView.java

// SparseArray 类似与 HashMap<int,ScrapData>
// 特点: key相同会保留最后一个,
//      会根据key的int值排序(从小到大)
SparseArray<ScrapData> mScrap = new SparseArray<>();

 public void putRecycledView(ViewHolder scrap) 
       // 获取ViewHolder布局类型
      final int viewType = scrap.getItemViewType();

      // 根据布局类型来获取ViewHolder
       final ArrayList scrapHeap = getScrapDataForType(viewType).mScrapHeap;

       // 判断缓存池的大小
       // mScrap.get(viewType).mMaxScrap 默认为 5
       // 同一种ViewType 只保存5个ViewHolder
        if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) 
           return;
        

       // 清空ViewHolder记录
        scrap.resetInternal();
        
        //add
        scrapHeap.add(scrap);

 // 清空ViewHolder记录
 void resetInternal() 
            mFlags = 0recyclerview缓存复用解析,源码解读(代码片段)

...如何工作的2.源码时序图和解读2.1缓存回收LinearLayoutManagerRecyclerView2.2缓存复用LinearLayoutManagerRecyclerView关于mChangedScrap2.3回收池结构时序图代码参考材料RecyclerView有四级缓存,其中一级缓存是用户自定义的缓存,四级缓存本... 查看详情

recyclerview缓存复用解析,源码解读(代码片段)

...如何工作的2.源码时序图和解读2.1缓存回收LinearLayoutManagerRecyclerView2.2缓存复用LinearLayoutManagerRecyclerView关于mChangedScrap2.3回收池结构附:时序图代码参考材料RecyclerView有四级缓存,其中一级缓存是用户自定义的缓存,四... 查看详情

recyclerview的缓存机制(代码片段)

原文链接原文出处:基于滑动场景解析RecyclerView的回收复用机制原理前言之前优化Feed流框架的时候,看到过一篇基于滑动场景分析RecyclerView回收复用的文章,感觉十分经典,因此转载复习之。正题RecyclerView的回收... 查看详情

recyclerview使用大全

...recylerview即回收view也可以看出。官方对于它的介绍则是:RecyclerView是ListView的升级版本,更加先进和灵活。RecyclerView通过设置LayoutManager,ItemDeco 查看详情

listview与recyclerview对比浅析——缓存机制

https://www.jianshu.com/p/193fb966e954 一,背景RecyclerView是谷歌官方出的一个用于大量数据展示的新控件,可以用来代替传统的ListView,更加强大和灵活。最近,自己负责的业务,也遇到这样的一个问题,关于是否要将ListView替换为Rec... 查看详情

recyclerview的复用缓存机制

...分析缓存复用机制时,先去缓存与复用的时机在哪里。在RecyclerView滑动时,item会显示出来。所以首先先想到在onTouchEvent()中ACTION_MOVE事件里做了什么事。接着又到scrollStep(x,y,mReusableIntPair)->mLayout.scrollVerticallyBy(dy,mRecycler,mState)->... 查看详情

这recyclerview的特效,谁看了不说6(代码片段)

...制,因为实现这个效果,需要自定义LayoutManager()…众所周知,RecyclerView是一个可滑动的View,那么他的回收/复用入口一定是在onTouchEvent()事件中滑动过程中响应的是MotionEvent.ACTION_MOVE事件,所以直接来这里找找看!!缓存机制onTouchEvent()入口... 查看详情

时光轴二之recyclerview版时光轴效果

因为现在RecyclerView是support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,同样拥有item回收复用的功能,但是直接把viewholder的实现封装起来,用户只要实现自己的viewholder就可以了,该组件会自动帮你回收复用每... 查看详情

时光轴二之recyclerview版时光轴效果

由于如今RecyclerView是support-v7包中的新组件,是一个强大的滑动组件。与经典的ListView相比,相同拥有item回收复用的功能,可是直接把viewholder的实现封装起来,用户仅仅要实现自己的viewholder就能够了,该组件会自己主动帮你回收... 查看详情

recyclerview使用大全

转载请注明出处:http://blog.csdn.net/lowprofile_coding/article/details/54098101RecylerView介绍RecylerView是support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,同样拥有item回收复用的功能,这一点从它的名字recylerview即回收view也... 查看详情

android踩坑日记:recyclerview中edittext和imageview的viewholder复用坑

RecyclerView中EditText和ImageView的ViewHolder复用坑RecyclerView作为ListView的升级版,目前来讲讲开发过程遇到的坑。RecyclerView中使用EditText滚动后数据消失,错乱场景:RecyclerView中的每个Item的ViewHolder布局中为都有EditText控件... 查看详情

recyclerview的使用

什么是RecyclerViewRecyclerView是Android5.0materialsdesign中的组件之一,相应的还有CardView、Palette等。看名字我们就能看出一点端倪,没错,它主要的特点就是复用。我们知道,Listview中的Adapter中可以实现ViewHolder的复用。RecyclerView提供了... 查看详情

android自定义recyclerview分割线,打造无边缘分割线

前言: 现在的RecyclerView几乎已经完全取代ListView和GridView了,已经几年没使用ListView和GridView了,想当年还需要自己在getView方法中复用convertView。而现在的RecyclerView一出生就被设计成convertView复用的,尽管你不想复... 查看详情

android5.x新特性详解——列表与卡片

RecyclerView在Android5.X中将使用了很久的ListView做了升级,增加了一个使用更方便、效率更高的控件——RecyclerView。RecyclerView是support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,它同样拥有item回收... 查看详情

Android 电视不显示 Recyclerview

】Android电视不显示Recyclerview【英文标题】:AndroidTvnotdisplayingRecyclerview【发布时间】:2020-05-2819:46:29【问题描述】:我在我的androidtv应用程序中创建了一个回收站视图,但它没有显示任何内容,我这样做就像我为移动应用程序所... 查看详情

用十张图帮你解析recyclerview的缓存复用机制了~(代码片段)

ViewPager2是在RecyclerView的基础上构建而成的,意味着其可以复用RecyclerView对象的绝大部分特性,比如缓存复用机制等。本篇的主要目的是快速普及必要的前置知识,而内容的核心,正是前面所提到的RecyclerView的缓存... 查看详情

recyclerview使用大全

...erview即回收view也可以看出。官方对于它的介绍则是:RecyclerView是ListView的升级版本,更加先进和灵活。RecyclerView通过设置LayoutManager,ItemDecoration,ItemAnimator实现你想要的效果。使用LayoutManager来确定每一个item的排... 查看详情

Android RecyclerView 项目动态布局

】AndroidRecyclerView项目动态布局【英文标题】:AndroidRecyclerViewitemdynamiclayout【发布时间】:2020-08-1311:21:02【问题描述】:在我的回收器视图项目布局中,我想添加类似水平回收器视图布局的东西,因为我不知道我需要多少个ImageVie... 查看详情