美文网首页
自定义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