android 浅析RecyclerView回收复用机制及实战(仿探探效果)
Posted android超级兵
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 浅析RecyclerView回收复用机制及实战(仿探探效果)相关的知识,希望对你有一定的参考价值。
android 浅析RecyclerView回收复用机制及实战,仿探探效果
还是老套路,先来看看实现的效果!
浅析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);
}
}
}
接着这里会执行到RecyclerView
的removeAndRecycleViewAt()
方法
#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 = 0以上是关于android 浅析RecyclerView回收复用机制及实战(仿探探效果)的主要内容,如果未能解决你的问题,请参考以下文章