TableView開發筆記(一)

GitHub源碼學習筆記之TableView

最近想做一個TableView,主要用於展示表格數據,並且要支持滑動和自定義表格內部的元素(子view)樣式

所以先佔個坑,做一些準備工作。

第一步:確定一下需求

左上角的“lock”白色塊,我把它稱爲“lockView”;上方的“abc”列我把它稱爲“horizontalView”;左側的“1234567”列我把它稱爲“verticalView”。中間標爲“GridView”的地方,表示數據域。slide表示滑動方向;lock代表滑動鎖定。例如當該表格發生水平滑動時,左側的“verticalView”是不發生變化的,同理,當發生垂直滑動時,上方的“horizontalView”是不發生變化的。這就是表格的基礎功能。

另外左上角lockView控制了horizontalView的高度和verticalView的寬度,我的設想lockView是一個可自定義的View,用於展示多種不一樣的功能和樣式。

並且horizontalView、verticalView和GridView中每一個子元素(view)都要是可以自定義的。例如在horizontalView中,可以出現好幾個不同的標籤:姓名、年齡、性別、吃飯速度,這時我不一定全部使用文字來描述該標籤,比如性別一欄中我可以使用圖標,再比如吃飯速度一欄在數據域(GridView)中的描述不一定要用枯燥的文字,可以使用顏色標籤,顏色越深,則吃飯速度越快。

第二步:理清楚了以上基本需求,接下來就是研究具體如何實現了。

我設想是自定義一個TableView繼承自FrameLayout,隨後是注意重寫onMeasure方法來設定寬高

public class TableView extends FrameLayout{

    private int width,height;

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int tempWidth = getMeasuredSize(widthMeasureSpec,true);
        int tempHeight = getMeasuredSize(heightMeasureSpec,false);
        width = tempWidth;
        height = tempHeight;
        setMeasuredDimension(tempWidth,tempHeight);
    }

    /**
     * 計算控件的實際大小
     * @param length onMeasure方法的參數,widthMeasureSpec或者heightMeasureSpec
     * @param isWidth 是寬度還是高度
     * @return int 計算後的實際大小
     */
    private int getMeasuredSize(int length, boolean isWidth){
        // 模式
        int specMode = MeasureSpec.getMode(length);
        // 尺寸
        int specSize = MeasureSpec.getSize(length);
        // 計算所得的實際尺寸,要被返回
        int retSize = 0;
        // 得到兩側的padding(留邊)
        int padding = (isWidth? getPaddingLeft()+getPaddingRight():getPaddingTop()+getPaddingBottom());

        // 對不同的指定模式進行判斷
        if(specMode==MeasureSpec.EXACTLY){  // 顯式指定大小,如40dp或fill_parent
            retSize = specSize + padding;
        }else{                              // 如果使用wrap_content
            retSize = (int) (isWidth? DEF_WIDTH + padding : DEF_HEIGHT + padding);
            if(specMode==MeasureSpec.AT_MOST){
                retSize = Math.min(retSize, specSize);
            }
        }
        return retSize;
    }

}

完成確定寬高方法之後,隨後在其中添加控件,horizontalView和verticalView我想通過RecyclerView來實現。也就是在TableView添加兩個RecyclerView。

public class TableView extends FrameLayout {
    
    //本方法在構造方法中調用
    private void init(){    
        horizontal = new RecyclerView(mContext);
        vertical = new RecyclerView(mContext);

        hAdapter = new Adapter();
        vAdapter = new Adapter2();

        GridLayoutManager hGrid = new GridLayoutManager(mContext, 1, LinearLayoutManager.HORIZONTAL, false);
        GridLayoutManager vGrid = new GridLayoutManager(mContext, 1, LinearLayoutManager.VERTICAL, false);

        horizontal.setLayoutManager(hGrid);
        horizontal.setAdapter(hAdapter);

        vertical.setLayoutManager(vGrid);
        vertical.setAdapter(vAdapter);

        horizontal.setTag("horizontal");
        vertical.setTag("vertical");

        addView(horizontal,0);
        addView(vertical,0);
    }    
    
    //添加了View之後要在這裏給每個View配置對應的位置
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        View view = findViewWithTag("horizontal");
        view.layout(100,0,right,100);
        View view1 = findViewWithTag("vertical");
        view1.layout(0,100,100,bottom);
    }
}

隨後是按照設想,配置滑動,我可以選擇在TableView中任何一個地方觸發滑動,並且相應的要將horizontalView和verticalView也聯動起來。也就是說需要重寫onTouchEvent來實現聯動

    private RecyclerView tmpTouch;
    int startX,startY;
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        //先判斷是垂直滑動還是水平滑動
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                startX = (int) ev.getX();
                startY = (int) ev.getY();
                break;

            case MotionEvent.ACTION_MOVE:

                int distanceX = (int) (ev.getX() - startX);
                int distanceY = (int) (ev.getY() - startY);

                if (distanceX < 10 && distanceY < 10) break;

                if (Math.abs(distanceX) > Math.abs(distanceY)){
                    tmpTouch = horizontal;
                } else {
                    tmpTouch = vertical;
                }
                break;

        }
        if (tmpTouch != null){
            tmpTouch.onTouchEvent(ev);
        }
        return true;
    }

這裏首先實現了滑動衝突的問題解決,其次將滑動事件直接傳遞給對應的RecyclerView。

未完待續

如果大家有興趣可以去GitHub上看看其它大神已經寫好的TableView項目

發佈了19 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章