Android 左右竖直滚动的Table(二)

前言

  大家好呀,最近烦事特别多,现在好不容易静下心写第二篇Table系列文章,第一篇文章:Android 左右竖直滚动的Table(一)主要是用HorizontalScrollView+NestScrollView+RecyclerView实现Table复杂的滚动效果(效果动图可看上面第一篇文章),但是这个会带来一个致命的问题就是Item的不能够复用,因为ScrollView的特性,它会一次性渲染全部的item,所以数据量到几百的时候,要很久才能进来这个页面,这个东西给产品经理看到还不diss你。。。嗯,今天主要介绍单纯地用RecyclerView来实现这个Table复杂的滑动效果吧。


分析

  同样的,我们上次系列一文章已经分析过了滑动的设计,并且给出了大概的布局,今天我们就把ScrollView的控件全部给剔除掉,只剩下RecyclerView会是怎样的?下面是我们画出大概的布局:
在这里插入图片描述
现在我们很容易看出要想实现效果,必须RecyclerView3和RecyclerView2保持竖直滚动,RecyclerView2和RecyclerView1保持水平滚动,而RecyclerView2采用嵌套RecyclerView来展示table数据,所以与RecyclerView1保持水平滚动的对象是RecyclerView2中嵌套的水平RecyclerView,这样就实现了水平竖直滚动的Table。


代码设计

1.布局文件

主布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_content_table"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/corner_view_height"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_table_title"
            android:layout_width="@dimen/corner_view_width"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="表格"
            android:textColor="#000000"
            android:textSize="14sp" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_column_header"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#EAEAEA"
            android:overScrollMode="never" />

    </LinearLayout>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">


        <!-- 左侧header的父容器 -->
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_row_header"
            android:layout_width="@dimen/corner_view_width"
            android:layout_height="wrap_content"
            android:background="#EAEAEA" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_cell_parent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:overScrollMode="never" />
    </LinearLayout>

</LinearLayout>

其它item的布局文件和Android 左右竖直滚动的Table(一)是一样的:

  1. 列标题布局文件item_table_reuse_column:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="@dimen/column_width"
    android:layout_height="@dimen/corner_view_height"
    android:layout_gravity="center">

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="测试"
        android:padding="3dp"
        android:textSize="14sp" />
</LinearLayout>
  1. 行标题布局文件item_table_reuse_row:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="@dimen/corner_view_width"
    android:layout_height="@dimen/row_height"
    android:layout_gravity="center">

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:padding="3dp"
        android:text="测试"
        android:textSize="14sp" />
</LinearLayout>
  1. cell的父布局文件item_table_reuse_cell_parent:
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView 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:id="@+id/rv_cell_child"
    android:layout_width="match_parent"
    android:layout_height="@dimen/row_height"
    android:orientation="horizontal"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
    tools:listitem="@layout/item_table_blog_cell_child" />
  1. cell的父布局RecyclerView的item布局文件item_table_reuse_cell_child:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="@dimen/column_width"
    android:layout_height="@dimen/row_height"
    android:layout_gravity="center">

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="测试"
        android:padding="3dp"
        android:textSize="14sp" />
</LinearLayout>

dimens文件:

    <dimen name="corner_view_width">50dp</dimen>
    <dimen name="corner_view_height">50dp</dimen>

    <dimen name="column_width">100dp</dimen>
    <dimen name="row_height">40dp</dimen>

2.Activity页面


public class VHSlideTableActivity extends AppCompatActivity {
    private RecyclerView mRvColumnHeader;
    private RecyclerView mRvRowHeader;
    private RecyclerView mRvCellParent;

    private CommonAdapter mColumnAdapter, mRowAdapter;

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

        mRvColumnHeader = findViewById(R.id.rv_column_header);
        mRvRowHeader = findViewById(R.id.rv_row_header);
        mRvCellParent = findViewById(R.id.rv_cell_parent);


        List<String> columnList = getColumnHeaderList();
        List<String> rowList = getRowHeaderList();
        List<List<String>> cellList = getCellList();

