RecyclerView SnapHelper查找被捕捉项目的确切位置

时间:2018-08-05 05:20:13

标签: android android-recyclerview

如何找到捕捉的项目的索引? 我尝试使用:

recyclerView.setOnFlingListener(new RecyclerView.OnFlingListener() {
        @Override
        public boolean onFling(int velocityX, int velocityY) {
            Log.i(TAG, "initRecyclerView: SNAPPING TO:" +snapHelper.findTargetSnapPosition(recyclerView.getLayoutManager(),velocityX,velocityY)%songs.size());
            return false;
        }
    });

但它确实不准确,有时返回-1。

我尝试过:

snapHelper.findSnapView(recyclerView.getLayoutManager());

但是它返回一个视图,我不认为我可以在recyclerview中获取视图的索引。 (我错了吗?)

我不希望方法何时返回索引(在清除完成之前或之后)。

ps:我的recyclerview有一个自定义的ZoomingLayoutManager(用于轮播效果)。 也许它正在改变视图的大小,这会导致snapHelper错误?我知道 snapHelper可以处理可见的childViews的平均高度和宽度

public class ZoomingLayoutManager extends LinearLayoutManager {
    private final float mShrinkAmount = 0.5f;
    private final float mShrinkDistance = 0.9f;

    public ZoomingLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        int orientation = getOrientation();
        if (orientation == HORIZONTAL) {
            int scrolled = super.scrollHorizontallyBy(dx, recycler, state);

            float midpoint = getWidth() / 2.f;
            float d0 = 0.f;
            float d1 = mShrinkDistance * midpoint;
            float s0 = 1.f;
            float s1 = 1.f - mShrinkAmount;
            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                float childMidpoint =
                        (getDecoratedRight(child) + getDecoratedLeft(child)) / 2.f;
                float d = Math.min(d1, Math.abs(midpoint - childMidpoint));
                float scale = s0 + (s1 - s0) * (d - d0) / (d1 - d0);
                child.setScaleX(scale);
                child.setScaleY(scale);
            }
            return scrolled;
        } else {
            return 0;
        }

    }

    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        int orientation = getOrientation();
        if (orientation == VERTICAL) {
            int scrolled = super.scrollVerticallyBy(dy, recycler, state);
            float midpoint = getHeight() / 2.f;
            float d0 = 0.f;
            float d1 = mShrinkDistance * midpoint;
            float s0 = 1.f;
            float s1 = 1.f - mShrinkAmount;
            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                float childMidpoint =
                        (getDecoratedBottom(child) + getDecoratedTop(child)) / 2.f;
                float d = Math.min(d1, Math.abs(midpoint - childMidpoint));
                float scale = s0 + (s1 - s0) * (d - d0) / (d1 - d0);
                child.setScaleX(scale);
                child.setScaleY(scale);
            }
            return scrolled;
        } else {
            return 0;
        }
    }
}

如果是真的,如何覆盖snapHelper方法以找到更准确的位置?还是我只是想念一些明显的东西!?

2 个答案:

答案 0 :(得分:0)

您可以尝试这样的事情;在我的情况下,只有一个项目位于屏幕中央,左右项目仅部分可见,并且效果很好。

添加一个onScrollListener并等待 SCROLL_STATE_SETTLING 状态,然后获取 FirstCompletelyVisibleItemPosition

注意::我使用了Handler,延迟了 1秒,如下所示,您可以删除该部分,而只能使用findFirstCompletelyVisibleItemPosition()。用于确定滚动状态。

    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);

            if (newState == RecyclerView.SCROLL_STATE_SETTLING) {
                new Handler().postDelayed(() -> {
                    int pos = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
                    if (pos >= 0) {
                        // do your thing, the pos value must be your first fully visible or snapped view
                    }
                }, 1000);

            }
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);

        }
    });

答案 1 :(得分:0)

因此,由于有了OğuzhanDöngül的提示,我知道了使用findFirstCompletelyVisibleItemPosition()的位置,但是在那里“何时”调用该方法很重要。 滚动完成后,recyclerview的状态更改为“ idle”,因此感谢您可以使用提供的方法获取第一个完全可见视图的确切位置。 但是,如果第一个可见视图的位置不是您想要的,并且您希望视图的位置在屏幕中间怎么办?

好吧,您可以通过计算在中间项目和屏幕边框之间的屏幕中完全可见的视图数量(实际上非​​常简单)来添加偏移量

so,代码:

 recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            if(newState==RecyclerView.SCROLL_STATE_IDLE){
                int pos = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
                if (pos >= 0) {
                    int position=pos+ bigScreenOffset;
                    // do your thing, the position value is the exact index of the middle item
                }
            }
        }
    });

bigScreenOffset的计算依据:

        bigScreenOffset= ( Utility.getDisplaySize(this,true,true)/200)/2;

和我在dp | px中处理width | height的实用程序类中的自定义方法是:

 public static int getDisplaySize(Context context, boolean width, boolean dp) {
    DisplayMetrics displayMetrics = new DisplayMetrics();
    ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    if (width) {
        if (dp)
            return (int) convertPixelsToDp(displayMetrics.widthPixels, context);
        else
            return displayMetrics.widthPixels;
    } else {
        if (dp)
            return (int) convertPixelsToDp(displayMetrics.heightPixels, context);
        else
            return displayMetrics.heightPixels;
    }
}
相关问题