转载请注明:https://blog.csdn.net/feather_wch/article/details/79932523
参考和学习资料
1、LayoutManager的作用
layout(布局)
子视图- 滚动过程中根据
子视图
在布局中
所处的位置,决定何时添加和回收子视图
滚动
子视图。
2、RecyclerView与LayoutManager的联系
RV
设置LayoutManager
会调用requestLayout()进行View树的重绘
,最终会走RV的测量、布局、绘制三大流程。
RV
的子View的测量、布局
最终都在布局阶段
交由LayoutManager
的onLayoutChildren完成
3、LinearLayoutManager的主要方法
方法 | 解释 |
---|---|
onLayoutChildren | LayoutManager 的主入口,在初始化布局时 调用。如果Adapter 数据改变或者Adapter 被替换时,会再次调用。作用:初始化时放置Item ,直至填满布局 为止。 |
canScrollHorizontally() & canScrollVertically() | 当想要滚动的方向,与当前布局方向相同时,在对应方法返回true,另一个返回false。 |
scrollHorizontallyBy() & scrollVerticallyBy() | RecyclerView 已经处理了触摸事件 ,会将相应的偏移值(dx/dy)传入这两个方法中的对应方法 。根据偏移值 需要完成三件事:1、将所有Item视图移动到适当位置 2、决定移动视图后 添加/移除 视图。 3、返回滚动的实际距离。框架会根据它判断你是否触碰到边界。 |
4、LinearLayoutManager的滚动方法源码分析。
/**==============================================
* RecyclerView的三种状态: 1-开始;2-布局;3-动画
*==============================================*/
public static class State {
static final int STEP_START = 1;
static final int STEP_LAYOUT = 1 << 1;
static final int STEP_ANIMATIONS = 1 << 2;
//xxx
}
/**================================================
* 1、是否支持水平/垂直滚动
* 1. 如果可以水平滚动,canScrollHorizontally()就返回true
* 2. 如果可以垂直滚动,canScrollVertically()就返回true
* @return
*=============================================*/
@Override
public boolean canScrollHorizontally() {
return mOrientation == HORIZONTAL;
}
@Override
public boolean canScrollVertically() {
return mOrientation == VERTICAL;
}
/**
* 2、处理滚动事件.
* RecyclerView处理滚动事件后,会调用对应方向的方法,并传入dx/dy偏移值。
*/
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
if (mOrientation == VERTICAL) {
return 0;
}
return scrollBy(dx, recycler, state);
}
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
if (mOrientation == HORIZONTAL) {
return 0;
}
return scrollBy(dy, recycler, state);
}
/**
* 进行滑动
*/
int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
//1. 没有子元素直接返回
if (getChildCount() == 0 || dy == 0) {
return 0;
}
//2. 暂定需要“回收”
mLayoutState.mRecycle = true;
//3. 创建mLayoutState。并且创建mOrientationHelper
ensureLayoutState();
//4. dy>0-LAYOUT_END: 表示向下翻滚到末尾(或者向右翻滚到最右侧), LAYOUT_START相反
final int layoutDirection = dy > 0 ? LinearLayoutManager.LayoutState.LAYOUT_END : LinearLayoutManager.LayoutState.LAYOUT_START;
//5. 均用正数进行计算
final int absDy = Math.abs(dy);
//6. 更新布局状态
updateLayoutState(layoutDirection, absDy, true, state);
//7. consumed = 不添加新View情况下可以滑动的距离 + 添加View所增加的像素数
final int consumed = mLayoutState.mScrollingOffset
+ fill(recycler, mLayoutState, state, false);
if (consumed < 0) {
return 0;
}
final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;
mOrientationHelper.offsetChildren(-scrolled);
mLayoutState.mLastScrollDelta = scrolled;
//8. 实际滑动的距离
return scrolled;
}
/**
* 更新布局状态
*/
private void updateLayoutState(int layoutDirection, int requiredSpace, boolean canUseExistingSpace, RecyclerView.State state) {
//1-用于在View的放置数量没有限制的情况中
mLayoutState.mInfinite = resolveIsInfinite();
//2-用于你想要在item可见前提前布局item
mLayoutState.mExtra = getExtraLayoutSpace(state);
//3-滑动的方向
mLayoutState.mLayoutDirection = layoutDirection;
int scrollingOffset;
if (layoutDirection == LinearLayoutManager.LayoutState.LAYOUT_END) {
mLayoutState.mExtra += mOrientationHelper.getEndPadding();
final View child = getChildClosestToEnd(); //方向上第一个child
//4-遍历数据适配器的方向(遍历children的方向)
mLayoutState.mItemDirection = mShouldReverseLayout ? LinearLayoutManager.LayoutState.ITEM_DIRECTION_HEAD
: LinearLayoutManager.LayoutState.ITEM_DIRECTION_TAIL;
//5-当前应该从adapter中获取Item的position
mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
//6-布局开始位置的像素偏移(添加布局时需要通过这个确定子View的位置)
mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child);
scrollingOffset = mOrientationHelper.getDecoratedEnd(child)
- mOrientationHelper.getEndAfterPadding();
} else {
xxx
}
//7-布局方向上需要填充的像素数量(并减去 (View的最右侧-View不包含爬到顶的最右侧))
mLayoutState.mAvailable = requiredSpace;
if (canUseExistingSpace) {
mLayoutState.mAvailable -= scrollingOffset;
}
//8-在不创建新View的情况下,我们可以滚动的距离(独立于布局)
mLayoutState.mScrollingOffset = scrollingOffset;
}
int fill(RecyclerView.Recycler recycler, LinearLayoutManager.LayoutState layoutState, RecyclerView.State state, boolean stopOnFocusable) {
// max offset we should set is mFastScroll + available
final int start = layoutState.mAvailable;
//1-根据布局状态回收不再显示的子View
if (layoutState.mScrollingOffset != LinearLayoutManager.LayoutState.SCROLLING_OFFSET_NaN) {
xxx
recycleByLayoutState(recycler, layoutState);
}
int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
LinearLayoutManager.LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
//2-(无限个Item 或者 有多余的空间) 并且 Data Adapater还有更多的Item。
// layoutState.mExtra就是整个Recyclerview的宽度或者0,
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
layoutChunkResult.resetInternal();
//3-添加View
layoutChunk(recycler, state, layoutState, layoutChunkResult);
xxx
}
//4-添加View所增加的像素数
return start - layoutState.mAvailable;
}
/**
* 根据布局方向调用合适的回收方法
* @param recycler
* @param layoutState
*/
private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
if (!layoutState.mRecycle || layoutState.mInfinite) {
return;
}
//1-上滑到最顶端or滑动到最左端。从另一头开始回收
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
} else {
recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
}
}
/**
* 从末端开始视图回收
* @param recycler
* @param dt
*/
private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt) {
//1-RecyclerView当前可见的Item数量
final int childCount = getChildCount();
//2-RV末端不包含padding的位置 - 在不创建新View可以滑动的最大距离
final int limit = mOrientationHelper.getEnd() - dt;
//3-是否是反向遍历布局
if (mShouldReverseLayout) {
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (mOrientationHelper.getDecoratedStart(child) < limit
|| mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
// stop here
recycleChildren(recycler, 0, i);
return;
}
}
} else {
//4-(正常顺序的处理情况下)遍历当前可见的ItemView, 此时左滑,因此右面的View可能要回收
for (int i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
//5-当Child View的左侧已经处于limit边界之内,将该ChildView右侧所有的View回收
if (mOrientationHelper.getDecoratedStart(child) < limit
|| mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
//[childCount - 1, i) 内部的全部要回收
recycleChildren(recycler, childCount - 1, i);
return;
}
}
}
}
//LinearLayoutManager.java 回收[startIndex, endIndex)
private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
xxx
for (int i = startIndex; i > endIndex; i--) {
removeAndRecycleViewAt(i, recycler);
}
}
//RecyclerView.java
public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) {
final View view = getChildAt(index);
//1. 移除View
removeViewAt(index);
//2. 将该View回收,加入到View Pool(视图池)中,用于之后的复用。
recycler.recycleView(view);
}
//LinearLayoutManager.java 根据state进行布局
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
//1-根据Position获得第一个View
View view = layoutState.next(recycler);
//2-没有更多Item需要布局了
if (view == null) {
result.mFinished = true;
return;
}
//3-addView进行添加,最终是要通过Viewgroup的addView完成添加
LayoutParams params = (LayoutParams) view.getLayoutParams();
if (layoutState.mScrapList == null) {
if (mShouldReverseLayout == (layoutState.mLayoutDirection == LayoutState.LAYOUT_START)) {
addView(view);
} else {
addView(view, 0);
}
} else {
if (mShouldReverseLayout == (layoutState.mLayoutDirection == LayoutState.LAYOUT_START)) {
addDisappearingView(view);
} else {
addDisappearingView(view, 0);
}
}
//4-测量
measureChildWithMargins(view, 0, 0);
result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
int left, top, right, bottom;
...
//5-布局
layoutDecoratedWithMargins(view, left, top, right, bottom);
...
result.mFocusable = view.hasFocusable();
}