当前位置: 首页 > 知识库问答 >
问题:

recyclerview向左水平滚动

萧永长
2023-03-14

我正在制作一个应用程序,其中我使用recyclerViewsnaphelper类来制作一个可滚动的水平卡片堆栈。我已经成功地实现了,但我的问题是刷卡向右的方向,我想刷卡向左的方向。

我对SO做了一些研究,发现了1和2

更新:我仍然在努力,几乎没有发现很多,因为它仍然很难改变滚动的方向在回收,所以任何人仍然想帮助我,是欢迎的。

我还发现findsnapview方法传递滚动位置和direcion可能我错了,但beow code可能是解决方案

**@Override
    public View findSnapView(RecyclerView.LayoutManager layoutManager) {
        if (layoutManager instanceof LadderLayoutManager) {
            int pos = ((LadderLayoutManager) layoutManager).getFixedScrollPosition(
                    mDirection, mDirection != 0 ? 0.8f : 0.5f);
            mDirection = 0;
            if (pos != RecyclerView.NO_POSITION) {
                return layoutManager.findViewByPosition(pos);
            }
        }
        return null;
    }**

我的主要班级

public class MainActivity extends AppCompatActivity {
    LadderLayoutManager llm;
    RecyclerView rcv;
    HSAdapter adapter;
    int scrollToPosition;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        llm = new LadderLayoutManager(1.5f, 0.85f, LadderLayoutManager.HORIZONTAL).
                setChildDecorateHelper(new LadderLayoutManager
                        .DefaultChildDecorateHelper(getResources().getDimension(R.dimen.item_max_elevation)));
        llm.setChildPeekSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                30, getResources().getDisplayMetrics()));
        llm.setMaxItemLayoutCount(5);

        rcv = (RecyclerView) findViewById(R.id.rcv);
        rcv.setLayoutManager(llm);

        new LadderSimpleSnapHelper().attachToRecyclerView(rcv);
        adapter = new HSAdapter();
        rcv.setAdapter(adapter);

        final SeekBar sb = (SeekBar) findViewById(R.id.sb);
    }
public class LadderLayoutManager extends RecyclerView.LayoutManager implements RecyclerView.SmoothScroller.ScrollVectorProvider {
    private static final int INVALIDATE_SCROLL_OFFSET = Integer.MAX_VALUE;
    private static final float DEFAULT_CHILD_LAYOUT_OFFSET = 0.2f;
    public static final int UNLIMITED = 0;
    public static final int VERTICAL = 1;
    public static final int HORIZONTAL = 0;
    private boolean mCheckedChildSize;
    private int[] mChildSize;
    private int mChildPeekSize;
    private int mChildPeekSizeInput;
    private boolean mReverse;
    private int mScrollOffset = INVALIDATE_SCROLL_OFFSET;
    private float mItemHeightWidthRatio;
    private float mScale;
    private int mChildCount;
    private float mVanishOffset = 0;
    private Interpolator mInterpolator;
    private int mOrientation;
    private ChildDecorateHelper mDecorateHelper;
    private int mMaxItemLayoutCount;

    public LadderLayoutManager(float itemHeightWidthRatio) {
        this(itemHeightWidthRatio, 0.9f, VERTICAL);
    }


    public LadderLayoutManager(float itemHeightWidthRatio, float scale, int orientation) {
        this.mItemHeightWidthRatio = itemHeightWidthRatio;
        this.mOrientation = orientation;
        this.mScale = scale;
        this.mChildSize = new int[2];
        this.mInterpolator = new DecelerateInterpolator();
    }

