一个基于RecyclerView+Scroller实现的二维表格组件,同时支持侧滑菜单、拖动调整列表顺序、刷新动画等拓展功能。
A two-dimensional table view, base on recyclerview, both support to side slide menu、drag item、refresh animation and more.
- 基于RecyclerView实现,可复用视图 
- 支持自定义数据源类型、表头及单元格样式 
- 支持列表Item侧滑菜单 
- 支持拖拽变换顺序 
- 支持默认刷新动画 
- 支持设置点击头部监听 
- 支持AndroidX 
在项目根目录的build.gradle添加:
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
在项目的build.gradle添加如下依赖:
implementation 'com.github.GitHubZJY:XTableView:v1.0.0'
<com.zjy.xtableview.XTableView
        android:id="@+id/table_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:swipeLayout="@layout/table_swipe_menu_layout"
        app:headerHeight="50dp"
        app:rowHeight="80dp"
        app:cellWidth="130dp"
        app:notifyAnim="true">
</com.zjy.xtableview.XTableView>ITableView vTableView = findViewById(R.id.table_view);
//设置是否支持长按拖动列表项
vTableView.setLongPressDragEnable(true);
//设置是否支持侧滑菜单
vTableView.setSwipeEnable(true);绑定数据的设计灵感来自于RecyclerView的adapter设计,与数据相关的操作均通过 XTableAdapter 作为中间者来进行.
onBindTableHeader 绑定左上角表头视图
onBindColumnHeader 绑定(除表头外的)普通列头部视图
onCreateTableItem 创建每一个单元格的视图
onBindTableItem 绑定每一个单元格的视图数据
onCreateRowHeader 创建每一行的头部视图
onBindRowHeader 绑定每一行的头部视图数据
public class CustomTableAdapter extends XTableAdapter<String, TableRowModel<TableRowHeaderModel, TableRowCellModel>>{
    public CustomTableAdapter(Context context) {
        super(context);
    }
    @Override
    public View onBindTableHeader(String s) {
        TextView cellTv = new TextView(getContext());
        cellTv.setTextColor(getColor(R.color.table_view_second_txt_color));
        cellTv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
        cellTv.setGravity(Gravity.START|CENTER);
        cellTv.setText(s);
        return cellTv;
    }
    @Override
    public View onBindColumnHeader(int position, String t) {
        TextView cellTv = new TextView(getContext());
        cellTv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
        cellTv.setGravity(Gravity.CENTER);
        cellTv.setText(t);
        return cellTv;
    }
    @Override
    public View onCreateTableItem(int position) {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.table_item_cell_layout, null);
        TextView cellTv = view.findViewById(R.id.cell_tv);
        cellTv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
        cellTv.setGravity(Gravity.CENTER);
        return view;
    }
    @Override
    public void onBindTableItem(int position, View view, TableRowModel<TableRowHeaderModel, TableRowCellModel> rowModel) {
        final TableRowCellModel cellModel = rowModel.getRowData().get(position);
        TextView cellTv = view.findViewById(R.id.cell_tv);
        cellTv.setTextColor(cellModel.isRise() ?
                getColor(R.color.table_view_rise_txt_color)
                : getColor(R.color.table_view_fall_txt_color));
        cellTv.setText(cellModel.getContent());
        cellTv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(getContext(), cellModel.getContent(), Toast.LENGTH_SHORT).show();
            }
        });
    }
    @Override
    public View onCreateRowHeader(int position) {
        return LayoutInflater.from(getContext()).inflate(R.layout.table_item_title_layout, null);
    }
    @Override
    public void onBindRowHeader(int position, View view, TableRowModel<TableRowHeaderModel, TableRowCellModel> rowModel) {
        TextView vTitle = view.findViewById(R.id.title_tv);
        TextView vDetail = view.findViewById(R.id.detail_tv);
        String title = rowModel.rowHeader.getTitle();
        String detail = rowModel.rowHeader.getDetail();
        vTitle.setText(TextUtils.isEmpty(title) ? "" : title);
        vDetail.setText(TextUtils.isEmpty(detail) ? "" : detail);
    }
}/**
 * 更新表格数据
 * @param header 左上角的表头数据
 * @param columnHeader 列头部数据集合
 * @param tableData 每一行的数据集合
 */
public void bindData(T header, List<T> columnHeader, List<H> tableData)vTableView.setTableAdapter(adapter);
数据变更也是通过adapter对象来进行:
如果是所有数据替换,可调用 bindData 方法设置新的数据,然后通过 notifyDataSetChanged 进行更新. 
如果是单条数据刷新,可调用 notifyItemData(int position, H data) 进行更新,position是对应的下标,data为新的数据. 
如果是单条数据插入,可调用 notifyInsertData(int position, H data) 进行更新,position是对应的下标,data为新的数据.
 
vTableView.setTableListener(new XTableListener() {
    @Override
    public void clickSwipeMenu(int position) {
        //点击菜单
        ...
    }
    @Override
    public void onItemMove(int fromPos, int toPos) {
        //拖拽Item
        ...
    }
    @Override
    public void onColumnHeaderItemClick(int position) {
        Toast.makeText(MainActivity.this, "点击了第" + position + "列的头部", Toast.LENGTH_SHORT).show();
        //点击某一列头部之后对列表数据进行重排序
        ..
    }
});
目前支持点击侧滑菜单、拖拽列表项、点击列头部的监听回调,例如实现根据某列进行排序的需求可以在 onColumnHeaderItemClick 里面进行
 
本库也提供了LayoutManager的一些配置,例如: setReverseLayout 、 setStackFromEnd 、 scrollToPosition 、 scrollToPositionWithOffset 等,后续会再根据需要进行扩充.
SwipeRecyclerView 
侧滑和拖拽是在SwipeRecyclerView这个库的基础上修改,是一个基于RecyclerView拓展得不错的组件。本库的自定义样式和数据类型基于适配器的模式设计,后续会继续更新,提升组件的定制性和可拔插性,欢迎issue和star~
