GitHub源码学习笔记之TableView(一)

写在前面:

最近发现一个很酷的开源项目,正好自己也打算写一个TableView,但是。。。写代码哪有抄代码爽!所以我决定来学习一下大佬的代码(先贴出来Github地址---->TableView

学习第一步,先看基本架构图:

按照图上的解释,TableView的实现方式是多个RecyclerView组合的结果,多个RecyclerView在TableView的统一架构下实现数据联动、滑动联动。至于为什么选择使用多个RecyclerView来实现的,原作者给出的理由是RecyclerView自带资源回收机制,并且非常高效,直接使用可以节省非常大的开发成本。

 

第二步:clone项目到本地

我将代码从github上克隆到本地,开始运行并分析源码。

这是运行效果:

可以看出来它的功能是非常强大的吧,整行、整列、单个选择都OJBK,而且全部支持自定义样式。。。

首先是从MainActivity开始看吧:

public class MainActivity extends AppCompatActivity {

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

        if (savedInstanceState == null) {
            getSupportFragmentManager()
                    .beginTransaction()
                    .add(
                            R.id.activity_container
                            , new MainFragment()
                            , MainFragment.class.getSimpleName()
                    ).commit();
        }
    }
}

从代码上看就是这样的非常简单,在activity中使用了一个Fragment,接下来看MainFragment中干了什么:

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle
            savedInstanceState) {
        View layout = inflater.inflate(R.layout.fragment_main, container, false);
        
        //。。。省略了部分代码。。。

        //获取到TableView
        mTableView = layout.findViewById(R.id.tableview);

        initializeTableView();//这里是配置TableView主要的地方

        return layout;
    }

    //这里是配置TableView主要的地方
    private void initializeTableView() {
        // 这是一个数据中配置该表格的主要参数,比如总行数,总列数等
        // Create TableView View model class  to group view models of TableView
        TableViewModel tableViewModel = new TableViewModel();

        // TableViewAdapter继承自AbstractTableAdapter
        //原作者在注释表示“TableViewAdapter”只是一个简单的使用例子,也就是说
        //如果我们要自己使用“TableView”,那么也是要自己写一个新的Adapter,就像使用RecyclerView一样
        TableViewAdapter tableViewAdapter = new TableViewAdapter(tableViewModel);

        mTableView.setAdapter(tableViewAdapter);
        mTableView.setTableViewListener(new TableViewListener(mTableView));

        // Create an instance of a Filter and pass the TableView.
        //mTableFilter = new Filter(mTableView);

        // Load the dummy data to the TableView
        tableViewAdapter.setAllItems(tableViewModel.getColumnHeaderList(), tableViewModel
                .getRowHeaderList(), tableViewModel.getCellList());

       
    }

这是MainFragment中的部分代码,也是主要用来配置TableView的代码。

先从 TableViewModel tableViewModel = new TableViewModel(); 开始,也就是TableViewModel类,这个类中定义了很多参数,这些参数决定了表格中该显示什么东西。