    @Override
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
        return new RecyclerView.LayoutParams(mChildSize[0], mChildSize[1]);
    }

    public LadderLayoutManager setChildDecorateHelper(ChildDecorateHelper layoutHelper) {
        mDecorateHelper = layoutHelper;
        return this;
    }

    public void setMaxItemLayoutCount(int count) {
        mMaxItemLayoutCount = Math.max(2, count);
        if (getChildCount() > 0) {
            requestLayout();
        }
    }


    public void setVanishOffset(float offset) {
        mVanishOffset = offset;
        if (getChildCount() > 0) {
            requestLayout();
        }
    }


    public void setChildPeekSize(int childPeekSize) {
        mChildPeekSizeInput = childPeekSize;
        mCheckedChildSize = false;
        if (getChildCount() > 0) {
            requestLayout();
        }
    }


    public void setItemHeightWidthRatio(float itemHeightWidthRatio) {
        mItemHeightWidthRatio = itemHeightWidthRatio;
        mCheckedChildSize = false;
        if (getChildCount() > 0) {
            requestLayout();
        }
    }


    public void setReverse(boolean reverse) {
        if (mReverse != reverse) {
            mReverse = reverse;
            if (getChildCount() > 0) {
                requestLayout();
            }
        }
    }

    public boolean isReverse() {
        return mReverse;
    }



    public int getFixedScrollPosition(int direction, float fixValue) {
        if (mCheckedChildSize) {
            if (mScrollOffset % mChildSize[mOrientation] == 0) {
                return RecyclerView.NO_POSITION;
            }
            float position = mScrollOffset * 1.0f / mChildSize[mOrientation];
            return convert2AdapterPosition((int) (direction > 0 ? position + fixValue : position + (1 - fixValue)) - 1);
        }
        return RecyclerView.NO_POSITION;
    }


    @Override
    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
        super.onMeasure(recycler, state, widthSpec, heightSpec);
        mCheckedChildSize = false;
    }

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (state.getItemCount() == 0) {
            removeAndRecycleAllViews(recycler);
            return;
        }
        if (!mCheckedChildSize) {
            if (mOrientation == VERTICAL) {
                mChildSize[0] = getHorizontalSpace();
                mChildSize[1] = (int) (mItemHeightWidthRatio * mChildSize[0]);
            } else {
                mChildSize[1] = getVerticalSpace();
                mChildSize[0] = (int) (mChildSize[1] / mItemHeightWidthRatio);
            }
            mChildPeekSize = mChildPeekSizeInput == 0 ?
                    (int) (mChildSize[mOrientation] * DEFAULT_CHILD_LAYOUT_OFFSET) : mChildPeekSizeInput;
            mCheckedChildSize = true;
        }
        int itemCount = getItemCount();
        if (mReverse) {
            mScrollOffset += (itemCount - mChildCount) * mChildSize[mOrientation];
        }
        mChildCount = itemCount;
        mScrollOffset = makeScrollOffsetWithinRange(mScrollOffset);
        fill(recycler);
    }

    public void fill(RecyclerView.Recycler recycler) {
        int bottomItemPosition = (int) Math.floor(mScrollOffset / mChildSize[mOrientation]);//>=1
        int bottomItemVisibleSize = mScrollOffset % mChildSize[mOrientation];
        final float offsetPercent = mInterpolator.getInterpolation(
                bottomItemVisibleSize * 1.0f / mChildSize[mOrientation]);//[0,1)
        final int space = mOrientation == VERTICAL ? getVerticalSpace() : getHorizontalSpace();

        ArrayList<ItemLayoutInfo> layoutInfos = new ArrayList<>();
        for (int i = bottomItemPosition - 1, j = 1, remainSpace = space - mChildSize[mOrientation];
             i >= 0; i--, j++) {
            double maxOffset = mChildPeekSize * Math.pow(mScale, j);
            int start = (int) (remainSpace - offsetPercent * maxOffset);
            ItemLayoutInfo info = new ItemLayoutInfo(start,
                    (float) (Math.pow(mScale, j - 1) * (1 - offsetPercent * (1 - mScale))),
                    offsetPercent,
                    start * 1.0f / space
            );
            layoutInfos.add(0, info);

            if (mMaxItemLayoutCount != UNLIMITED && j == mMaxItemLayoutCount - 1) {
                if (offsetPercent != 0) {
                    info.start = remainSpace;
                    info.positionOffsetPercent = 0;
                    info.layoutPercent = remainSpace / space;
                    info.scaleXY = (float) Math.pow(mScale, j - 1);
                }
                break;
            }
            remainSpace -= maxOffset;
            if (remainSpace <= 0) {
                info.start = (int) (remainSpace + maxOffset);
                info.positionOffsetPercent = 0;
                info.layoutPercent = info.start / space;
                info.scaleXY = (float) Math.pow(mScale, j - 1);
                break;
            }
        }

        if (bottomItemPosition < mChildCount) {
            final int start = space - bottomItemVisibleSize;
            layoutInfos.add(new ItemLayoutInfo(start, 1.0f,
                    bottomItemVisibleSize * 1.0f / mChildSize[mOrientation], start * 1.0f / space).
                    setIsBottom());
        } else {
            bottomItemPosition -= 1;
        }

        int layoutCount = layoutInfos.size();

        final int startPos = bottomItemPosition - (layoutCount - 1);
        final int endPos = bottomItemPosition;
        final int childCount = getChildCount();
        for (int i = childCount - 1; i >= 0; i--) {
            View childView = getChildAt(i);
            int pos = convert2LayoutPosition(getPosition(childView));
            if (pos > endPos || pos < startPos) {
                removeAndRecycleView(childView, recycler);
            }
        }
        detachAndScrapAttachedViews(recycler);

        for (int i = 0; i < layoutCount; i++) {
            fillChild(recycler.getViewForPosition(convert2AdapterPosition(startPos + i)), layoutInfos.get(i));
        }
    }

    private void fillChild(View view, ItemLayoutInfo layoutInfo) {
        addView(view);
        measureChildWithExactlySize(view);
        final int scaleFix = (int) (mChildSize[mOrientation] * (1 - layoutInfo.scaleXY) / 2);
        final float gap = (mOrientation == VERTICAL ? getHorizontalSpace() : getVerticalSpace())
                - mChildSize[(mOrientation + 1) % 2] * layoutInfo.scaleXY;

        if (mOrientation == VERTICAL) {
            int left = (int) (getPaddingLeft() + (gap * 0.5 * mVanishOffset));
            layoutDecoratedWithMargins(view, left, layoutInfo.start - scaleFix
                    , left + mChildSize[0], layoutInfo.start + mChildSize[1] - scaleFix);
        } else {
            int top = (int) (getPaddingTop() + (gap * 0.5 * mVanishOffset));
            layoutDecoratedWithMargins(view, layoutInfo.start - scaleFix, top
                    , layoutInfo.start + mChildSize[0] - scaleFix, top + mChildSize[1]);
        }
        ViewCompat.setScaleX(view, layoutInfo.scaleXY);
        ViewCompat.setScaleY(view, layoutInfo.scaleXY);
        if (mDecorateHelper != null) {
            mDecorateHelper.decorateChild(view, layoutInfo.positionOffsetPercent, layoutInfo.layoutPercent, layoutInfo.isBottom);
        }
    }

    private void measureChildWithExactlySize(View child) {
        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
        final int widthSpec = View.MeasureSpec.makeMeasureSpec(
                mChildSize[0] - lp.leftMargin - lp.rightMargin, View.MeasureSpec.EXACTLY);
        final int heightSpec = View.MeasureSpec.makeMeasureSpec(
                mChildSize[1] - lp.topMargin - lp.bottomMargin, View.MeasureSpec.EXACTLY);
        child.measure(widthSpec, heightSpec);
    }

    private int makeScrollOffsetWithinRange(int scrollOffset) {
        return Math.min(Math.max(mChildSize[mOrientation], scrollOffset), mChildCount * mChildSize[mOrientation]);
    }


    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        int pendingScrollOffset = mScrollOffset + dy;
        mScrollOffset = makeScrollOffsetWithinRange(pendingScrollOffset);
        fill(recycler);
        return mScrollOffset - pendingScrollOffset + dy;
    }

    @Override
    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        int pendingScrollOffset = mScrollOffset + dx;
        mScrollOffset = makeScrollOffsetWithinRange(pendingScrollOffset);
        fill(recycler);
        return mScrollOffset - pendingScrollOffset + dx;
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
        final LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
            @Override
            public int calculateDyToMakeVisible(final View view, final int snapPreference) {
                if (mOrientation == VERTICAL) {
                    return -calculateDistanceToPosition(getPosition(view));
                }
                return 0;
            }

            @Override
            public int calculateDxToMakeVisible(final View view, final int snapPreference) {
                if (mOrientation == HORIZONTAL) {
                    return -calculateDistanceToPosition(getPosition(view));
                }
                return 0;
            }
        };
        linearSmoothScroller.setTargetPosition(position);
        startSmoothScroll(linearSmoothScroller);
    }


    public int calculateDistanceToPosition(int targetPos) {
        int pendingScrollOffset = mChildSize[mOrientation] * (convert2LayoutPosition(targetPos) + 1);
        return pendingScrollOffset - mScrollOffset;
    }


    @Override
    public void scrollToPosition(int position) {
        if (position > 0 && position < mChildCount) {
            mScrollOffset = mChildSize[mOrientation] * (convert2LayoutPosition(position) + 1);
            requestLayout();
        }
    }


    @Override
    public boolean canScrollVertically() {
        return mOrientation == VERTICAL;
    }

    @Override
    public boolean canScrollHorizontally() {
        return mOrientation == HORIZONTAL;
    }

    public int convert2AdapterPosition(int layoutPosition) {
        return mReverse ? mChildCount - 1 - layoutPosition : layoutPosition;
    }

    public int convert2LayoutPosition(int adapterPostion) {
        return mReverse ? mChildCount - 1 - adapterPostion : adapterPostion;
    }

    public int getVerticalSpace() {
        return getHeight() - getPaddingTop() - getPaddingBottom();
    }

    public int getHorizontalSpace() {
        return getWidth() - getPaddingLeft() - getPaddingRight();
    }

    @Override
    public PointF computeScrollVectorForPosition(int targetPosition) {
        int pos = convert2LayoutPosition(targetPosition);
        int scrollOffset = (pos + 1) * mChildSize[mOrientation];
        return mOrientation == VERTICAL ? new PointF(0, Math.signum(scrollOffset - mScrollOffset))
                : new PointF(Math.signum(scrollOffset - mScrollOffset), 0);
    }

    private static class ItemLayoutInfo {
        float scaleXY;
        float layoutPercent;
        float positionOffsetPercent;
        int start;
        boolean isBottom;

        ItemLayoutInfo(int top, float scale, float positonOffset, float percent) {
            this.start = top;
            this.scaleXY = scale;
            this.positionOffsetPercent = positonOffset;
            this.layoutPercent = percent;
        }

        ItemLayoutInfo setIsBottom() {
            isBottom = true;
            return this;
        }

    }


    @Override
    public Parcelable onSaveInstanceState() {
        SavedState savedState = new SavedState();
        savedState.scrollOffset = mScrollOffset;
        savedState.reverse = mReverse;
        savedState.vanishOffset = mVanishOffset;
        savedState.scale = mScale;
        savedState.childLayoutOffsetInput = mChildPeekSizeInput;
        savedState.itemHeightWidthRatio = mItemHeightWidthRatio;
        savedState.orientation = mOrientation;
        return savedState;
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        if (state instanceof SavedState) {
            SavedState s = (SavedState) state;
            mScrollOffset = s.scrollOffset;
            mReverse = s.reverse;
            mVanishOffset = s.vanishOffset;
            mScale = s.scale;
            mChildPeekSizeInput = s.childLayoutOffsetInput;
            mItemHeightWidthRatio = s.itemHeightWidthRatio;
            mOrientation = s.orientation;
            requestLayout();
        }
    }

    public static class SavedState implements Parcelable {

        int scrollOffset, childLayoutOffsetInput, orientation;
        float itemHeightWidthRatio, scale, elevation, vanishOffset;
        boolean reverse;

        public SavedState() {
        }

        SavedState(LadderLayoutManager.SavedState other) {
            scrollOffset = other.scrollOffset;
            childLayoutOffsetInput = other.childLayoutOffsetInput;
            orientation = other.orientation;
            itemHeightWidthRatio = other.itemHeightWidthRatio;
            scale = other.scale;
            elevation = other.elevation;
            vanishOffset = other.vanishOffset;
            reverse = other.reverse;
        }

        SavedState(Parcel in) {
            scrollOffset = in.readInt();
            childLayoutOffsetInput = in.readInt();
            orientation = in.readInt();
            itemHeightWidthRatio = in.readFloat();
            scale = in.readFloat();
            elevation = in.readFloat();
            vanishOffset = in.readFloat();
            reverse = in.readInt() == 1;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(scrollOffset);
            dest.writeInt(childLayoutOffsetInput);
            dest.writeInt(orientation);
            dest.writeFloat(itemHeightWidthRatio);
            dest.writeFloat(scale);
            dest.writeFloat(elevation);
            dest.writeFloat(vanishOffset);
            dest.writeInt(reverse ? 1 : 0);
        }

        @Override
        public int describeContents() {
            return 0;
        }

        public static final Creator<SavedState> CREATOR
                = new Creator<SavedState>() {
            @Override
            public LadderLayoutManager.SavedState createFromParcel(Parcel in) {
                return new LadderLayoutManager.SavedState(in);
            }

            @Override
            public LadderLayoutManager.SavedState[] newArray(int size) {
                return new LadderLayoutManager.SavedState[size];
            }
        };
    }


    public interface ChildDecorateHelper {

        void decorateChild(View child, float posOffsetPercent, float layoutPercent, boolean isBottom);
    }

    public static class DefaultChildDecorateHelper implements ChildDecorateHelper {
        private float mElevation;

        public DefaultChildDecorateHelper(float maxElevation) {
            mElevation = maxElevation;
        }

        @Override
        public void decorateChild(View child, float posOffsetPercent, float layoutPercent, boolean isBottom) {
            ViewCompat.setElevation(child, (float) (layoutPercent * mElevation * 0.7 + mElevation * 0.3));
        }
    }
}

