曲线以图形表现数据,比表格数据直观多了,在各行业得到广泛的应用。浮云E绘图长期从事工业控制和军工软件研发,擅长绘图编辑器、曲线控件等软件研发,本文开启通用曲线控件的设计到编码实现系列文章。
通用曲线,即曲线功能较丰富,可以灵活满足常用曲线支持。本系列文章针对通用曲线控件做实现,下一个系列讲针对基于时间的海量数据曲线高效实现。
一、曲线控件部件介绍
曲线控件一般如上图所示,包含以下部件:
0.控件描述:曲线控件区域大小、背景颜色等。支持曲线实时测量、缩放、翻页、导出打印等。
1.标题Title:标题文本内容、字体类型、字体大小、字体颜色。标题区域大小、区域背景色、区域内文字对齐(左中右上下)、文字排列角度(横/竖/斜角)、标题区域基于曲线方位(上下左右/内嵌)、是否显示。
2.网格Grid:网格向类型(横向/纵向)、网格线类型(主/次线)、是否显示(主次线)、线粗细、线型(虚实)、线颜色。
3.曲线轴(X轴和Y轴):轴向类型(X/Y)、轴主副类型(X1/X2、Y1/Y2)、轴数据类型(线性轴/指数/时间/文本轴)、轴名称(文字内容、字体颜色、字型、字号、文字对齐、文字排列),轴线(线宽、线颜色、线型、端线类型(无/实心箭头/箭头线))。实现支持双X轴、双Y轴。
4.曲线:曲线类型(线性曲线/阶梯/散点/柱状/区域曲线)、曲线颜色、线条粗细、是否标注Marker(标注类型、颜色、宽度等)、曲线数据集。
曲线数据与轴类型有对应关系。一般Y轴都是数值轴,而X轴可能是数值、时间、文本等轴。当X是时间轴、Y是数值轴时,曲线点数据格式应该时x是时间值、y是数值。
5.图例Legend:图例列表排列方式(横/纵向)、图例区域块(区域大小、背景色)、图例基于控件的方位(上下左右/内嵌)、是否显示。
图例与曲线是一一对应关系,风格类型也是一致,这样才能直观的示例。比如曲线控件有2条曲线,A曲线是红色线条+方块标注,B曲线是蓝色柱状曲线,那么图例应该也是2个,A曲线图例一条线中间+方块图例,B曲线图例蓝色方块。
6.(脚注FootNote):与标题Title类似,有的曲线需要底部一段文字备注说明,或者实时显示轴移动中的原点值。
二、通用曲线控件灵活功能支持
如果定制的曲线控件应用场景单一,就没必要实现过多用不到的功能细节(比如标题文字多种排列和布局);如果想定制开发一套比较通用的曲线控件源码,那么就需要兼顾多种情况。
1. 坐标轴数据类型:较常用的包含线性轴 / 指数轴 / 时间轴 / 文本轴
2. 曲线类型:较常用的包含线性曲线 / 阶梯曲线 / 散点曲线 / 柱状曲线 / 饼状占比等
3. 坐标轴主副类型:同时支持X主轴X1、X副轴X2和Y主轴X1、Y副轴X2。至少一对X和Y即可。(一般不会用多轴展示数据,对比反而不直观。多数据对比曲线,一般采用,多区域纵向排列(X-Y1,X-Y2,X-Y3...)X轴同步的方式实现,请等待下一个系列曲线详细说明。)
三、通用曲线控件源码软件结构设计
曲线控件功能结构图如下所示,画布View作为曲线呈现,Chart作为数据管理容器。
根据上面功能结构,设计主要类:
1. 画布类:ChartView和SimpleView,ChartView作为通用画布,支持丰富XY多坐标系,而SimpleView实现为最简单的绘图接口,默认一些参数,使用简单。
public ChartView(AbstractChart chart)
2.数据管理类:AbstractChart --> XY坐标系的XYChart 和 饼状图的RoundChart。
// 作者:浮云绘图,擅长工控、军工等领域绘图编辑器、曲线控件定制研发
// 联系:QQ 316868127
public XYChart(AbstractAxis axisX, AbstractAxis axisY,AbstractAxis axisX2, AbstractAxis axisY2)
public XYChart(AbstractAxis axisX, AbstractAxis axisY, null, null)
public void draw(Canvas canvas, Paint paint)
{
canvas.save();
canvas.clipRect(_clientArea);
super.calcChartArea(paint);
// 背景
super.drawBackground(canvas, paint);
// 轴、网格
drawAxis(canvas, paint);
// 曲线、图例、标题
super.draw(canvas, paint);
canvas.restore();
}
@Override protected void drawSeries(Canvas canvas, Paint paint)
{
// TODO Auto-generated method stub
for (int i = 0; i < _series.size(); i++) {
AbstractSeries ser = _series.get(i);
ser.drawLine(canvas, paint,_plotArea);
}
}
@Override protected void scroll(EnZoomOrientation orientation, double physicsOffset)
@Override protected void zoom(EnZoomOrientation orientation, PointD physicsCenter, double physicsScale)
3. 头部Header
public class Header extends TextNote {
private EnHeadPosition _position = EnHeadPosition.TopCenter;
public Header(String title, AbstractChart parent) {
super(title, parent);
}
public void draw(Canvas canvas, Paint paint){
super.draw(canvas, paint);
}
public void setPosition(EnHeadPosition position){
if(_position != position){
_position = position;
_parent.calcChartArea();
}
}
public EnHeadPosition getPosition(){
return _position;
}
public enum EnHeadPosition{
TopCenter,
BottomCenter,
LeftCenter,
}
}
public class TextNote {
protected AbstractChart _parent;
protected boolean _visible = true;
protected String _title = "Header title";
protected FontStyle _fontStyle = new FontStyle(12, Color.BLACK);
protected int _backColor = Color.LT