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

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