LinearLayoutManager 平滑滚动跳转到错误位置

LinearLayoutManager smooth scroll jumps to wrong position

我正在尝试制作类似于 Instagram 滤镜布局的布局。基本上,当您 select 一个过滤器时,它会滚动到您 selected + 1 的项目,向您显示还有更多过滤器。

我目前正尝试在此处为我的水平 RecyclerView 构建自定义 LinearLayoutManager

public class LinearLayoutSnapManager extends LinearLayoutManager {
    private int mCurrentPos = 0;

    public LinearLayoutSnapManager(Context context) {
        super(context);
    }

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

    public LinearLayoutSnapManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

    }


    public void snap(RecyclerView rv, int position) {
        if (mCurrentPos == position) {
            // No move
            return;
        }

        boolean goingRight = true;
        if (position < mCurrentPos) {
            goingRight = false;
        }
        mCurrentPos = position;
        smoothScrollToPosition(rv, new RecyclerView.State(), goingRight ? getScrollRightPos(): getScrollLeftPos());
    }

    private int getScrollLeftPos() {
        int newPos = mCurrentPos - 1;
        return (newPos > 0) ? newPos : 0;
    }

    private int getScrollRightPos() {
        return mCurrentPos + 1;
    }
}

向左滚动按预期工作,但当我向右滚动时,它似乎只是跳到列表的末尾,而不是 newItem + 1,我不明白为什么会这样。

如果您希望最后选择的筛选器显示在第二位(前面一个筛选器,后面几个筛选器),您的方法不正确。

此时无论方向是右还是左,过程都是一样的。 RecyclerView的位置总是比选中的filter的位置小1,除了第一个元素。

(此示例未针对最新位置进行优化)

public class LinearLayoutSnapManager extends LinearLayoutManager {
    private int mCurrentSelectedPos = 0;

    public LinearLayoutSnapManager(Context context) {
        super(context);
    }

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

    public LinearLayoutSnapManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

    }


    public void snap(RecyclerView rv, int selectedPosition) {
        if (mCurrentSelectedPos == selectedPosition) {
            // No move
            return;
        }

        mCurrentSelectedPos = selectedPosition;

        int newDisplayPos;
        if (mCurrentSelectedPos > 0) {
            newDisplayPos = mCurrentSelectedPos - 1;
        } else {
            newDisplayPos = 0;
        }

        smoothScrollToPosition(rv, new RecyclerView.State(), newDisplayPos);
    }

}

我会建议一个不同的解决方案。 LinearLayoutManager 附带方便的功能:

int findFirstCompletelyVisibleItemPosition()

Returns the adapter position of the first fully visible view.


int findLastCompletelyVisibleItemPosition()

Returns the adapter position of the last fully visible view.


int findFirstVisibleItemPosition()

Returns the adapter position of the first visible view.


int findFirstVisibleItemPosition()

Returns the adapter position of the last visible view.

前两个函数 return 完全可见视图的位置,而后两个 return 甚至部分可见视图。选择取决于您想要实现的行为。

使用它们时,函数 getScrollLeftPosgetScrollRightPos 应如下所示:

private int getScrollLeftPos() {
    int newPos = findFirstCompletelyVisibleItemPosition() - 1;
    return (newPos > 0) ? newPos : 0;
}

private int getScrollRightPos() {
    return findLastCompletelyVisibleItemPosition()+ 1;
}

并且不要调用方法:

smoothScrollToPosition(rv, new RecyclerView.State(), goingRight ? getScrollRightPos(): getScrollLeftPos());

来自 LayoutManager。如果您有对 RecyclerView 的引用,请调用:

rv.smoothScrollToPosition(newPosition);

如果你查看 RecyclerView 的源代码,你会发现这样的实现:

public void smoothScrollToPosition(int position) {
    if (mLayoutFrozen) {
        return;
    }
    if (mLayout == null) {
        Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
                "Call setLayoutManager with a non-null argument.");
        return;
    }
    mLayout.smoothScrollToPosition(this, mState, position);
}

这和你做的基本一样,但是要注意LayoutManager的函数是用适当的RecyclerView.State调用的。刚好符合RecyclerView

我在水平列表中的 onclicklistener 接口回调中调用我的 snap 方法。在调用界面之前,我正在设置所选项目,然后通知视图已更改以更新当前状态。这导致布局管理器返回错误索引的所有问题。

我在创建一个空项目并测试我的逻辑后发现了这一点。令我惊讶的是,它工作得很好,这让我找到了真正的问题。

一旦我将 snaplogic 移动到我所有的视图逻辑之前,一切都按预期工作,而我发布的代码没有任何变化。

希望这对以后的人有所帮助。

这是因为输入的'position'不是布局位置,先找到如下布局位置,然后应用smoothscrolltoposition。

  Common.foodlistnuber = viewHolder.getLayoutPosition(); //position
  new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
  recyclerView.smoothScrollToPosition(Common.foodlistnuber);
        }
    }, 200);