android recyclerview表格分割线 recyclerview 分页_pagersnaphelper

如上图所示,要想实现此类效果,第一联想到的就是viewpager了,因为它翻页啊,然后处理它的联动以及跟随翻页时效果展示,但是我们今天研究的是用两个Recyclerview去实现,有人就问了,这玩意不是列表么,我咋翻页啊,不会还要自己处理吧,别怕,了解的人都知道了谷歌有个PagerSnapHelper,完美贴合咱们的需求,完成翻页效果,代码大概如下:

PagerSnapHelper snapHelperContent=new PagerSnapHelper();
snapHelperContent.attachToRecyclerView(mRvContent);

ok,这样就实现了翻页效果,想要研究原理的,自行查看源代码,这里不做重点讲述。
接着我们简单进行布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
      />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_top"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="@+id/rv_content"
        android:translationY="-70dp"
        />

</androidx.constraintlayout.widget.ConstraintLayout>

实现简单的上下两个列表的adapter数据填充后,大概效果如下:

android recyclerview表格分割线 recyclerview 分页_pagersnaphelper_02

要想实现理想中的效果,我们必须给列表添加分割线,这里只贴出下面列表的分割线代码:

public class TopItemDecoration extends RecyclerView.ItemDecoration {
    private int mLeftMargin;

    public TopItemDecoration(Context context) {
            mLeftMargin = (Globals.SCREEN_WIDTH - ScreenUtils.dp2px(context,100)) / 2;
    }
    
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        int position = parent.getChildAdapterPosition(view);
        int itemCount = parent.getAdapter().getItemCount();
        int leftMargin;
        int rightMargin;
        if (position == 0) {
            leftMargin = mLeftMargin;
        } else {
            leftMargin = 0;
        }
        if (position == itemCount - 1) {
            rightMargin = mLeftMargin;
        } else {
            rightMargin = 0;
        }
        RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
        layoutParams.setMargins(leftMargin, 0, rightMargin, 0);
        super.getItemOffsets(outRect, view, parent, state);
    }
}

好了,我们再看下效果:

android recyclerview表格分割线 recyclerview 分页_viewpager_03


到这里翻页和大体布局已经完成了,下面就是核心的滑动过程处理了,以下举例下面recyclerview的监听:

@Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (mRvContent.getChildCount() == 1) {
                    View view = mRvContent.getChildAt(0);
                    view.setScaleY(1);
                    view.setScaleX(1);
                } else {
                    for (int i = 0; i < mRvContent.getChildCount(); i++) {
                        View view = mRvContent.getChildAt(i);
                        float rate;
                        int left = (view.getLeft() + view.getRight()) / 2;
                        if (left <= mGalleryMiddle) {
                            if (left < mGalleryMiddle - mGalleryMove) {
                                rate = 1f;
                            } else {
                                rate = (mGalleryMiddle - left) * 1f / mGalleryMove;

                            }
                            view.setScaleY(1 - rate * mGalleryScaleY);
                            view.setScaleX(1 - rate * mGalleryScaleY);
                            view.setTranslationX(rate * mGalleryTranslation);
                        } else {
                            if (left > mGalleryMiddle + mGalleryMove) {
                                rate = 0f;
                            } else {
                                rate = (mGalleryMiddle + mGalleryMove - left) * 1f / mGalleryMove;
                            }
                            view.setScaleY((1 - mGalleryScaleY) + rate * mGalleryScaleY);
                            view.setScaleX((1 - mGalleryScaleY) + rate * mGalleryScaleY);
                            view.setTranslationX((rate - 1) * mGalleryTranslation);
                        }
                    }
                }
            }

同理也可实现顶部列表的滚动变化,难度不大,详见源码。
重点是两个列表如何进行关联呢,肯定在下面列表滚动监听过程中,去触发上面列表的滚动,但是上下布局不一样大,所以我们要等比例进行滚动:

mRvTop.scrollBy(dx * 100 / 327, dy);

乍一看没啥问题,我们跑下代码试试,恩,感觉有那个意思了,不对啊,咋感觉怪怪的,为啥越往后滑,错位越大,不在正中央,这就尴尬了,而且当你缓慢滑动下面列表时,上面列表压根就不叼你。

android recyclerview表格分割线 recyclerview 分页_翻页_04

这是因为public void scrollBy(int x, int y) 这个方法的参数只能传int值

这就导致了精度的丢失,那我们换scrooTo吧,直接一步到位,不就没有什么误差了么,
然而请看recyclerview中:

@Override
    public void scrollTo(int x, int y) {
        Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
                + "Use scrollToPosition instead");
    }

这tm~,不好意思,没忍住,这还咋玩啊,既然recyclerview没有给我们实现,那我们就换个思路,自己去完成“一步到位”的滚动吧。

我们回看mRvTop.scrollBy(dx * 100 / 327, dy);这段代码,此处dx是下面列表每次滑动的距离,然后换算比例得出上面列表需要叠加滑动距离,然后调用scrollBy方法,但是这样误差会一直叠加,所以我们换种思路,下面的列表我们每次拿到总滑动距离,然后换算比例得出上面列表需要滑动的总距离,然后再减去上面列表当前滑动距离,得出来的数据不就是没有误差的滑动差么,这样我们就可以正大光明调用scrollBy方法了:

mTotalContentX += dx;
mRvTop.scrollBy(mTotalContentX * 100 / 327 - mTotalTopX, dy);

大功告成,我们看下最终实现效果:

android recyclerview表格分割线 recyclerview 分页_pagersnaphelper

源码下载