TableView开发笔记(一)

陌路散爱 提交于 2020-01-17 17:14:45

最近想做一个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项目

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!