1.改造ViewPager
2.RecyclerView + 改造PagerSnapHelper
这里最后用了第二种。
尝试
记录下,实现该界面的历程:
第一种方法:
这里曾试着解决三个问题:
1).垂直滑动的ViewPager
2).一个界面显示多页
3).无限循环
1).垂直滑动的ViewPager
由于之前用ViewPager实现过一个界面显示多页的效果(水平方向上),所以,第一眼我觉得可以用ViewPager来实现。但是,后来发现要实现垂直方向滑动,需要改造ViewPager,实现垂直方向滑动的ViewPager采用了网上的做法:交换x,y的TouchEvent事件《30行代码,打造一个垂直的ViewPager》,感觉还是没有水平方向的好。。。
2).一个界面显示多页
在布局文件中配置ViewPager 和其父布局的 android:clipChildren属性为”false”(android:clipChildren表示是否限制子View在其范围内,默认为true),同时,其父布局添加android:layerType=”software”属性。但是需要设置layout_margin才有效果。
参考:ViewPager系列之ViewPager一屏显示多个子页面
但是滑动的时候又会只显示一个页面或者有闪屏的感觉。
3).实现无限循环
这里的无限循环是指:重复使用几个数据,然后滑动的时候不断更新这些数据,达到循环的感觉。
由于这里使用Fragment作为界面展示,且需要展示数据较多,因此这里Adapter继承的是FragmentStatePagerAdapter。
主要是先以下方法:
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
return super.instantiateItem(container,position % mRealCount);
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container,position % mRealCount);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position % mRealCount, object);
}
mRealCount 为数据的大小
采用这方方式,需要一开始先用ViewPager的setCurrentItem的方法,设置当前的页面为一个较大的数值,比如:Integer.MAX_VALUE / 2。
小结:感觉ViewPager更适合做水平方向,单页显示的。
第二种方法:
这里层试着解决:
1). 自定义SnapHelper
2). 无限循环
1).自定义SnapHelper:
这里直接附上目前可以顶部对齐并且回调对应位置的SnapHelper
import android.graphics.PointF;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.OrientationHelper;
import android.support.v7.widget.PagerSnapHelper;
import android.support.v7.widget.RecyclerView;
import android.view.View;
public class CustomPagerSnapHelper extends PagerSnapHelper {
private OnPageListener mOnPageListener;
private int mCurrentPosition = 0;
@Nullable
private OrientationHelper mVerticalHelper;
@Nullable
private OrientationHelper mHorizontalHelper;
/**
* 页面选择回调监听
*/
public CustomPagerSnapHelper setOnPageListener(OnPageListener onPageListener) {
mOnPageListener = onPageListener;
return this;
}
@Nullable
@Override
public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) {
int[] out = new int[2];
if (layoutManager.canScrollHorizontally()) {
out[0] = this.distanceToStart(layoutManager, targetView, this.getHorizontalHelper(layoutManager));
} else {
out[0] = 0;
}
if (layoutManager.canScrollVertically()) {
out[1] = this.distanceToStart(layoutManager, targetView, this.getVerticalHelper(layoutManager));
} else {
out[1] = 0;
}
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) targetView.getLayoutParams();
int position = params.getViewAdapterPosition();
boolean isLastItem = position == layoutManager.getItemCount() - 1;
if (mOnPageListener != null && mCurrentPosition != position && (out[0] == 0 || isLastItem)) {
mOnPageListener.onPageSelected(mCurrentPosition = position);
}
return out;
}
@Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
if (layoutManager.canScrollVertically()) {
return this.findStartView(layoutManager, this.getVerticalHelper(layoutManager));
} else {
return layoutManager.canScrollHorizontally() ? this.findStartView(layoutManager, this.getHorizontalHelper(layoutManager)) : null;
}
}
@Override
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) {
int itemCount = layoutManager.getItemCount();
if (itemCount == 0) {
return -1;
} else {
View mStartMostChildView = null;
if (layoutManager.canScrollVertically()) {
mStartMostChildView = this.findStartView(layoutManager, this.getVerticalHelper(layoutManager));
} else if (layoutManager.canScrollHorizontally()) {
mStartMostChildView = this.findStartView(layoutManager, this.getHorizontalHelper(layoutManager));
}
if (mStartMostChildView == null) {
return -1;
} else {
int centerPosition = layoutManager.getPosition(mStartMostChildView);
if (centerPosition == -1) {
return -1;
} else {
boolean forwardDirection;
if (layoutManager.canScrollHorizontally()) {
forwardDirection = velocityX > 0;
} else {
forwardDirection = velocityY > 0;
}
boolean reverseLayout = false;
if (layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider) {
RecyclerView.SmoothScroller.ScrollVectorProvider vectorProvider = (RecyclerView.SmoothScroller.ScrollVectorProvider)layoutManager;
PointF vectorForEnd = vectorProvider.computeScrollVectorForPosition(itemCount - 1);
if (vectorForEnd != null) {
reverseLayout = vectorForEnd.x < 0.0F || vectorForEnd.y < 0.0F;
}
}
return reverseLayout ? (forwardDirection ? centerPosition - 1 : centerPosition) : (forwardDirection ? centerPosition + 1 : centerPosition);
}
}
}
}
private int distanceToStart(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView, OrientationHelper helper) {
int childStart = helper.getDecoratedStart(targetView);
int containerStart;
if (layoutManager.getClipToPadding()) {
containerStart = helper.getStartAfterPadding();
} else {
containerStart = 0;
}
return childStart - containerStart;
}
@Nullable
private View findStartView(RecyclerView.LayoutManager layoutManager, OrientationHelper helper) {
int childCount = layoutManager.getChildCount();
if (childCount == 0) {
return null;
} else {
View closestChild = null;
int start;
if (layoutManager.getClipToPadding()) {
start = helper.getStartAfterPadding();
} else {
start = 0 ;
}
int absClosest = Integer.MAX_VALUE;
for(int i = 0; i < childCount; ++i) {
View child = layoutManager.getChildAt(i);
int childStart = helper.getDecoratedStart(child);
int absDistance = Math.abs(childStart - start);
if (absDistance < absClosest) {
absClosest = absDistance;
closestChild = child;
}
}
return closestChild;
}
}
@NonNull
private OrientationHelper getVerticalHelper(@NonNull RecyclerView.LayoutManager layoutManager) {
if (this.mVerticalHelper == null || this.mVerticalHelper.getLayoutManager() != layoutManager) {
this.mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager);
}
return this.mVerticalHelper;
}
@NonNull
private OrientationHelper getHorizontalHelper(@NonNull RecyclerView.LayoutManager layoutManager) {
if (this.mHorizontalHelper == null || this.mHorizontalHelper.getLayoutManager() != layoutManager) {
this.mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
}
return this.mHorizontalHelper;
}
public interface OnPageListener {
void onPageSelected(int position);
}
}
外部要获取当前顶部为的位置,可用如下方式获取
new CustomPagerSnapHelper().setOnPageListener(new CustomPagerSnapHelper.OnPageListener() {
@Override
public void onPageSelected(int position) {
// do something
}
}).attachToRecyclerView(mRecyclerView);
参考:
让你明明白白的使用RecyclerView——SnapHelper详解
SnapHelper学习记录
2).无限循环
这种无限循环的思路和前面的ViewPager的一样,但是更简单
在Adapter中实现方式如下:
@Override
public int getItemCount() {
if (mData.size() > 0) {
return Integer.MAX_VALUE;
}
return 0;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (mData.size() <= 0) {
return;
}
DataInfo dataInfo = mData.get(position % mData.size());
}
其中,mData为数据列表
同样的,需要调用如下方法,滚动到较大的位置。
mRecyclerView.getLayoutManager().scrollToPosition();
**小结:**比较适合做固定数据的无限循环,例如banner。如果要左右滑动都能加载数据,且不能有闪屏的,则不合适。
总结
最后,这里采用第二种方法,但没有实现无限循环,由于不知如何处理前后滑动加载数据而又不闪屏,这里采用最简单的方式,就是直接加载100年的数据。。。