        mRvColumnHeader.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));
        mColumnAdapter = new CommonAdapter(R.layout.item_table_reuse_column, columnList);
        mColumnAdapter.setOnItemChildClickListener((adapter, view, position) -> {
            String name = mColumnAdapter.getItem(position);
            Toast.makeText(this, name, Toast.LENGTH_SHORT).show();
        });
        mRvColumnHeader.setAdapter(mColumnAdapter);
        mRvColumnHeader.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL));

        CellParentAdapter cellParentAdapter = new CellParentAdapter(R.layout.item_table_reuse_cell_parent, cellList);
        mRvCellParent.setLayoutManager(new LinearLayoutManager(this));
        mRvCellParent.setAdapter(cellParentAdapter);
        mRvCellParent.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));

        mRvRowHeader.setLayoutManager(new LinearLayoutManager(this));
        mRowAdapter = new CommonAdapter(R.layout.item_table_reuse_row, rowList);
        mRowAdapter.setOnItemChildClickListener((adapter, view, position) -> {
            String name = mRowAdapter.getItem(position);
            Toast.makeText(this, name, Toast.LENGTH_SHORT).show();
        });
        mRvRowHeader.setAdapter(mRowAdapter);
        mRvRowHeader.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));

        final List<RecyclerView> syncRecyclerViews = new ArrayList<>();

        syncRecyclerViews.add(mRvCellParent);
        syncRecyclerViews.add(mRvRowHeader);

        //添加监听,使所有的RecyclerView保持同步
        RecyclerView.OnScrollListener listener = new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                for (RecyclerView item : syncRecyclerViews) {
                    if (recyclerView == item) {
                        continue;
                    }
                    item.clearOnScrollListeners();
                    item.scrollBy(dx, dy);
                    item.addOnScrollListener(this);
                }
            }
        };

        mRvRowHeader.addOnScrollListener(listener);
        mRvCellParent.addOnScrollListener(listener);
        cellParentAdapter.addRecyclerView(mRvColumnHeader);


    }

    public class CellParentAdapter extends BaseQuickAdapter<List<String>, BaseViewHolder> {

        private HashSet<RecyclerView> observerList = new HashSet<>();
        private int firstPos = -1;
        private int firstOffset = -1;

        public CellParentAdapter(int layoutResId, @Nullable List<List<String>> data) {
            super(layoutResId, data);
        }

        @Override
        protected void convert(@NotNull BaseViewHolder holder, List<String> data) {

            RecyclerView recyclerView = holder.getView(R.id.rv_cell_child);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext(), RecyclerView.HORIZONTAL, false));

            CommonAdapter commonAdapter = new CommonAdapter(R.layout.item_table_reuse_cell_child, data);
            if (recyclerView.getItemDecorationCount() == 0) {
                recyclerView.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.HORIZONTAL));

            }
            commonAdapter.setOnItemChildClickListener((adapter, view, position) -> {
                String name = commonAdapter.getItem(position);
                Toast.makeText(getContext(), name, Toast.LENGTH_SHORT).show();
            });

            addRecyclerView(recyclerView);

            recyclerView.setAdapter(commonAdapter);

        }


        /**
         * 维护RecyclerView集合,控制联动滑动
         *
         * @param recyclerView
         */
        public void addRecyclerView(RecyclerView recyclerView) {
            recyclerView.setHasFixedSize(true);
            LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
            if (layoutManager != null && firstPos > 0 && firstOffset > 0) {
                layoutManager.scrollToPositionWithOffset(CellParentAdapter.this.firstPos + 1, CellParentAdapter.this.firstOffset);

            }
            observerList.add(recyclerView);
            recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                    LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
                    int firstPos = linearLayoutManager.findFirstVisibleItemPosition();
                    View firstVisibleItem = linearLayoutManager.findViewByPosition(firstPos);
                    if (firstVisibleItem != null) {
                        int firstRight = linearLayoutManager.getDecoratedRight(firstVisibleItem);
                        for (RecyclerView rv : observerList) {
                            if (recyclerView != rv) {
                                LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager();
                                if (layoutManager != null) {
                                    CellParentAdapter.this.firstPos = firstPos;
                                    CellParentAdapter.this.firstOffset = firstRight;
                                    layoutManager.scrollToPositionWithOffset(firstPos + 1, firstRight);

                                }
                            }
                        }
                    }
                }

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

        /**
         * 控制集合RecyclerView联动滑动
         *
         * @param recyclerView
         * @param position
         */
        public void scrollRecyclerView(RecyclerView recyclerView, int position) {
            for (RecyclerView rv : observerList) {
                if (recyclerView != rv) {
                    LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager();
                    if (layoutManager != null) {
                        layoutManager.scrollToPositionWithOffset(position, 0);

                    }
                }
            }

        }


    }

    public class CommonAdapter extends BaseQuickAdapter<String, BaseViewHolder> {

        public CommonAdapter(int layoutResId, @Nullable List<String> data) {
            super(layoutResId, data);
            addChildClickViewIds(R.id.tv_name);
        }

        @Override
        protected void convert(@NotNull BaseViewHolder holder, String s) {
            holder.setText(R.id.tv_name, s);
        }
    }


    public List<String> getColumnHeaderList() {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 15; i++) {
            list.add("column " + i);
        }
        return list;
    }

    public List<String> getRowHeaderList() {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            list.add("row " + i);
        }
        return list;
    }

    public List<List<String>> getCellList() {
        List<List<String>> cellList = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            List<String> list = new ArrayList<>();
            for (int j = 0; j < 15; j++) {
                list.add("row" + i + " column" + j);
            }
            cellList.add(list);
        }
        return cellList;
    }

}

这里主要是数据和列表的展示,还有就是用了一个集合去存储需要滑动的列表RecyclerView,这样相互监听就可以达到一起滚动的效果,很容易理解,相信大家的能力,下面是BaseRecyclerViewAdapterHelper的依赖:

//强大的RecyclerView适配器
implementation ‘com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4’


总结

  其实这样操作也没有什么难度,重要的是集合存储需要滑动的RecyclerView,然后利用其LayoutManage去控制它们的滑动。这里很好地处理了复用问题,不然item项会有部分滑动,而其它还是停在原地。最后,可能还会有系列三的出现,主要想把一些想法写出来,又到夜深了,睡觉。。。。
最后附上下载地址


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黄小梁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值