难点分析:
1:如何在有限的数据里面, 实现无限个Item呢?
2:如何让滑动的时候, 一个一个的滑动, 而不会一下子滚动多个呢?
3:如何在第一次显示的时候, 就可以左滑呢?
更新于2018-3-8
鉴于之前的时候方式, 有很多问题, 思路也不是特别好. 最近在学习的过程中, 发现了大神写了自定义的ViewPagerLayoutManager支持无限循环, 所以我就打算使用这个LayoutManager重写LoopRecyclerView.
1:继承ViewPagerLayoutManager
public static class LoopLayoutManager extends ViewPagerLayoutManager {
private float itemWidth = -1;
private float itemHeight = -1;
public LoopLayoutManager(Context context) {
this(context, ViewPagerLayoutManager.HORIZONTAL, false);
}
public LoopLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
setEnableBringCenterToFront(true);
}
public void setItemWidthHeight(float itemWidth, float itemHeight) {
this.itemWidth = itemWidth;
this.itemHeight = itemHeight;
requestLayout();
}
@Override
public void setInfinite(boolean enable) {
if (!enable) {
int positionOffset = getCurrentPositionOffset();
if (positionOffset > getItemCount() || positionOffset < 0) {
mOffset = 0;
}
}
super.setInfinite(enable);
}
/**
* Item 之间间隔的大小
* 默认情况下, Item之间是相互叠加显示的, 需要通过此方法, 设置间隔才能显示出线性的效果
*/
@Override
protected float setInterval() {
if (getOrientation() == ViewPagerLayoutManager.VERTICAL) {
return itemHeight;
}
return itemWidth;
}
/**
* 用来控制item属性, 比如各种属性动画, 在滑动的时候出发
*/
@Override
protected void setItemViewProperty(View itemView, float targetOffset) {
//targetOffset 和 itemInterval 密切广西
//targetOffset 取值范围 -itemInterval/2 0 itemInterval/2
//L.e("setItemViewProperty() -> " + targetOffset);
}
}
上面这个类, 主要作用就是让ViewPagerLayoutManager
产生出LinearLayoutManager
的水平布局效果, 和支持无限循环布局.
2:继承PagerSnapHelper
让RecyclerView滚动的时候, 模拟出ViewPager的效果, 并且监听页面改变回调.
publicstatic class LoopSnapHelper extends RPagerSnapHelper {
@Override
public boolean onFling(int velocityX, int velocityY) {
super.onFling(velocityX, velocityY);
//拦截fling操作
return true;
}
@Nullable
@Override
public View findSnapView(LayoutManager layoutManager) {
return super.findSnapView(layoutManager);
}
@Override
public int findTargetSnapPosition(LayoutManager lm, int velocityX, int velocityY) {
//return super.findTargetSnapPosition(layoutManager, velocityX, velocityY);
//重写fling操作
if (lm instanceof LoopLayoutManager) {
} else {
return RecyclerView.NO_POSITION;
}
RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
if (adapter == null) {
return RecyclerView.NO_POSITION;
}
final int minFlingVelocity = mRecyclerView.getMinFlingVelocity();
LoopLayoutManager layoutManager = (LoopLayoutManager) lm;
int orientation = layoutManager.getOrientation();
final boolean forwardDirection;
if (layoutManager.canScrollHorizontally()) {
forwardDirection = velocityX > 0;
} else {
forwardDirection = velocityY > 0;
}
final int offsetPosition;
if (forwardDirection) {
offsetPosition = 1;
} else {
offsetPosition = -1;
}
final int currentPosition = layoutManager.getCurrentPosition();
if ((orientation == ViewPagerLayoutManager.VERTICAL
&& Math.abs(velocityY) > minFlingVelocity) || (orientation == ViewPagerLayoutManager.HORIZONTAL
&& Math.abs(velocityX) > minFlingVelocity)) {
int position = layoutManager.getReverseLayout() ?
currentPosition - offsetPosition : currentPosition + offsetPosition;
mRecyclerView.smoothScrollToPosition(position);
}
//不需要默认的fling操作
return RecyclerView.NO_POSITION;
}
@Nullable
@Override
public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) {
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) targetView.getLayoutParams();
int position = params.getViewAdapterPosition();
int left = targetView.getLeft();
int right = targetView.getRight();
int top = targetView.getTop();
int bottom = targetView.getBottom();
ViewGroup viewGroup = (ViewGroup) targetView.getParent();
int[] out = new int[]{0, 0};
boolean isLastItem;
if (mOrientation == LinearLayoutManager.HORIZONTAL) {
isLastItem = position == layoutManager.getItemCount() - 1/*最后一个*/ && right == viewGroup.getMeasuredWidth();
out[0] = left;
out[1] = 0;
} else {
isLastItem = position == layoutManager.getItemCount() - 1/*最后一个*/ && bottom == viewGroup.getMeasuredHeight();
out[0] = 0;
out[1] = top;
}
if (mOnPageListener != null && mCurrentPosition != position) {
int currentPosition = mCurrentPosition;
boolean listener = false;
if (mOrientation == LinearLayoutManager.HORIZONTAL && (out[0] == 0 || isLastItem)) {
listener = true;
} else if (mOrientation == LinearLayoutManager.VERTICAL && (out[1] == 0 || isLastItem)) {
listener = true;
}
if (listener) {
mCurrentPosition = position;
mOnPageListener.onPageSelector(mCurrentPosition);
mOnPageListener.onPageSelector(currentPosition, mCurrentPosition);
}
}
return out;
}
}
public class RPagerSnapHelper extends PagerSnapHelper {
protected RecyclerView mRecyclerView;
OnPageListener mOnPageListener;
int mCurrentPosition = -1;
/**
* 默认是横向Pager
*/
int mOrientation = LinearLayoutManager.HORIZONTAL;
private RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
//开始滚动
} else if (newState == RecyclerView.SCROLL_STATE_IDLE) {
//结束滚动
} else if (newState == RecyclerView.SCROLL_STATE_SETTLING) {
//滑行中
}
}
};
public RPagerSnapHelper() {
}
public RPagerSnapHelper(int mCurrentPosition) {
this.mCurrentPosition = mCurrentPosition;
}
public int getCurrentPosition() {
return mCurrentPosition;
}
public RPagerSnapHelper setCurrentPosition(int currentPosition) {
mCurrentPosition = currentPosition;
return this;
}
public OnPageListener getOnPageListener() {
return mOnPageListener;
}
/**
* 页面选择回调监听
*/
public RPagerSnapHelper setOnPageListener(OnPageListener onPageListener) {
mOnPageListener = onPageListener;
return this;
}
@Override
public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws IllegalStateException {
super.attachToRecyclerView(recyclerView);
mRecyclerView = recyclerView;
if (recyclerView != null) {
recyclerView.removeOnScrollListener(mScrollListener);
recyclerView.addOnScrollListener(mScrollListener);
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
mOrientation = ((LinearLayoutManager) layoutManager).getOrientation();
}
}
}
@Nullable
@Override
public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) {
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) targetView.getLayoutParams();
int position = params.getViewAdapterPosition();
int left = targetView.getLeft();
int right = targetView.getRight();
int top = targetView.getTop();
int bottom = targetView.getBottom();
ViewGroup viewGroup = (ViewGroup) targetView.getParent();
int[] out = new int[]{0, 0};
boolean isLastItem;
if (mOrientation == LinearLayoutManager.HORIZONTAL) {
isLastItem = position == layoutManager.getItemCount() - 1/*最后一个*/ && right == viewGroup.getMeasuredWidth();
out[0] = left;
out[1] = 0;
} else {
isLastItem = position == layoutManager.getItemCount() - 1/*最后一个*/ && bottom == viewGroup.getMeasuredHeight();
out[0] = 0;
out[1] = top;
}
if (mOnPageListener != null && mCurrentPosition != position) {
int currentPosition = mCurrentPosition;
boolean listener = false;
if (mOrientation == LinearLayoutManager.HORIZONTAL && (out[0] == 0 || isLastItem)) {
listener = true;
} else if (mOrientation == LinearLayoutManager.VERTICAL && (out[1] == 0 || isLastItem)) {
listener = true;
}
if (listener) {
mCurrentPosition = position;
mOnPageListener.onPageSelector(mCurrentPosition);
mOnPageListener.onPageSelector(currentPosition, mCurrentPosition);
}
}
return out;
}
public RPagerSnapHelper setOrientation(int orientation) {
mOrientation = orientation;
return this;
}
public static abstract class OnPageListener {
@Deprecated
public void onPageSelector(int position) {
}
public void onPageSelector(int fromPosition, int toPosition) {
}
}
}
3:重写LoopRecyclerView
实现界面展示, 实现自动滚动, 封装功能
public class RExLoopRecyclerView extends RRecyclerView {
private LoopLayoutManager mLoopLayoutManager;
private boolean mInfinite = true;
private RPagerSnapHelper.OnPageListener mOnPageListener;
public RExLoopRecyclerView(Context context) {
this(context, null);
}
public RExLoopRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public RExLoopRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void initView(Context context) {
super.initView(context);
autoScrollRunnable = new Runnable() {
@Override
public void run() {
curScrollPosition = getCurrentPosition();
scrollToNext();
if (enableScroll) {
postDelayed(autoScrollRunnable, autoScrollTimeInterval);
}
}
};
mLoopLayoutManager = new LoopLayoutManager(getContext(), ViewPagerLayoutManager.HORIZONTAL, false);
setInfinite(true);
setLayoutManager(mLoopLayoutManager);
new RExLoopRecyclerView.LoopSnapHelper().setOnPageListener(new RPagerSnapHelper.OnPageListener() {
@Override
public void onPageSelector(int fromPosition, int toPosition) {
super.onPageSelector(fromPosition, toPosition);
//L.e("onPageSelector() -> " + fromPosition + " to " + toPosition);
if (mOnPageListener != null) {
mOnPageListener.onPageSelector(fromPosition, toPosition);
}
}
}).attachToRecyclerView(this);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (mLoopLayoutManager != null) {
mLoopLayoutManager.setItemWidthHeight(w, h);
}
}
/**
* 打开无限循环
*/
public void setInfinite(boolean enable) {
mInfinite = enable;
if (mLoopLayoutManager != null) {
mLoopLayoutManager.setInfinite(enable);
}
}
/**
* @see ViewPagerLayoutManager#setOrientation(int)
*/
public void setOrientation(int orientation) {
if (mLoopLayoutManager != null) {
mLoopLayoutManager.setOrientation(orientation);
}
}
/**
* 滚动到下一个
*/
public void scrollToNext() {
scrollTo(true);
}
/**
* 滚动到上一个
*/
public void scrollToPrev() {
scrollTo(false);
}
public void scrollTo(boolean forwardDirection) {
if (getLayoutManager() instanceof LoopLayoutManager && getAdapter() != null) {
final int offsetPosition;
if (forwardDirection) {
offsetPosition = 1;
} else {
offsetPosition = -1;
}
final int currentPosition = getCurrentPosition();
int position = ((LoopLayoutManager) getLayoutManager()).getReverseLayout() ?
currentPosition - offsetPosition : currentPosition + offsetPosition;
smoothScrollToPosition(position);
}
}
public int getCurrentPosition() {
if (getLayoutManager() instanceof LoopLayoutManager) {
return ((LoopLayoutManager) getLayoutManager()).getCurrentPosition();
} else {
return RecyclerView.NO_POSITION;
}
}
public void setOnPageListener(RPagerSnapHelper.OnPageListener onPageListener) {
mOnPageListener = onPageListener;
}
}
RRecyclerView
是我自己封装的RecyclerView类, 没有特殊之处. 就是多了一个 自动滚动的触发机制.
如果RExLoopRecyclerView
编译有错误, 可以自行实现对应功能, 或者删除报错代码.
以下是老的实现方式:
针对以上问题, 接下来一一解决:
问题1:
只需要在RecyclerView.Adapter
的方法中:
@Override
public int getItemCount() {
return Integer.MAX_VALUE;
}
可能距离真正无限个Item还是有差距, 但是达到效果还是可以的;
问题2:
其实谷歌已经帮我们实现了.
new PagerSnapHelper().attachToRecyclerView(this);
这样之后, 就可以一个一个的滚动Item了, 一行代码就实现了类似ViewPager的效果,
你还有什么理由使用ViewPager?
但是有一个缺点, 就是没有像ViewPager那样的OnPageChangeListener事件监听.
不过, 完全可以自己动手, 添加这个事件.
实现起来也是非常简单. 项目中有源码.
问题3:
@Override
public void setAdapter(Adapter adapter) {
super.setAdapter(adapter);
scrollToPosition(getAdapter().getItemRawCount() * 10000);//开始时的偏移量
}
只需要让开始的时候, 产生一定的位置偏移就行了.
其实思路很简单, 大家多动动脑, 动动手. 完全无压力的!
开源地址: https://github.com/angcyo/LoopRecyclerView
也许你还想学习更多, 来我的群吧, 我写代码的能力, 远大于写文章的能力:
联系作者
请使用QQ扫码加群, 小伙伴们都在等着你哦!
关注我的公众号, 每天都能一起玩耍哦!