横向居中的RecyclerView

首先先要配置compile “com.android.support:recyclerview-v7:24.2.0”
SnapHelper LinearSnapHelper,在版本大于24.2.0中才支持
效果图如图所示:

这里写图片描述
SnapHelper helper = new LinearSnapHelper();
helper.attachToRecyclerView(mRlRecommends);

        LinearSnapHelper snapHelper = new LinearSnapHelper() {
            @Override
            public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) {
                View centerView = findSnapView(layoutManager);
                if (centerView == null)
                    return RecyclerView.NO_POSITION;

                int position = layoutManager.getPosition(centerView);
                int targetPosition = -1;
                if (layoutManager.canScrollHorizontally()) {
                    if (velocityX < 0) {
                        targetPosition = position - 1;
                    } else {
                        targetPosition = position + 1;
                    }
                }

                if (layoutManager.canScrollVertically()) {
                    if (velocityY < 0) {
                        targetPosition = position - 1;
                    } else {
                        targetPosition = position + 1;
                    }
                }

                final int firstItem = 0;
                final int lastItem = layoutManager.getItemCount() - 1;
                targetPosition = Math.min(lastItem, Math.max(targetPosition, firstItem));
                return targetPosition;
            }

        };
        snapHelper.attachToRecyclerView(mRlRecommends);

二,使用完这个方法我们会发现一个小问题,横向时,第一个和最后一个在滑动过后,
当设置点击事件后没有响应了,首先 debug 了 内层 RecyclerView 的 onInterceptTouchEvent,
发现这个时候 return 为 true, 而且这时 RecyclerView 的状态 不是 SCROLL_STATE_IDLE,而是 SCROLL_STATE_SETTLING ,看 RecyclerView 的源码

if (mScrollState == SCROLL_STATE_SETTLING) {
getParent().requestDisallowInterceptTouchEvent(true);
setScrollState(SCROLL_STATE_DRAGGING);
}

return mScrollState == SCROLL_STATE_DRAGGING;
因此这个时候 RecyclerView 会拦截事件。
事实上导致 RecyclerView 的状态 为 SCROLL_STATE_SETTLING 是因为 LinearSnapHelper。
LinearSnapHelper 原理是监听 RecyclerView 的滚动状态,一旦处于RecyclerView.SCROLL_STATE_IDLE (当然还有 Fling 的情况,这里先不看) 就会计算得到要处于中心的 Item View,然后在计算需要滚动的距离。
处于中心这个点很重要,细心的读者可能会发现第一个 Item 并不会处于中心的,那么这个时候 LinearSnapHelper 会怎么处理呢?
LinearSnapHelper 会根据 calculateDistanceToFinalSnap 得到的距离 利用 RecyclerView 的 smoothScrollBy 来滚动,而这个时候第一个 Item 永远都滚动不到中心的位置,于是不停的 ScrollBy 也就导致 状态 为 SCROLL_STATE_SETTLING。

知道原因后就好办了, 重写 calculateDistanceToFinalSnap 修正距离就可以了
用以下的 FixLinearSnapHelper 代替 LinearSnapHelper 就可以

public class MySnapHelper extends LinearSnapHelper {

private OrientationHelper mVerticalHelper;

private OrientationHelper mHorizontalHelper;

private RecyclerView mRecyclerView;

@Override
public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,
                                          @NonNull View targetView) {
    int[] out = new int[2];

    if (layoutManager.canScrollHorizontally()) {
        out[0] = distanceToCenter(targetView, getHorizontalHelper(layoutManager));
    } else {
        out[0] = 0;
    }

    if (layoutManager.canScrollVertically()) {
        out[1] = distanceToCenter(targetView, getVerticalHelper(layoutManager));
    } else {
        out[1] = 0;
    }
    return out;
}

@Override
public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws IllegalStateException {
    this.mRecyclerView = recyclerView;
    super.attachToRecyclerView(recyclerView);
}

private int distanceToCenter(View targetView, OrientationHelper helper) {
    //如果已经滚动到尽头 并且判断是否是第一个item或者是最后一个,直接返回0,不用多余的滚动了
    if ((helper.getDecoratedStart(targetView) == 0 && mRecyclerView.getChildAdapterPosition(targetView) == 0)
            || (helper.getDecoratedEnd(targetView) == helper.getEndAfterPadding()
            && mRecyclerView.getChildAdapterPosition(targetView) == mRecyclerView.getAdapter().getItemCount() - 1))
        return 0;

    int viewCenter = helper.getDecoratedStart(targetView) + (helper.getDecoratedEnd(targetView) - helper.getDecoratedStart(targetView)) / 2;
    int correctCenter = (helper.getEndAfterPadding() - helper.getStartAfterPadding()) / 2;
    return viewCenter - correctCenter;
}

private OrientationHelper getVerticalHelper(RecyclerView.LayoutManager layoutManager) {
    if (mVerticalHelper == null) {
        mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager);
    }
    return mVerticalHelper;
}

private OrientationHelper getHorizontalHelper(RecyclerView.LayoutManager layoutManager) {
    if (mHorizontalHelper == null) {
        mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
    }
    return mHorizontalHelper;
}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值