其中比较重要的三个方法是:

    /**
     * 循环创建"行"的头文件,
     * <code>ROW_SIZE</code> 表示最大行数,如果这个值为500,那么生成的表格就有500行
     * <code>RowHeader</code> 表示"行"的第一列的数据,它继承自Cell
     */    
    @NonNull
    private List<RowHeader> getSimpleRowHeaderList() {
        List<RowHeader> list = new ArrayList<>();
        for (int i = 0; i < ROW_SIZE; i++) {
            RowHeader header = new RowHeader(String.valueOf(i), "row " + i);
            list.add(header);
        }

        return list;
    }

    /**
     * 循环创建列
     * 这里它使用了随机数来使某些列的标题不一样(同时列的宽度的发生了变化)
     * <code>ColumnHeader</code> 表示"列"的第一行的数据,它继承自Cell
     */
    @NonNull
    private List<ColumnHeader> getRandomColumnHeaderList() {
        List<ColumnHeader> list = new ArrayList<>();

        for (int i = 0; i < COLUMN_SIZE; i++) {
            String title = "column " + i;
            int nRandom = new Random().nextInt();
            if (nRandom % 4 == 0 || nRandom % 3 == 0 || nRandom == i) {
                title = "large column " + i;
            }

            ColumnHeader header = new ColumnHeader(String.valueOf(i), title);
            list.add(header);
        }

        return list;
    }

    /**
     * 双层循环创建数据域的每一个元素(也就是表格中的每一个单元格)
     * <code>Cell</code> 也就是每个单元格对象
     */
    @NonNull
    private List<List<Cell>> getCellListForSortingTest() {
        List<List<Cell>> list = new ArrayList<>();
        for (int i = 0; i < ROW_SIZE; i++) {
            List<Cell> cellList = new ArrayList<>();
            for (int j = 0; j < COLUMN_SIZE; j++) {
                Object text = "cell " + j + " " + i;

                final int random = new Random().nextInt();
                if (j == 0) {
                    text = i;//第一列的所有数据为"行"数
                } else if (j == 1) {
                    text = random;//第二列的所有数据都是随机数
                } else if (j == MOOD_COLUMN_INDEX) {
                    text = random % 2 == 0 ? HAPPY : SAD;//该列表示情绪
                } else if (j == GENDER_COLUMN_INDEX) {
                    text = random % 2 == 0 ? BOY : GIRL;//该类表示性别
                }

                // Create dummy id.
                String id = j + "-" + i;

                Cell cell = new Cell(id, text);
                cellList.add(cell);
            }
            list.add(cellList);
        }

        return list;
    }

TableViewModel这类通过这三个方法描述了一个表格绘制所需要的数据。多少行、多少列、每一列(行)对应的数据都在这个类中生成好了;可以这样理解:TableViewModel就是表格数据的抽象,它决定(创建)了一个表格应该有些什么东西,但具体如何绘制和它没有关系

接下来就是将这个数据具体的绘制出来了,请大家回到这一行

TableViewAdapter tableViewAdapter = new TableViewAdapter(tableViewModel);

TableViewAdapter是一个将抽象数据(TableViewModel)进行具体绘制的类,它与我们常用的RecyclerView.Adapter非常相似。

首先TableViewAdapter继承自AbstractTableAdapterAbstractTableAdapter实现了ITableAdapter接口。所以为了更好的理解源码,我们先从ITableAdapter接口开始看:

public interface ITableAdapter<CH, RH, C> {

    //获取“列”的头标签的类型(在本项目的例子中,该方法只是全部返回0
    int getColumnHeaderItemViewType(int position);

    //获取“行”的头标签的类型(在本项目的例子中,该方法只是全部返回0
    int getRowHeaderItemViewType(int position);

    //获取“单元格”的类型(在本项目的例子中,该方法只是全部返回0
    int getCellItemViewType(int position);

    //获取左上角的Corner
    View getCornerView();

    //这个方法用于创建具体的单元格View
    @NonNull
    AbstractViewHolder onCreateCellViewHolder(@NonNull ViewGroup parent, int viewType);
    
    //这个方法用于对创建的单元格View绑定数据
    void onBindCellViewHolder(@NonNull AbstractViewHolder holder, @Nullable C cellItemModel, int columnPosition, int rowPosition);

    //这个方法用于创建具体“列”的头标签View
    @NonNull
    AbstractViewHolder onCreateColumnHeaderViewHolder(@NonNull ViewGroup parent, int viewType);

    //这个方法用于对创建的“列”的头标签View绑定数据
    void onBindColumnHeaderViewHolder(@NonNull AbstractViewHolder holder, @Nullable CH columnHeaderItemModel, int columnPosition);
    