这是我的snaphelper代码

public class LadderSimpleSnapHelper extends SnapHelper {
    private int mDirection;
    //int position = layoutManager.getPosition(centerView);
    @Override
    public int[] calculateDistanceToFinalSnap(
            @NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) {

        if (layoutManager instanceof LadderLayoutManager) {
            int[] out = new int[2];
            if (layoutManager.canScrollHorizontally()) {
                out[0] = ((LadderLayoutManager) layoutManager).calculateDistanceToPosition(
                        layoutManager.getPosition(targetView));
                out[1] = 0;
            } else {
                out[0] = 0;
                out[1] = ((LadderLayoutManager) layoutManager).calculateDistanceToPosition(
                        layoutManager.getPosition(targetView));
            }
            return out;
        }
        return null;
    }

    @Override
    public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX,
                                      int velocityY) {
        if (layoutManager.canScrollHorizontally()) {

                mDirection = velocityX;
            } else {
                mDirection = velocityY;
            }
        return RecyclerView.NO_POSITION;
    }

    @Override
    public View findSnapView(RecyclerView.LayoutManager layoutManager) {
        if (layoutManager instanceof LadderLayoutManager) {
            int pos = ((LadderLayoutManager) layoutManager).getFixedScrollPosition(
                    mDirection, mDirection != 0 ? 0.8f : 0.5f);
            mDirection = 0;
            if (pos != RecyclerView.NO_POSITION) {
                return layoutManager.findViewByPosition(pos);
            }
        }
        return null;
    }
}

