如何使用回收器视图模仿视图寻呼机?

por*_*der 5 android android-animation android-viewpager android-recyclerview

ViewPager 比 RecyclerView 的优势之一是它有一个 setPageTransformer() 监听器。在这里,您可以操作页面并将其转换为在滑动之间或滑动时执行简洁的动画。我想模仿能够在使用 RecyclerView 时转换页面。

首先,我使用 LinearSnapHelper 模仿 ViewPager 的行为

    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(recyclerview);
Run Code Online (Sandbox Code Playgroud)

这工作完美,看起来非常好。现在,我想在滑动页面(项目)时对其进行转换。使用 ViewPager,我可以使用以下代码执行此操作:

    viewpager.setPageTransformer(false, new ViewPager.PageTransformer() {
        private static final float MIN_SCALE = 0.85f;

        public void transformPage(@NonNull View view, float position) {
            int pageWidth = view.getWidth();
            int pageHeight = view.getHeight();

            // modify the default slide transition to shrink the page
            float scaleFactor = Math.max(MIN_SCALE, 1 - (Math.abs(position) / 5));
            float vertMargin = pageHeight * (1 - scaleFactor) / 2;
            float horzMargin = pageWidth * (1 - scaleFactor) / 2;
            if (position < 0) {
                view.setTranslationX(horzMargin - vertMargin / 2);
            } else {
                view.setTranslationX(-horzMargin + vertMargin / 2);
            }

            // scale the page down (between MIN_SCALE and 1)
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);
        }
    });
Run Code Online (Sandbox Code Playgroud)

由于 RecyclerViews 没有页面转换器侦听器,我认为等效项将在 ScrollChangeListener 上。所以我将代码添加到该侦听器

    recyclerview.setOnScrollChangeListener(new View.OnScrollChangeListener() {
        @Override
        public void onScrollChange(View view, int i, int i1, int i2, int i3) {
            int pageWidth = view.getWidth();
            int pageHeight = view.getHeight();
            // modify the default slide transition to shrink the page
            float scaleFactor = Math.max(0.85f, 1 - (Math.abs(mLayoutManager.findFirstVisibleItemPosition()) / 5));
            float vertMargin = pageHeight * (1 - scaleFactor) / 2;
            float horzMargin = pageWidth * (1 - scaleFactor) / 2;
            if (mLayoutManager.findFirstVisibleItemPosition() < 0) {
                view.setTranslationX(horzMargin - vertMargin / 2);
            } else {
                view.setTranslationX(-horzMargin + vertMargin / 2);
            }

            // scale the page down (between MIN_SCALE and 1)
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);
        }
    });
Run Code Online (Sandbox Code Playgroud)

该页面根本没有缩放。一切似乎都被忽略了。我应该把转换代码放在哪里?我希望每个项目在您在项目之间切换时缩小和增长。想想 Carousal 动画,例如https://github.com/jgabrielfreitas/android-carousel

编辑:尝试在 onScroll 中管理转换视图太困难了,所以我最终修改了 LayoutManager。这很好用。我只需要弄清楚如何偷看。这是我想出的,它具有我正在寻找的轮播动画外观。

public class CustomLinearLayoutManager extends LinearLayoutManager {

    private final float mShrinkAmount = 0.15f;
    private final float mShrinkDistance = 1.0f;

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

    @Override
    public int scrollVerticallyBy(int dy, @NonNull RecyclerView.Recycler recycler, @NonNull RecyclerView.State state) {
        int orientation = getOrientation();
        if (orientation == VERTICAL) {
            int scrolled = super.scrollVerticallyBy(dy, recycler, state);
            float midpoint = getHeight() / 2.0f;
            float d0 = 0.0f;
            float d1 = mShrinkDistance * midpoint;
            float s0 = 1.0f;
            float s1 = 1.0f - mShrinkAmount;
            // loop through active children and set scale of child
            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                float childMidpoint = (getDecoratedBottom(child) + getDecoratedTop(child)) / 2.0f;
                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 scrollHorizontallyBy(int dx, @NonNull RecyclerView.Recycler recycler, @NonNull RecyclerView.State state) {
        int orientation = getOrientation();
        if (orientation == HORIZONTAL) {
            int scrolled = super.scrollHorizontallyBy(dx, recycler, state);
            float midpoint = getWidth() / 2.0f;
            float d0 = 0.0f;
            float d1 = mShrinkDistance * midpoint;
            float s0 = 1.0f;
            float s1 = 1.0f - mShrinkAmount;
            // loop through active children and set scale of child
            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                float childMidpoint = (getDecoratedRight(child) + getDecoratedLeft(child)) / 2.0f;
                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 void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        super.onLayoutChildren(recycler, state);
        scrollVerticallyBy(0, recycler, state);
    }
}
Run Code Online (Sandbox Code Playgroud)

小智 1

我实现了你的(@portfoliobuilder)类,但它只向我显示垂直回收器视图,我想要回收器视图的水平方向,但不知何故它不模仿水平方向。如果我自定义你的类,那么它就会停止模仿 viewpager2 并成为正常的回收器视图。

public class CustomLinearLayoutManager extends LinearLayoutManager {

private final float mShrinkAmount = 0.15f;
private final float mShrinkDistance = 1.0f;

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

@Override
public int scrollVerticallyBy(int dy, @NonNull RecyclerView.Recycler recycler, @NonNull RecyclerView.State state) {
    int orientation = getOrientation();
    if (orientation == VERTICAL) {
        int scrolled = super.scrollVerticallyBy(dy, recycler, state);
        float midpoint = getHeight() / 2.0f;
        float d0 = 0.0f;
        float d1 = mShrinkDistance * midpoint;
        float s0 = 1.0f;
        float s1 = 1.0f - mShrinkAmount;
        // loop through active children and set scale of child
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            float childMidpoint = (getDecoratedBottom(child) + getDecoratedTop(child)) / 2.0f;
            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 scrollHorizontallyBy(int dx, @NonNull RecyclerView.Recycler recycler, @NonNull RecyclerView.State state) {
    int orientation = getOrientation();
    if (orientation == HORIZONTAL) {
        int scrolled = super.scrollHorizontallyBy(dx, recycler, state);
        float midpoint = getWidth() / 2.0f;
        float d0 = 0.0f;
        float d1 = mShrinkDistance * midpoint;
        float s0 = 1.0f;
        float s1 = 1.0f - mShrinkAmount;
        // loop through active children and set scale of child
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            float childMidpoint = (getDecoratedRight(child) + getDecoratedLeft(child)) / 2.0f;
            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 void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    super.onLayoutChildren(recycler, state);
    scrollVerticallyBy(0, recycler, state);
}
}
Run Code Online (Sandbox Code Playgroud)