快速上手之先把套路搞起来 :
1、继承RecyclerView.LayoutManager
public class LinearLayoutManager extends RecyclerView.LayoutManager implements
ItemTouchHelper.ViewDropHandler, RecyclerView.SmoothScroller.ScrollVectorProvider {
}
2、实现generateDefaultLayoutParams
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
3、重写isAutoMeasureEnabled或者onMeasure方法
@Override
public boolean isAutoMeasureEnabled() {
return true;
}
//isAutoMeasureEnabled和onMeasure方法互斥 根据实际情况重写其中一个
//RecyclerView自带的三个LayoutManager均实现了isAutoMeasureEnabled
@Override
public void onMeasure(@NonNull RecyclerView.Recycler recycler,
@NonNull RecyclerView.State state, int widthSpec, int heightSpec) {
super.onMeasure(recycler, state, widthSpec, heightSpec);
}
4、重写onLayoutChildren 开始填充子VIew(注意此处不可一次性填充所有view,只填充屏幕可 展示view即可)
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
super.onLayoutChildren(recycler, state);
}
5、重写canScrollVerticall或者canScrollHorizontally来决定滑动方向(两个都写表示支持横竖滑动)
@Override
public boolean canScrollVertically() {
return true;
}
6、重写scrollVerticallyBy或者scrollHorizontallyBy 在滑动的时候做边界处理,item的回收以及重用逻辑
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
//这里编写逻辑 realDy 表示经过计算之后的需要滑动的真实距离
int realDy = dy;
...
return realDy ;
}
7、scrollToPosition()和smoothScrollToPosition()方法支持
直接看LinearLayoutManager.scrollToPosition代码实现:
@Override
public void scrollToPosition(int position) {
mPendingScrollPosition = position;
mPendingScrollPositionOffset = INVALID_OFFSET;
if (mPendingSavedState != null) {
mPendingSavedState.invalidateAnchor();
}
requestLayout();
}
7.1 、将mPendingScrollPosition设置为需要滚动到的position,然后调用requestLayout()方法,这个方法会回调onLayoutChildren,在onLayoutChildren中会调用updateAnchorFromPendingData()根据position获取view看此view是否正处于屏幕内,然后再根据这个position判断这个view是从左往右布局,还是从右往左布局
private boolean updateAnchorFromPendingData(RecyclerView.State state, AnchorInfo anchorInfo) {
...
if (mPendingScrollPositionOffset == INVALID_OFFSET) {
//根据position获取需要滚动到的view
View child = findViewByPosition(mPendingScrollPosition);
//如果view处于屏幕内(有可能未完全显示)
if (child != null) {
...
//获取view的左边缘
final int startGap = mOrientationHelper.getDecoratedStart(child)
- mOrientationHelper.getStartAfterPadding();
//如果小于0则表示未完全显示 左边缘处于屏幕外
if (startGap < 0) {
anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding();
//从左到右布局
anchorInfo.mLayoutFromEnd = false;
return true;
}
//使用layoutManager的宽度减去view的右边缘
final int endGap = mOrientationHelper.getEndAfterPadding()
- mOrientationHelper.getDecoratedEnd(child);
//如果结果小于0则表示右边缘处于屏幕外
if (endGap < 0) {
anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding();
//从右到左布局
anchorInfo.mLayoutFromEnd = true;
return true;
}
...
} else { // item is not visible.
//如果根据position获取需要滚动到的view不可见
if (getChildCount() > 0) {
// get position of any child, does not matter
//获取当前屏幕上第一个显示的view的position
int pos = getPosition(getChildAt(0));
//通过对比当前需要滚动到的view的position以及当前显示的view的position来决定如何布局
anchorInfo.mLayoutFromEnd = mPendingScrollPosition < pos
== mShouldReverseLayout;
}
...
}
return true;
8、适配smoothScrollToPosition()
//LinearLayuotManager
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
int position) {
LinearSmoothScroller linearSmoothScroller =
new LinearSmoothScroller(recyclerView.getContext());
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
8.1自定义的layoutManager实现ScrollVectorProvider接口
public class LinearLayoutManager extends RecyclerView.LayoutManager implements
ItemTouchHelper.ViewDropHandler, RecyclerView.SmoothScroller.ScrollVectorProvider {
}
8.2 重写computeScrollVectorForPosition方法
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
if (getChildCount() == 0) {
return null;
}
final int firstChildPos = getPosition(getChildAt(0));
final int direction = targetPosition < firstChildPos != mShouldReverseLayout ? -1 : 1;
if (mOrientation == HORIZONTAL) {
return new PointF(direction, 0);
} else {
return new PointF(0, direction);
}
}
9、回收View:如果能在第一次布局和滑动的时候准确的做好Holder的回收以及复用的话,则可以直 接拿到mAttachedScrap中的holder直接放到mRecyclerPool中回收。(例如每次滑动的时候都有调用detachAndScrapAttachedViews,然后在再去缓存中拿去有用的itemview)
//拿到临时缓存
List scrapList = recycler.getScrapList();
//遍历,然后先移除,后回收,其实也就是removeAndRecycleView方法所做的事
for (int i = 0; i < scrapList.size(); i++) {
RecyclerView.ViewHolder holder = scrapList.get(i);
removeView(holder.itemView);
recycler.recycleView(holder.itemView);
}
否则需要自己计算开始以及结束index去回收
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--) {
removeAndRecycleViewAt(i, recycler);
}
} else {
for (int i = startIndex; i > endIndex; i--) {
removeAndRecycleViewAt(i, recycler);
}
}
}
以上为自定义Layoutmananger所需步骤,注意以上并非完整代码,实际仍然在各个步骤中补充业务逻辑,需要根据实际业务做到完整的item回收,复用,滑动距离计算,滑动边界计算才能真正实现完整的Layoutmanager