    //这个方法用于创建具体“行”的头标签View
    @NonNull
    AbstractViewHolder onCreateRowHeaderViewHolder(@NonNull ViewGroup parent, int viewType);

    //这个方法用于对创建的“行”的头标签View绑定数据
    void onBindRowHeaderViewHolder(@NonNull AbstractViewHolder holder, @Nullable RH rowHeaderItemModel, int rowPosition);

    //这个方法用于创建左上角的CornerView
    @NonNull
    View onCreateCornerView(@NonNull ViewGroup parent);

    //这个方法返回一个TableView的抽象接口,放到后面再讲
    ITableView getTableView();

    /**
     * Sets the listener for changes of data set on the TableView.
     * 设置数据变动监听
     * @param listener The AdapterDataSetChangedListener listener.
     */
    void addAdapterDataSetChangedListener(@NonNull AdapterDataSetChangedListener<CH, RH, C> listener);
}

该接口的方法还挺通俗易懂的吧,然后我们继续看实现了这个接口的抽象类AbstractTableAdapter

public abstract class AbstractTableAdapter<CH, RH, C> implements ITableAdapter<CH, RH, C> {

    public void setTableView(@NonNull ITableView tableView) {
        mTableView = tableView;
        initialize();
    }

    private void initialize() {
        Context context = mTableView.getContext();

        // Create Column header RecyclerView Adapter
        mColumnHeaderRecyclerViewAdapter = new ColumnHeaderRecyclerViewAdapter<>(context,
                mColumnHeaderItems, this);

        // Create Row Header RecyclerView Adapter
        mRowHeaderRecyclerViewAdapter = new RowHeaderRecyclerViewAdapter<>(context,
                mRowHeaderItems, this);

        // Create Cell RecyclerView Adapter
        mCellRecyclerViewAdapter = new CellRecyclerViewAdapter<>(context, mCellItems, mTableView);
    }

}

在这个抽象类中。。。它又创建了三个Adapter。。。分别用于ColumnHeader、RowHeader、Cell,所以还得继续一步步分析。。。(另外该类的泛型CH、RH、C,分别表示ColumnHeader、RowHeader、Cell)。这里创建的三个Adapter比较简单,它们都是集成自抽象类(AbstractRecyclerViewAdapter<T>),从名字就看看出来它集成自RecyclerViewAdapter<AbstractViewHolder>

AbstractRecyclerViewAdapter<T>中重写了两个方法:

public abstract class AbstractRecyclerViewAdapter<T> extends RecyclerView
    .Adapter<AbstractViewHolder> {
    
    protected List<T> mItemList;    

    @Override
    public int getItemCount() {
        return mItemList.size();
    }

    @Override
    public int getItemViewType(int position) {
        return 1;
    }
}

其它方法都是针对mItemList的增删改查。逻辑都比较简单,就不一一贴出来了。

让我们回到AbstractTableAdapter,在它创建完三个分别用于ColumnHeader、RowHeader、Cell的Adapter之后,它还提供了针对这三个Adapter的增删改查的方法,以及对数据修改的监听方法

addAdapterDataSetChangedListener(@NonNull AdapterDataSetChangedListener<CH, RH, C> listener)

(。。。怎么感觉一脚踩坑里了。。。这代码怎么这么多多多多多多多。。。)

接下来继续回到TableViewAdapter,(终于看完了。。。心好累)

现在我们知道TableViewAdapter是怎么绘制各个“单元格”了吧,由于TableView所涉及的子View比较多,所它的Adapter写的也比较复杂。原作者能把各个子View之间的逻辑整理的如此清楚,其Java段位可见一斑,只能说大佬不愧是大佬。

(一篇博客不好写的太长。。。所以我打算将剩下的关于点击事件、刷动事件等内容写在另一篇,未完待续

各位看官如果觉得喜欢,或者有任何意见和建议都可以直接给我留言。

最后再贴一下大佬的Github项目地址---->TableView

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章