共有1个答案

方韬
2023-03-14

请尝试以下代码:

mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true));
mRecyclerView.setReverseLayout(true);
 类似资料:
  • 我正在尝试在Android中实现一个自定义线性布局管理器。用于获得水平自动滑动的RecolyerView。但是当我试图将自定义类调用到主java类中时,我会遇到一些问题。 下面列出了我的代码所面临的问题。 请告诉上述错误的解决方法。另外,请精确的代码,以实现水平自动滑动回收视图。使用我已经提到的自定义线性布局管理器。

  • 正如标题所说:如何将水平滚动条添加到我用JFreeChart创建的阴阳烛图表中?我希望用户在放大时能够水平滚动图表。现在放大起作用了,但我不能向左或向右移动。我试着把图表面板放入JScrollPane,但那是一个图表面板,而不是图表本身。我自定义的ChartPanel构造函数: 我的自定义Jboard,我在其中创建图表和图表面板,并将图表面板放在JScrollPane中: 然后,我将MyCandl

  • 问题内容: 我正在尝试创建一系列只能水平滚动的照片。 它应该看起来像这样的 但是,只有通过指定包含照片的的宽度,才能实现上述目的(因此它们不会“自动换行”)。如果我不放宽- 看起来像这样; 使用CSS可以防止固定宽度的图像发生变化,该怎么办。 谢谢 问题答案: 您可以使用与。这样写:

  • 我做了一个水平循环视图,效果很好(多亏了这个),但滚动和数据的方向是从左向右扩展的;那么,我该如何更改RecyclerView滚动方向,如下图所示? 我的代码:

  • 我需要具有水平滚动行为的RecyclerView Gridlayout管理器。它必须添加子视图,就像它具有垂直方向时一样(例如(行1,列1)=索引0,(行1,列2)=索引1等等)。 目前垂直方向不允许水平滚动,并且适合设备宽度内的子视图。 编辑: 我在RecyclerView GridLayoutManager中有100行和150列,需要水平滚动视图。我隐藏和显示行,这只有在垂直方向上才可能。

  • 在Chrome上,页面上有一个水平滚动条,方向为rtl,与和组合,即使没有边距和内容。 有关示例,请参见https://jsfiddle.net/ht3drjae/2/.我希望看到没有绿色的背景色和没有水平滚动条,因为内部和外部应该有相同的宽度。 CSS: