美文网首页
自定义View之FormView

自定义View之FormView

作者: Routee | 来源:发表于2018-07-23 19:15 被阅读0次

    本Demo主要目的为学习及研究自定义View,通过实现一个图表的数据展示功能,熟悉和了解View的绘制过程

    先看一下产品需求

    产品需求 产品需求
    • X轴和Y轴坐标分别表示时间及对应的数值
    • Y轴坐标依数据显示5-10行,Y轴辅助线显示3-5条
    • X轴依时间文字的长短进行展示,要求X轴坐标值不重合
    • 各坐标点用直线相连,且连线与X轴区域添加渐变色
    • 添加touch时间,当触摸至坐标点时显示文本框,显示说明文本,绘制坐标点圆圈及X、Y轴辅助线

    功能分析

    为完成产品的需求,我们需要解决如下的6个问题:
    1.首先,我们需要计算出绘图区域及坐标轴文字显示区域;
    2.绘制坐标轴文字;
    3.绘制平行于X轴的辅助线;
    4.计算各坐标点位置;
    5.连接各坐标点并绘制渐变区域;
    6.捕捉touch事件并添加回调;
    7.依据回调设置提示框内容并绘制提示框。
    

    代码实现

    因为要展示数据,所以需要自定义View暴露对外的设置数据的接口,同时数据需要如下三个属性:颜色(绘制连接线时连接线的颜色)、Y轴坐标(选用string类型,因为横坐标可能是周一、二……)、X轴坐标值(这里选用double类型)。因此,在自定义View中可以使用内部类Units来作为坐标点,同时用Map<Color,Units>来保存需要展示的数据。

    //坐标点位置
    public static class Units {
        public double y;
        String x;
    
        public Units(double y, String x) {
            this.x = x;
            this.y = y;
        }
    }
    
    //对外暴露的接口,用以设置数据
    public void resetData(Map<Integer, List<Units>> map) {
        this.mDatas.clear();
        Iterator<Integer> it = map.keySet().iterator();
        while (it.hasNext()) {
            Integer color = it.next();
            mDatas.put(color, map.get(color));
        }
        invalidate();
    }
    

    我们知道,因为是自定义View,所以我们需要添加一些atrrs属性,便于对View进行一些设置;
    本demo中添加的一些属性如下

    属性名 类型 说明
    min_size integer view最小尺寸
    base_stroke_width integer 基础线条宽度
    base_stroke_color color 基础线条颜色
    base_text_size integer 坐标文字大小
    help_text_size integer 弹出提示框文字大小
    help_text_margin integer 弹出提示框Margin
    text_margin_y integer Y方向文字与表格间距
    point_size integer 触摸时显示坐标点的大小
    point_touch_size integer 触摸范围
    text_margin_x integer X方向文字与表格间距
    zero_start boolean Y轴是否从零开始
    help_text_bg_res reference 触摸响应说明背景
    shader boolean 是否添加X坐标与连线间的渐变

    有了如上属性,我们在自定义初始化的初始化这些属性,同时初始化Paint

    private void init(AttributeSet attrs) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.RouteeFormView);
        mMinSize = a.getInteger(R.styleable.RouteeFormView_min_size, 0);
        mBaseColor = a.getColor(R.styleable.RouteeFormView_base_stroke_color, Color.parseColor("#d0d0d0"));
        mBaseStrokeWidth = a.getInteger(R.styleable.RouteeFormView_base_stroke_width, 1);
        mBaseTextSize = a.getInteger(R.styleable.RouteeFormView_base_text_size, 12);
        mHelpTextSize = a.getInteger(R.styleable.RouteeFormView_help_text_size, 14);
        mHelpTextMargin = a.getInteger(R.styleable.RouteeFormView_help_text_margin, 8);
        mTextMarginX = DisplayUtils.dp2px(getContext(), a.getInteger(R.styleable.RouteeFormView_text_margin_x, 4));
        mTextMarginY = DisplayUtils.dp2px(getContext(), a.getInteger(R.styleable.RouteeFormView_text_margin_y, 4));
        mHelpTextBgResId = a.getResourceId(R.styleable.RouteeFormView_help_text_bg_res, R.drawable.bg_routee_form_view_help_text);
        mNeedDrawShader = a.getBoolean(R.styleable.RouteeFormView_shader, false);
        mPointWidth = DisplayUtils.dp2px(getContext(), a.getInteger(R.styleable.RouteeFormView_point_size, 2));
        mPointTouchWith = DisplayUtils.dp2px(getContext(), a.getInteger(R.styleable.RouteeFormView_point_touch_size, 10));
        isStartZero = a.getBoolean(R.styleable.RouteeFormView_zero_start, false);
        a.recycle();
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
    }
    

    然后,我们需要重写我们的onMeasure方法,计算自定义View的大小

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        if (widthSpecMode == AT_MOST && heightSpecMode == AT_MOST) {
            setMeasuredDimension(mMinSize, mMinSize);
        } else if (widthMeasureSpec == AT_MOST) {
            setMeasuredDimension(mMinSize, heightSpecSize);
        } else if (heightMeasureSpec == AT_MOST) {
            setMeasuredDimension(widthSpecSize, mMinSize);
        }
    }
    

    在计算出View的尺寸后,我们需要开始完成自定View最重要的一步绘制,也就是重写onDraw(Canvas canvas)方法,依据需求分析,我们需要进行一些列的计算再去按如下顺序去绘制View的不同部分:

    效果图

    相关文章

      网友评论

          本文标题:自定义View之FormView

          本文链接:https://www.haomeiwen.com/subject/atzjmftx.html