ListView 能局部刷新吗?
可以。。。
和ListView 的区别? 强在哪里?
更加灵活
怎么布局的? 怎么就实现了可以列表 和 网格的切换?
是怎样的回收和重复利用View?
不管recycleView 是何方神圣,最终都会调用到onMeasure、onLayout
androidx.recyclerview.widget.LinearLayoutManager#fill
1.从mAttachedScrap 里面获取
mAttachedScrap 好像没有被使用
真的没有看到这个的作用在哪里。好像没有用。
2.mCachedViews 获取
用于存放最常用的3个,包括将要显示的那个,预加载的,以及刚回收的
GapWorker 预创建View 完成之后就会放到cached 里面
"main"@12,125 in group "main": RUNNING
recycleViewHolderInternal(RecyclerView$ViewHolder):6487, RecyclerView$Recycler {androidx.recyclerview.widget}, RecyclerView.java
recycleView(View):6369, RecyclerView$Recycler {androidx.recyclerview.widget}, RecyclerView.java
prefetchPositionWithDeadline(RecyclerView, int, long):295, GapWorker {androidx.recyclerview.widget}, GapWorker.java
flushTaskWithDeadline(GapWorker$Task, long):345, GapWorker {androidx.recyclerview.widget}, GapWorker.java
flushTasksWithDeadline(long):361, GapWorker {androidx.recyclerview.widget}, GapWorker.java
prefetch(long):368, GapWorker {androidx.recyclerview.widget}, GapWorker.java
run():399, GapWorker {androidx.recyclerview.widget}, GapWorker.java
handleCallback(Message):873, Handler {android.os}, Handler.java
dispatchMessage(Message):99, Handler {android.os}, Handler.java
loop():193, Looper {android.os}, Looper.java
3.mRecyclerPool 获取
用于存在每一种类型的,5个。和CacheView 作用不太一样。
4.ViewCacheExtension获取
为什么做这么多层级的缓存?意义在哪里?
难道是这一层级的,不需要绑定数据?可以直接用?
5.GapWorker 到底是干什么用的? 怎么干的?
6.绑定ViewHolder之后什么时候添加到View树上的?
androidx.recyclerview.widget.LinearLayoutManager#layoutChunk
里面会调用addView
7.怎么实现的?滑动就增加view? 怎么实现这么流畅的?
onMeasure,onLayout 不会调用吗?(不会)
为什么?
父容器RecycleView 是不会重新调用onMeasure,onLayout的,但是会调用item 的 onMeasure 和 on Layout.
androidx.recyclerview.widget.LinearLayoutManager#layoutChunk
但是不调用invalidate 会刷新吗?
他不会去刷新layout。
8.requestLayout 和 invalidate 什么区别? 分别什么时候调用比较好?
invalidate 会导致重新布局吗?
requestLayout 会导致 onDraw 调用吗?
//invalidate 不会重新onLayout onMeasure
// invalidate();
// 重新布局 onMeasure onLayout onDraw 都会重新调用
requestLayout();
9.view 怎么 移动的 ? 肯定不是scroll, 应该是调用layout方法。
offsetTopAndBottom(int):15964, View {android.view}, View.java
offsetChildrenVertical(int):5042, RecyclerView {androidx.recyclerview.widget}, RecyclerView.java
offsetChildrenVertical(int):9136, RecyclerView$LayoutManager {androidx.recyclerview.widget}, RecyclerView.java
offsetChildren(int):369, OrientationHelper$2 {androidx.recyclerview.widget}, OrientationHelper.java
scrollBy(int, RecyclerView$Recycler, RecyclerView$State):1399, LinearLayoutManager {androidx.recyclerview.widget}, LinearLayoutManager.java
scrollVerticallyBy(int, RecyclerView$Recycler, RecyclerView$State):1128, LinearLayoutManager {androidx.recyclerview.widget}, LinearLayoutManager.java
scrollStep(int, int, int[]):1841, RecyclerView {androidx.recyclerview.widget}, RecyclerView.java
scrollByInternal(int, int, MotionEvent):1940, RecyclerView {androidx.recyclerview.widget}, RecyclerView.java
调用的offsetTopAndBottom 实现移动。调用requestLayout 其实会更改mTop 的,但是recycleView 没有问题是因为重写了onMeasure 方法,不会重新给mTop赋值。
10.offsetTopAndBottom 有什么优点?有什么作用?
快速移动? 不需要重新布局?只移动自己,不需要麻烦parent.
11.ainamator是怎么实现的?
12.具体的recycleView 的机制是什么?会提前加载上下一个列表项目吗?
13.滑动的时候,是先回收view还是先创建view 添加到底部?
首先,回收view, 然后添加下面的view,最后再回收view.
androidx.recyclerview.widget.RecyclerView#scrollBy
fill(RecyclerView$Recycler, LinearLayoutManager$LayoutState, RecyclerView$State, boolean):1576, LinearLayoutManager {androidx.recyclerview.widget}, LinearLayoutManager.java
scrollBy(int, RecyclerView$Recycler, RecyclerView$State):1391, LinearLayoutManager {androidx.recyclerview.widget}, LinearLayoutManager.java
scrollVerticallyBy(int, RecyclerView$Recycler, RecyclerView$State):1128, LinearLayoutManager {androidx.recyclerview.widget}, LinearLayoutManager.java
scrollStep(int, int, int[]):1841, RecyclerView {androidx.recyclerview.widget}, RecyclerView.java
scrollByInternal(int, int, MotionEvent):1940, RecyclerView {androidx.recyclerview.widget}, RecyclerView.java
onTouchEvent(MotionEvent):3391, RecyclerView {androidx.recyclerview.widget}, RecyclerView.java
static class LayoutState {
static final String TAG = "LLM#LayoutState";
static final int LAYOUT_START = -1;
static final int LAYOUT_END = 1;
static final int INVALID_LAYOUT = Integer.MIN_VALUE;
static final int ITEM_DIRECTION_HEAD = -1;
static final int ITEM_DIRECTION_TAIL = 1;
static final int SCROLLING_OFFSET_NaN = Integer.MIN_VALUE;
/**
* We may not want to recycle children in some cases (e.g. layout)
*/
boolean mRecycle = true;
/**
* Pixel offset where layout should start
* 应该接着哪里布局,接着那里添加view
*/
int mOffset;
/**
* Number of pixels that we should fill, in the layout direction.
* 当前需要填充的长度
* 可以为负数 如果是负数 说明滑动的距离 不足够划出下一个展示的View
*/
int mAvailable;
/**
* Current position on the adapter to get the next item.
*/
int mCurrentPosition;
/**
* Defines the direction in which the data adapter is traversed.
* Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL}
*/
int mItemDirection;
/**
* Defines the direction in which the layout is filled.
* Should be {@link #LAYOUT_START} or {@link #LAYOUT_END}
*/
int mLayoutDirection;
/**
* Used when LayoutState is constructed in a scrolling state.
* It should be set the amount of scrolling we can make without creating a new view.
* Settings this is required for efficient view recycling.
* 滚动多少范围内,不需要创建新的view
*/
int mScrollingOffset;
/**
* Used if you want to pre-layout items that are not yet visible.
* The difference with {@link #mAvailable} is that, when recycling, distance laid out for
* {@link #mExtraFillSpace} is not considered to avoid recycling visible children.
*/
int mExtraFillSpace = 0;
/**
* Contains the {@link #calculateExtraLayoutSpace(RecyclerView.State, int[])} extra layout
* space} that should be excluded for recycling when cleaning up the tail of the list during
* a smooth scroll.
*/
int mNoRecycleSpace = 0;
/**
* Equal to {@link RecyclerView.State#isPreLayout()}. When consuming scrap, if this value
* is set to true, we skip removed views since they should not be laid out in post layout
* step.
*/
boolean mIsPreLayout = false;
/**
* The most recent {@link #scrollBy(int, RecyclerView.Recycler, RecyclerView.State)}
* amount.
*/
int mLastScrollDelta;
/**
* When LLM needs to layout particular views, it sets this list in which case, LayoutState
* will only return views from this list and return null if it cannot find an item.
*/
List<RecyclerView.ViewHolder> mScrapList = null;
/**
* Used when there is no limit in how many views can be laid out.
*/
boolean mInfinite;
/**
* @return true if there are more items in the data adapter
*/
boolean hasMore(RecyclerView.State state) {
return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount();
}
/**
* Gets the view for the next element that we should layout.
* Also updates current item index to the next item, based on {@link #mItemDirection}
*
* @return The next element that we should layout.
*/
View next(RecyclerView.Recycler recycler) {
if (mScrapList != null) {
return nextViewFromScrapList();
}
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
}
/**
* Returns the next item from the scrap list.
*
* Upon finding a valid VH, sets current item position to VH.itemPosition + mItemDirection
*
* @return View if an item in the current position or direction exists if not null.
*/
private View nextViewFromScrapList() {
final int size = mScrapList.size();
for (int i = 0; i < size; i++) {
final View view = mScrapList.get(i).itemView;
final RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams();
if (lp.isItemRemoved()) {
continue;
}
if (mCurrentPosition == lp.getViewLayoutPosition()) {
assignPositionFromScrapList(view);
return view;
}
}
return null;
}
public void assignPositionFromScrapList() {
assignPositionFromScrapList(null);
}
public void assignPositionFromScrapList(View ignore) {
final View closest = nextViewInLimitedList(ignore);
if (closest == null) {
mCurrentPosition = RecyclerView.NO_POSITION;
} else {
mCurrentPosition = ((RecyclerView.LayoutParams) closest.getLayoutParams())
.getViewLayoutPosition();
}
}
public View nextViewInLimitedList(View ignore) {
int size = mScrapList.size();
View closest = null;
int closestDistance = Integer.MAX_VALUE;
if (DEBUG && mIsPreLayout) {
throw new IllegalStateException("Scrap list cannot be used in pre layout");
}
for (int i = 0; i < size; i++) {
View view = mScrapList.get(i).itemView;
final RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams();
if (view == ignore || lp.isItemRemoved()) {
continue;
}
final int distance = (lp.getViewLayoutPosition() - mCurrentPosition)
* mItemDirection;
if (distance < 0) {
continue; // item is not in current direction
}
if (distance < closestDistance) {
closest = view;
closestDistance = distance;
if (distance == 0) {
break;
}
}
}
return closest;
}
void log() {
Log.d(TAG, "avail:" + mAvailable + ", ind:" + mCurrentPosition + ", dir:"
+ mItemDirection + ", offset:" + mOffset + ", layoutDir:" + mLayoutDirection);
}
}
/**
* The magic functions :). Fills the given layout, defined by the layoutState. This is fairly
* independent from the rest of the {@link LinearLayoutManager}
* and with little change, can be made publicly available as a helper class.
*
* @param recycler Current recycler that is attached to RecyclerView
* @param layoutState Configuration on how we should fill out the available space.
* @param state Context passed by the RecyclerView to control scroll steps.
* @param stopOnFocusable If true, filling stops in the first focusable new child
* @return Number of pixels that it added. Useful for scroll functions.
*/
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
// max offset we should set is mFastScroll + available
//记录最开始的时候 可用的空间
final int start = layoutState.mAvailable;
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
// TODO ugly bug fix. should not happen
if (layoutState.mAvailable < 0) {
//如果可用空间小于0 那么mScrollingOffset 就减去这个数
layoutState.mScrollingOffset += layoutState.mAvailable;
}
recycleByLayoutState(recycler, layoutState);
}
int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
layoutChunkResult.resetInternal();
if (RecyclerView.VERBOSE_TRACING) {
TraceCompat.beginSection("LLM LayoutChunk");
}
layoutChunk(recycler, state, layoutState, layoutChunkResult);
if (RecyclerView.VERBOSE_TRACING) {
TraceCompat.endSection();
}
if (layoutChunkResult.mFinished) {
break;
}
layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
/**
* Consume the available space if:
* * layoutChunk did not request to be ignored
* * OR we are laying out scrap children
* * OR we are not doing pre-layout
*/
if (!layoutChunkResult.mIgnoreConsumed || layoutState.mScrapList != null
|| !state.isPreLayout()) {
layoutState.mAvailable -= layoutChunkResult.mConsumed;
// we keep a separate remaining space because mAvailable is important for recycling
remainingSpace -= layoutChunkResult.mConsumed;
}
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
if (layoutState.mAvailable < 0) {
layoutState.mScrollingOffset += layoutState.mAvailable;
}
recycleByLayoutState(recycler, layoutState);
}
if (stopOnFocusable && layoutChunkResult.mFocusable) {
break;
}
}
if (DEBUG) {
validateChildOrder();
}
return start - layoutState.mAvailable;
}
回收View 代码块解读
private void recycleViewsFromStart(RecyclerView.Recycler recycler, int scrollingOffset,
int noRecycleSpace) {
if (scrollingOffset < 0) {
if (DEBUG) {
Log.d(TAG, "Called recycle from start with a negative value. This might happen"
+ " during layout changes but may be sign of a bug");
}
return;
}
// ignore padding, ViewGroup may not clip children.
final int limit = scrollingOffset - noRecycleSpace;
final int childCount = getChildCount();
if (mShouldReverseLayout) {
for (int i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
if (mOrientationHelper.getDecoratedEnd(child) > limit
|| mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
// stop here
recycleChildren(recycler, childCount - 1, i);
return;
}
}
} else {
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
//limit 表示滑动多少 就会创建新View
//如果说你的底部小于limit 说明你肯定要被回收
//直到找到一个View的底部>limit 的地方
if (mOrientationHelper.getDecoratedEnd(child) > limit
|| mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
// stop here
recycleChildren(recycler, 0, i);
return;
}
}
}
}