更新于:2017-2-16
以前的實(shí)現(xiàn)方式, 雖然面前可以達(dá)到效果, 但是著實(shí)有點(diǎn)low,
現(xiàn)在提供一種體驗(yàn)相當(dāng)好的解決方案:SnapHelper
以下是實(shí)現(xiàn)代碼: 其實(shí)就是同時(shí)處理OnScrollListener事件和OnFlingListener事件.
比我之前的方法多了一個(gè)OnFlingListener事件的監(jiān)聽(tīng).
public class ViewPagerSnapHelper extends SnapHelper {
/**
* 每一頁(yè)中, 含有多少個(gè)item
*/
int mPageItemCount = 1;
/**
* 當(dāng)前頁(yè)面索引
*/
int mCurrentPageIndex = 0;
RecyclerView mRecyclerView;
PageListener mPageListener;
/**
* 需要滾動(dòng)到目標(biāo)的頁(yè)面索引
*/
int mTargetIndex = RecyclerView.NO_POSITION;
/**
* fling操作時(shí),需要鎖住目標(biāo)索引位置
*/
boolean isFling = false;
int scrollState;
private RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
scrollState = newState;
L.w("scroll state : " + newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
onScrollEnd();
} else if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
isFling = false;
} else if (newState == RecyclerView.SCROLL_STATE_SETTLING) {
}
}
};
public ViewPagerSnapHelper(int pageItemCount) {
if (pageItemCount < 1) {
throw new IllegalStateException("page item count need greater than 1");
}
this.mPageItemCount = pageItemCount;
}
protected void onScrollEnd() {
int old = mCurrentPageIndex;
int index = getPagerIndex(0, 0);
//L.i("current->" + mCurrentPageIndex + " index->" + index + " target->" + mTargetIndex);
if (index == mTargetIndex) {
mCurrentPageIndex = mTargetIndex;
//滾動(dòng)結(jié)束后, 目標(biāo)的索引位置和當(dāng)前的索引位置相同, 表示已經(jīng)完成了頁(yè)面切換
if (old != mCurrentPageIndex) {
//L.e("page from->" + old + " to->" + mCurrentPageIndex);
}
if (mPageListener != null) {
mPageListener.onPageSelector(mCurrentPageIndex);
}
}
}
@Override
public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws IllegalStateException {
if (recyclerView == null) {
throw new NullPointerException("RecyclerView not be null");
}
mRecyclerView = recyclerView;
super.attachToRecyclerView(recyclerView);
mRecyclerView.addOnScrollListener(mScrollListener);
}
@Nullable
@Override
public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,
@NonNull View targetView) {
int[] out = new int[2];
if (layoutManager.canScrollHorizontally()) {
out[0] = mTargetIndex * mRecyclerView.getMeasuredWidth() - mRecyclerView.computeHorizontalScrollOffset();
} else {
out[0] = 0;
}
if (layoutManager.canScrollVertically()) {
out[1] = mTargetIndex * mRecyclerView.getMeasuredHeight() - mRecyclerView.computeVerticalScrollOffset();
} else {
out[1] = 0;
}
return out;
}
@Nullable
@Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
int childCount = mRecyclerView.getLayoutManager().getChildCount();
final int pagerIndex = getPagerIndex(0, 0);
if (childCount == 0 || isFling) {
return null;
}
mTargetIndex = pagerIndex;
//隨便返回一個(gè)補(bǔ)位空的view,就行.不需要通過(guò)這個(gè)View計(jì)算位置.
return mRecyclerView.getLayoutManager().getChildAt(0);
}
@Override
public boolean onFling(int velocityX, int velocityY) {
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
if (layoutManager == null) {
return false;
}
RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
if (adapter == null) {
return false;
}
int minFlingVelocity = mRecyclerView.getMinFlingVelocity();
boolean handle = Math.abs(velocityY) > minFlingVelocity || Math.abs(velocityX) > minFlingVelocity;
//L.w("onFling " + handle + " " + isFling);
if (isFling) {
return false;
}
if (handle) {
if (mTargetIndex != RecyclerView.NO_POSITION) {
mCurrentPageIndex = mTargetIndex;
}
if (velocityX > 0 || velocityY > 0) {
mTargetIndex = fixPagerIndex(mCurrentPageIndex + 1);
} else if (velocityX < 0 || velocityY < 0) {
mTargetIndex = fixPagerIndex(mCurrentPageIndex - 1);
} else {
mTargetIndex = fixPagerIndex(mCurrentPageIndex);
}
int[] snapDistance = calculateDistanceToFinalSnap(layoutManager, null);
if (snapDistance[0] != 0 || snapDistance[1] != 0) {
isFling = true;
mRecyclerView.smoothScrollBy(snapDistance[0], snapDistance[1]);
} else {
onScrollEnd();
}
}
return handle;
}
/**
* 只會(huì)在onFling的時(shí)候調(diào)用
*/
@Override
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX,
int velocityY) {
final int itemCount = layoutManager.getItemCount();
if (itemCount == 0) {
return RecyclerView.NO_POSITION;
}
mTargetIndex = fixPagerIndex(getPagerIndex(velocityX, velocityY));
return mTargetIndex * mPageItemCount;
}
/**
* 獲取當(dāng)前應(yīng)該顯示第幾頁(yè)
*/
private int getPagerIndex(int velocityX, int velocityY) {
final int verticalScrollOffset = mRecyclerView.computeVerticalScrollOffset();
final int horizontalScrollOffset = mRecyclerView.computeHorizontalScrollOffset();
final int currentVerticalScrollOffset = mCurrentPageIndex * mRecyclerView.getMeasuredHeight();
final int currentHorizontalScrollOffset = mCurrentPageIndex * mRecyclerView.getMeasuredWidth();
int index = 0;
if (mRecyclerView.getLayoutManager().canScrollVertically()) {
//除掉整頁(yè)距離之后的距離
final float offset = verticalScrollOffset * 1.f % mRecyclerView.getMeasuredHeight();
final float page = verticalScrollOffset * 1.f / mRecyclerView.getMeasuredHeight();//前面還有多少頁(yè)
index = (int) Math.floor(page);//前面還有多少頁(yè), 取整
if (offset == 0) {
return index;
}
if (currentVerticalScrollOffset <= verticalScrollOffset) {
//向上滾動(dòng)
if (offset >= mRecyclerView.getMeasuredHeight() / 2) {
//超過(guò)一半的距離
index = mCurrentPageIndex + 1;
} else {
if (velocityY > 0) {
index = mCurrentPageIndex + 1;
} else {
index = mCurrentPageIndex;
}
}
} else {
//向下滾動(dòng)
if (offset >= mRecyclerView.getMeasuredHeight() / 2) {
//超過(guò)一半的距離
if (velocityY < 0) {
index = mCurrentPageIndex - 1;
} else {
index = mCurrentPageIndex;
}
} else {
index = mCurrentPageIndex - 1;
}
}
} else if (mRecyclerView.getLayoutManager().canScrollHorizontally()) {
final float offset = horizontalScrollOffset * 1.f % mRecyclerView.getMeasuredWidth();
final float page = horizontalScrollOffset * 1.f / mRecyclerView.getMeasuredWidth();
index = (int) Math.floor(page);
if (offset == 0) {
return index;
}
if (currentHorizontalScrollOffset <= horizontalScrollOffset) {
//向左滾動(dòng)
if (offset >= mRecyclerView.getMeasuredWidth() / 2) {
//超過(guò)一半的距離
index = mCurrentPageIndex + 1;
} else {
if (velocityX > 0) {
index = mCurrentPageIndex + 1;
} else {
index = mCurrentPageIndex;
}
}
} else {
//向右滾動(dòng)
if (offset >= mRecyclerView.getMeasuredWidth() / 2) {
//超過(guò)一半的距離
if (velocityX < 0) {
index = mCurrentPageIndex - 1;
} else {
index = mCurrentPageIndex;
}
} else {
index = mCurrentPageIndex - 1;
}
}
}
return index;
}
private int fixPagerIndex(int index) {
int maxIndex = mRecyclerView.getLayoutManager().getItemCount() / mPageItemCount - 1;
int minIndex = 0;
index = Math.max(minIndex, Math.min(index, maxIndex));
if (index < mCurrentPageIndex) {
index = mCurrentPageIndex - 1;
} else if (index > mCurrentPageIndex) {
index = mCurrentPageIndex + 1;
}
return index;
}
/**
* 頁(yè)面選擇回調(diào)監(jiān)聽(tīng)
*/
public ViewPagerSnapHelper setPageListener(PageListener pageListener) {
mPageListener = pageListener;
return this;
}
public interface PageListener {
void onPageSelector(int position);
}
}
使用方法:
new ViewPagerSnapHelper(getItemCount()).setPageListener(new ViewPagerSnapHelper.PageListener() {
@Override
public void onPageSelector(int position) {
onViewPagerSelect(position);
}
}).attachToRecyclerView(recyclerView);
在配合我之前寫(xiě)的RecyclerViewPagerAdapter(必須), 就可以輕松實(shí)現(xiàn)效果了.
如題所示,
都支持橫向和縱向, 暫不支持StaggeredGridLayoutManager布局管理.
如圖:
在LinearLayoutManager中:
在GridLayoutManager中:
1:當(dāng)adapter中Item的數(shù)量不足時(shí), 需要用假數(shù)據(jù)填充.
否則最后一頁(yè)顯示不全, 達(dá)不到頁(yè)面的效果.
@Override
public int getItemCount() {
rawSize = mAllDatas == null ? 0 : mAllDatas.size();
final int itemCount = mRecyclerViewPager.getItemCount();
final double ceil = Math.ceil(rawSize * 1f / itemCount);//當(dāng)給定的item個(gè)數(shù)不足以填充一屏?xí)r, 使用占位item
return (int) (ceil * itemCount);
}
2:為了達(dá)到沾滿整屏的效果, 需要?jiǎng)討B(tài)計(jì)算每一個(gè)Item的寬高
@Override
protected void onBindView(RBaseViewHolder holder, int position, T bean) {
holder.itemView.setLayoutParams(new ViewGroup.LayoutParams(mRecyclerViewPager.getItemWidth(),
mRecyclerViewPager.getItemHeight()));
if (holder.getItemViewType() == 200) {
onBindRawView(holder, position, bean);
}
}
/**
* 計(jì)算每個(gè)Item的寬度
*/
public int getItemWidth() {
final LayoutManager layoutManager = getLayoutManager();
int itemWidth = 0;
if (layoutManager instanceof GridLayoutManager) {
final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
final int spanCount = gridLayoutManager.getSpanCount();
if (gridLayoutManager.getOrientation() == LinearLayoutManager.HORIZONTAL) {
itemWidth = getRawWidth() / (mItemCount / spanCount);
} else {
itemWidth = getRawWidth() / spanCount;
}
} else if (layoutManager instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
if (linearLayoutManager.getOrientation() == LinearLayoutManager.HORIZONTAL) {
itemWidth = getRawWidth() / mItemCount;
} else {
itemWidth = getRawWidth();
}
}
return itemWidth;
}
public int getItemHeight() {
final LayoutManager layoutManager = getLayoutManager();
int itemHeight = 0;
if (layoutManager instanceof GridLayoutManager) {
final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
final int spanCount = gridLayoutManager.getSpanCount();
if (gridLayoutManager.getOrientation() == LinearLayoutManager.HORIZONTAL) {
itemHeight = getRawHeight() / spanCount;
} else {
itemHeight = getRawHeight() / (mItemCount / spanCount);
}
} else if (layoutManager instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
if (linearLayoutManager.getOrientation() == LinearLayoutManager.HORIZONTAL) {
itemHeight = getRawHeight();
} else {
itemHeight = getRawHeight() / mItemCount;
}
}
return itemHeight;
}
3:一切準(zhǔn)備好了之后,核心的滾動(dòng)計(jì)算要開(kāi)始了.
private OnScrollListener mOnScrollListener = new OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == SCROLL_STATE_DRAGGING) {
//開(kāi)始滾動(dòng)
mVerticalScrollOffsetStart = recyclerView.computeVerticalScrollOffset();
mHorizontalScrollOffsetStart = recyclerView.computeHorizontalScrollOffset();
} else if (newState == SCROLL_STATE_IDLE) {
//滾動(dòng)結(jié)束之后
final int verticalScrollOffset = recyclerView.computeVerticalScrollOffset();
final int horizontalScrollOffset = recyclerView.computeHorizontalScrollOffset();
final int rawWidth = getRawWidth();
final int rawHeight = getRawHeight();
int pagerIndex = mCurrentPager;
int dx = 0, dy = 0;
if (verticalScrollOffset == 0 && horizontalScrollOffset != 0) {
//橫向滾動(dòng)
final float page = horizontalScrollOffset * 1.f / rawWidth;//當(dāng)前滾動(dòng)到了第幾頁(yè)
final double floor = Math.floor(page);//前一頁(yè)
final double ceil = Math.ceil(page);//后一頁(yè)
final int offset;
final int offsetWidth;//滑動(dòng)之后, 剩余屏幕的寬度
if (horizontalScrollOffset > mHorizontalScrollOffsetStart) {
pagerIndex = (int) floor;
//左滑動(dòng)
offset = (int) (horizontalScrollOffset - floor * rawWidth);
offsetWidth = rawWidth - offset;
if (offset >= rawWidth / 3) {
dx = offsetWidth;
} else {
dx = -offset;
}
} else if (mHorizontalScrollOffsetStart > horizontalScrollOffset) {
pagerIndex = (int) ceil;
//右滑動(dòng)
offset = (int) (ceil * rawWidth - horizontalScrollOffset);//橫向滾動(dòng)了多少距離
offsetWidth = rawWidth - offset;
if (offset >= rawWidth / 3) {
dx = -offsetWidth;
} else {
dx = offset;
}
}
} else if (horizontalScrollOffset == 0 && verticalScrollOffset != 0) {
//豎向滾動(dòng)
final float page = verticalScrollOffset * 1.f / rawHeight;//當(dāng)前滾動(dòng)到了第幾頁(yè)
final double floor = Math.floor(page);//前一頁(yè)
final double ceil = Math.ceil(page);//后一頁(yè)
final int offset;
final int offsetHeight;//滑動(dòng)之后, 剩余屏幕的高度
if (verticalScrollOffset > mVerticalScrollOffsetStart) {
pagerIndex = (int) floor;
//上滑動(dòng)
offset = (int) (verticalScrollOffset - floor * rawHeight);
offsetHeight = rawHeight - offset;
if (offset >= rawHeight / 3) {
dy = offsetHeight;
} else {
dy = -offset;
}
} else if (mVerticalScrollOffsetStart > verticalScrollOffset) {
pagerIndex = (int) ceil;
//下滑動(dòng)
offset = (int) (ceil * rawHeight - verticalScrollOffset);//橫向滾動(dòng)了多少距離
offsetHeight = rawHeight - offset;
if (offset >= rawHeight / 3) {
dy = -offsetHeight;
} else {
dy = offset;
}
}
} else {
pagerIndex = 0;
}
to(dx, dy);
onViewPagerSelect(pagerIndex);
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
}
};
4:其他需要注意的東西
//重寫(xiě)此方法, 讓手指快速滑動(dòng)的時(shí)候, 慢一點(diǎn)...
@Override
public boolean fling(int velocityX, int velocityY) {
return super.fling((int) (velocityX * 0.3f), (int) (velocityY * 0.3f));
}
開(kāi)源地址: https://github.com/angcyo/RecyclerViewPager
至此: 文章就結(jié)束了,如有疑問(wèn): QQ群 Android:274306954 Swift:399799363 歡迎您的加入.