曲线以图形表现数据,比表格数据直观多了,在各行业得到广泛的应用。浮云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。
1 // 作者:浮云绘图,擅长工控、军工等领域绘图编辑器、曲线控件定制研发 2 // 联系:QQ 316868127 3 4 public XYChart(AbstractAxis axisX, AbstractAxis axisY,AbstractAxis axisX2, AbstractAxis axisY2) 5 public XYChart(AbstractAxis axisX, AbstractAxis axisY, null, null) 6 7 8 public void draw(Canvas canvas, Paint paint) 9 {10 canvas.save();11 canvas.clipRect(_clientArea);12 super.calcChartArea(paint);13 14 // 背景15 super.drawBackground(canvas, paint);16 17 // 轴、网格18 drawAxis(canvas, paint);19 20 // 曲线、图例、标题21 super.draw(canvas, paint);22 canvas.restore();23 }24 25 26 @Override protected void drawSeries(Canvas canvas, Paint paint) 27 {28 // TODO Auto-generated method stub29 for (int i = 0; i < _series.size(); i++) {30 AbstractSeries ser = _series.get(i);31 ser.drawLine(canvas, paint,_plotArea);32 }33 }34 35 36 @Override protected void scroll(EnZoomOrientation orientation, double physicsOffset)37 @Override protected void zoom(EnZoomOrientation orientation, PointD physicsCenter, double physicsScale)
3. 头部Header
1 public class Header extends TextNote { 2 private EnHeadPosition _position = EnHeadPosition.TopCenter; 3 4 5 public Header(String title, AbstractChart parent) { 6 super(title, parent); 7 } 8 public void draw(Canvas canvas, Paint paint){ 9 super.draw(canvas, paint);10 }11 12 public void setPosition(EnHeadPosition position){13 if(_position != position){14 _position = position;15 _parent.calcChartArea(); 16 }17 }18 public EnHeadPosition getPosition(){19 return _position;20 }21 22 public enum EnHeadPosition{23 TopCenter,24 BottomCenter,25 LeftCenter,26 }27 }28 29 public class TextNote {30 protected AbstractChart _parent;31 protected boolean _visible = true;32 protected String _title = "Header title";33 protected FontStyle _fontStyle = new FontStyle(12, Color.BLACK);34 35 protected int _backColor = Color.LTGRAY;36 protected RectF _rectF = new RectF();37 protected boolean _isBoxShow = false;38 protected LineStyle _boxStyle = new LineStyle(1, Color.BLUE);39 40 private int _paddingInV = 2; 41 private int _paddingInH = 4; 42 43 public TextNote(String title, AbstractChart parent){44 this._title = title;45 _parent = parent;46 }47 public void draw(Canvas canvas, Paint paint){48 if (!_visible) {49 return;50 }51 52 paint.setStyle(Style.FILL);53 paint.setColor(_fontStyle.color);54 paint.setTextSize(_fontStyle.size);55 Rect r = new Rect();56 paint.getTextBounds(_title, 0, _title.length(), r);57 58 59 paint.setStyle(Style.FILL);60 paint.setColor(_backColor);61 canvas.drawRect(_rectF, paint);62 63 64 float x = 0F, y = 0F;65 x = _rectF.left + (_rectF.width() - r.width()) / 2 ;66 y = _rectF.top + (_rectF.height() - r.height()) / 2 + r.height() - 1.5f;67 68 paint.setTextSize(_fontStyle.size);69 paint.setColor(_fontStyle.color);70 canvas.drawText(_title, x, y, paint);71 72 73 if (_isBoxShow) {74 paint.setStyle(Style.STROKE);75 paint.setColor(_boxStyle.color);76 paint.setStrokeWidth(_boxStyle.width);77 canvas.drawRect(_rectF, paint);78 }79 }80 81 ......82 }
4. 网格Grid
1 public class Grid {2 private EnGridType _type = EnGridType.X;3 private boolean _visible = true;4 private LineStyle _majorGridLineStyle;5 private LineStyle _minorGridLineStyle;6 ......7 8 }
5.坐标轴:AbstractAxis --> 线性轴LinearAxis、指数轴LogAxis、时间轴DatetimeAxis 、文本轴LabelAxis。
1 // 作者:浮云绘图 2 // 联系:QQ 316868127 3 4 public abstract class AbstractAxis { 5 public enum EnAxisType // 坐标轴类型 6 { 7 X, // 主X轴 8 Y, // 主Y轴 9 X2, // 副X轴 10 Y2, // 副Y轴 11 All, // 所有轴 12 }; 13 14 public enum EnAxisTitleAlign // 轴名称文字位置 15 { 16 Center, // 居中 17 Away, // 轴最大值方向对齐 18 Near, // 轴最小值方向对齐 19 }; 20 21 public enum EnTickerMode { // 刻度线模式 22 Above, // 轴上 23 Underside, // 轴下 24 Both, // 轴上下 25 }; 26 27 28 protected EnAxisType _type; 29 protected boolean _visible = false; 30 // protected boolean _reversed = false; 31 // protected boolean _autoScale = true; 32 protected double _dMax; // 最大值 33 protected double _dMin; // 最小值 34 // protected double _dMajor; // 大刻度间隔 35 protected double _origin = Double.MAX_VALUE; // XY轴起点 36 protected boolean _autoOrigin = true; 37 38 // 标题--abcd 39 private int k = 3432; //svn tst 40 protected String _title = "Axis"; 41 protected FontStyle _titleFontStyle; 42 // protected EnAxisTitleAlign _titleAlign; 43 protected boolean _titleVisible = true; 44 45 // 轴线 46 protected LineStyle _axisLineStyle; 47 48 // 刻度线 49 protected LineStyle _scaleLineStyle; 50 protected int _majorScaleLineLength = 5; // 大刻度线长 51 protected int _scaleLabelToValueLength = 2; 52 protected boolean _showTickMarks = true ; 53 54 // 刻度值 55 protected float _scaleValeAngle; 56 protected FontStyle _scaleFontStyle; 57 protected int _scaleValueColor = Color.BLACK; 58 protected boolean _showLabels = true; 59 60 // 网格 61 protected Grid _grid; 62 63 protected AbstractAxis() { 64 _dMax = Double.MIN_VALUE; 65 _dMin = Double.MAX_VALUE; 66 67 _titleFontStyle = new FontStyle(11, Color.BLACK); 68 _axisLineStyle = new LineStyle(1, Color.BLACK); 69 _scaleLineStyle = new LineStyle(1, Color.BLACK); 70 _scaleFontStyle = new FontStyle(10, Color.CYAN); 71 72 _grid = new Grid(); 73 } 74 75 public void draw(Canvas canvas, Paint paint, RectF plotArea, 76 Listlabels) { 77 draw(canvas, paint, plotArea, labels, 0); 78 } 79 80 public void draw(Canvas canvas, Paint paint, RectF plotArea, 81 List labels, float originOffset) { 82 if (!_visible) { 83 return; 84 } 85 86 Rect txtRect = new Rect(); 87 String txtLable = ""; 88 float position = 0; 89 90 paint.setStyle(Style.FILL); 91 switch (_type) { 92 case X: { 93 double xPixelsPerUnit = plotArea.width() 94 / (WorldToPhysical(_dMax) - WorldToPhysical(_dMin)); 95 96 // 网格 刻度线 标签 轴线 名称 97 List axisLabels = labels; 98 boolean showGridY = _grid.getVisible(); 99 float orgY = plotArea.bottom - originOffset;100 int length = axisLabels.size();101 102 for (int i = 0; i < length; i++) {103 double label = axisLabels.get(i);104 float xLabel = (float) (plotArea.left + xPixelsPerUnit105 * (WorldToPhysical(label) - WorldToPhysical(_dMin)));106 107 paint.setStyle(Style.STROKE);108 if (showGridY) {109 paint.setStrokeWidth(_grid.getMarjorGridLineStyle().width);110 paint.setColor(_grid.getMarjorGridLineStyle().color);111 canvas.drawLine(xLabel, plotArea.bottom, xLabel,112 plotArea.top, paint);113 }114 if (_showTickMarks) {115 paint.setStrokeWidth(_scaleLineStyle.width);116 paint.setColor(_scaleLineStyle.color);117 canvas.drawLine(xLabel, orgY, xLabel, orgY118 + _majorScaleLineLength, paint);119 }120 if (_showLabels) {121 paint.setStyle(Style.FILL);122 paint.setColor(_scaleValueColor);123 paint.setTextSize(_scaleFontStyle.size);124 txtLable = getValueFormat(label);125 paint.getTextBounds(txtLable, 0, txtLable.length(), txtRect);126 127 position = orgY + _scaleFontStyle.size + _majorScaleLineLength + _scaleLabelToValueLength;128 if (!_showTickMarks) {129 position -= _majorScaleLineLength/* + _scaleLabelToValueLength*/;130 }131 132 canvas.drawText(txtLable, xLabel - txtRect.width() / 2, position, paint);133 }134 }135 paint.setStyle(Style.STROKE);136 paint.setColor(_axisLineStyle.color);137 paint.setStrokeWidth(_axisLineStyle.width);138 canvas.drawLine(plotArea.left, orgY, plotArea.right, orgY, paint);139 140 paint.setStyle(Style.FILL);141 paint.setColor(_titleFontStyle.color);142 paint.setTextSize(_titleFontStyle.size);143 144 Rect rect = GraphUtil.getBounds(paint, _title);145 position = orgY146 + _scaleFontStyle.size + _majorScaleLineLength147 + _scaleLabelToValueLength + _titleFontStyle.size;148 if (!_showTickMarks) {149 position -= _majorScaleLineLength/* + _scaleLabelToValueLength*/;150 }151 if (!_showLabels){152 position -= _scaleFontStyle.size;153 }154 canvas.drawText(_title, plotArea.right - rect.width() - 5, position, paint);155 }156 break;157 case X2: {158 double xPixelsPerUnit = plotArea.width()159 / (WorldToPhysical(_dMax) - WorldToPhysical(_dMin));160 161 // 网格 刻度线 标签 轴线 名称162 List axisLabels = labels;163 boolean showGridY = _grid.getVisible();164 float orgY = plotArea.top;165 int length = axisLabels.size();166 167 for (int i = 0; i < length; i++) {168 double label = axisLabels.get(i);169 float xLabel = (float) (plotArea.left + xPixelsPerUnit170 * (WorldToPhysical(label) - WorldToPhysical(_dMin)));171 172 paint.setStyle(Style.STROKE);173 if (showGridY) {174 paint.setStrokeWidth(_grid.getMarjorGridLineStyle().width);175 paint.setColor(_grid.getMarjorGridLineStyle().color);176 canvas.drawLine(xLabel, plotArea.bottom, xLabel,177 plotArea.top, paint);178 }179 if (_showTickMarks) {180 paint.setStrokeWidth(_scaleLineStyle.width);181 paint.setColor(_scaleLineStyle.color);182 canvas.drawLine(xLabel, orgY - _majorScaleLineLength,183 xLabel, orgY, paint);184 }185 if (_showLabels) {186 paint.setStyle(Style.FILL);187 paint.setColor(_scaleValueColor);188 paint.setTextSize(_scaleFontStyle.size);189 txtLable = getValueFormat(label);190 paint.getTextBounds(txtLable, 0, txtLable.length(), txtRect);191 192 position = orgY - _majorScaleLineLength- _scaleLabelToValueLength;193 if (!_showTickMarks) {194 position += _majorScaleLineLength/* + _scaleLabelToValueLength*/;195 }196 197 canvas.drawText(txtLable, xLabel - txtRect.width() / 2, position, paint);198 }199 }200 paint.setStyle(Style.STROKE);201 paint.setColor(_axisLineStyle.color);202 paint.setStrokeWidth(_axisLineStyle.width);203 canvas.drawLine(plotArea.left, orgY, plotArea.right, orgY, paint);204 205 paint.setStyle(Style.FILL);206 paint.setColor(_titleFontStyle.color);207 paint.setTextSize(_titleFontStyle.size);208 Rect rect = GraphUtil.getBounds(paint, _title);209 210 position = orgY - _scaleFontStyle.size - _majorScaleLineLength - _scaleLabelToValueLength;211 if (!_showTickMarks) {212 position += _majorScaleLineLength/* + _scaleLabelToValueLength*/;213 }214 if (!_showLabels){215 position += _scaleFontStyle.size;216 }217 218 canvas.drawText(_title, plotArea.right - rect.width() - 5, position, paint);219 220 }221 break;222 223 case Y: {224 double yPixelsPerUnit = plotArea.height()225 / (WorldToPhysical(_dMax) - WorldToPhysical(_dMin));226 227 // 网格 刻度线 轴线 标签 名称228 List axisLabels = labels;229 boolean showGridX = _grid.getVisible();230 float orgX = plotArea.left + originOffset;231 int length = axisLabels.size();232 233 for (int i = 0; i < length; i++) {234 double label = axisLabels.get(i);235 float yLabel = (float) (plotArea.bottom - yPixelsPerUnit236 * (WorldToPhysical(label) - WorldToPhysical(_dMin)));237 238 paint.setStyle(Style.STROKE);239 if (showGridX) {240 paint.setStrokeWidth(_grid.getMarjorGridLineStyle().width);241 paint.setColor(_grid.getMarjorGridLineStyle().color);242 canvas.drawLine(plotArea.left, yLabel, plotArea.right,243 yLabel, paint);244 }245 if (_showTickMarks) {246 paint.setStrokeWidth(_scaleLineStyle.width);247 paint.setColor(_scaleLineStyle.color);248 canvas.drawLine(orgX - _majorScaleLineLength, yLabel, orgX,249 yLabel, paint);250 }251 if (_showLabels) {252 paint.setStyle(Style.FILL);253 paint.setColor(_scaleValueColor);254 paint.setTextSize(_scaleFontStyle.size);255 txtLable = getValueFormat(label);256 paint.getTextBounds(txtLable, 0, txtLable.length(), txtRect);257 258 position = orgX - txtRect.width() - _majorScaleLineLength - _scaleLabelToValueLength;259 if (!_showTickMarks) {260 position += _majorScaleLineLength/* + _scaleLabelToValueLength*/;261 }262 canvas.drawText(txtLable, position, yLabel + _scaleFontStyle.size / 2, paint);263 }264 }265 paint.setStyle(Style.STROKE);266 paint.setColor(_axisLineStyle.color);267 paint.setStrokeWidth(_axisLineStyle.width);268 canvas.drawLine(orgX, plotArea.top, orgX, plotArea.bottom, paint);269 270 paint.setStyle(Style.FILL);271 paint.setColor(_titleFontStyle.color);272 paint.setTextSize(_titleFontStyle.size);273 Rect rect = GraphUtil.getBounds(paint, _title);274 int scaleVMaxWidth = getLablesMaxLength(paint, labels);275 276 position = orgX - scaleVMaxWidth - _majorScaleLineLength - _scaleLabelToValueLength - 2;277 if (!_showTickMarks) {278 position += _majorScaleLineLength/* + _scaleLabelToValueLength*/;279 }280 if (!_showLabels){281 position += scaleVMaxWidth;282 }283 GraphUtil.drawText(canvas, _title, position, plotArea.top + rect.width() + 5, paint, -90);284 }285 break;286 case Y2: {287 double yPixelsPerUnit = plotArea.height()288 / (WorldToPhysical(_dMax) - WorldToPhysical(_dMin));289 290 // 网格 刻度线 轴线 标签 名称291 List axisLabels = labels;292 boolean showGridX = _grid.getVisible();293 float orgX = plotArea.right;294 int length = axisLabels.size();295 296 for (int i = 0; i < length; i++) {297 double label = axisLabels.get(i);298 float yLabel = (float) (plotArea.bottom - yPixelsPerUnit299 * (WorldToPhysical(label) - WorldToPhysical(_dMin)));300 301 paint.setStyle(Style.STROKE);302 if (showGridX) {303 paint.setStrokeWidth(_grid.getMarjorGridLineStyle().width);304 paint.setColor(_grid.getMarjorGridLineStyle().color);305 canvas.drawLine(plotArea.left, yLabel, plotArea.right,306 yLabel, paint);307 }308 if (_showTickMarks) {309 paint.setStrokeWidth(_scaleLineStyle.width);310 paint.setColor(_scaleLineStyle.color);311 canvas.drawLine(orgX, yLabel, orgX + _majorScaleLineLength,312 yLabel, paint);313 }314 if (_showLabels) {315 paint.setStyle(Style.FILL);316 paint.setColor(_scaleValueColor);317 paint.setTextSize(_scaleFontStyle.size);318 txtLable = getValueFormat(label);319 paint.getTextBounds(txtLable, 0, txtLable.length(), txtRect);320 321 position = orgX + _majorScaleLineLength + _scaleLabelToValueLength;322 if (!_showTickMarks) {323 position -= _majorScaleLineLength/* + _scaleLabelToValueLength*/;324 }325 canvas.drawText(txtLable, position, yLabel + _scaleFontStyle.size / 2, paint);326 }327 }328 paint.setStyle(Style.STROKE);329 paint.setColor(_axisLineStyle.color);330 paint.setStrokeWidth(_axisLineStyle.width);331 canvas.drawLine(orgX, plotArea.top, orgX, plotArea.bottom, paint);332 333 paint.setStyle(Style.FILL);334 paint.setColor(_titleFontStyle.color);335 // Rect rect = GraphUtil.getBounds(paint, _title);336 int scaleVMaxWidth = getLablesMaxLength(paint, labels);337 338 position = orgX + scaleVMaxWidth + _majorScaleLineLength + _scaleLabelToValueLength + 2;339 if (!_showTickMarks) {340 position -= _majorScaleLineLength/* + _scaleLabelToValueLength*/;341 }342 if (!_showLabels){343 position -= scaleVMaxWidth;344 }345 GraphUtil.drawText(canvas, _title, position, plotArea.top + 5, paint, 90);346 }347 break;348 default:349 break;350 }351 }352 353 public int getAxisOutLength(Paint paint, List labels) {354 // 轴名称文本、刻度值文本、刻度线355 int outLength = 0;356 Rect rect = new Rect();357 358 if (_type == EnAxisType.X || _type == EnAxisType.X2) {359 if (_titleVisible) {360 paint.getTextBounds(_title, 0, _title.length(), rect);361 outLength += 1 + rect.height();362 }363 if (_showLabels) {364 outLength += 1 + _scaleFontStyle.size; //getLablesMaxLength(paint, labels)365 }366 if (_showTickMarks) {367 outLength += _scaleLabelToValueLength + _majorScaleLineLength;368 }369 } else if (_type == EnAxisType.Y || _type == EnAxisType.Y2) {370 if (_titleVisible) {371 paint.getTextBounds(_title, 0, _title.length(), rect);372 outLength += 1 + rect.height(); // 轴文本逆时针转90度,否则改成rect.width()373 }374 if (_showLabels) {375 outLength += 1 + getLablesMaxLength(paint, labels);376 }377 if (_showTickMarks) {378 outLength += _scaleLabelToValueLength + _majorScaleLineLength;379 }380 }381 return outLength;382 }383 384 private int getLablesMaxLength(Paint paint, List labels) {385 int len = 0;386 for (int i = 0; i < labels.size(); i++) {387 double label = labels.get(i);388 String s = getValueFormat(label);389 390 paint.setTextSize(_scaleFontStyle.size);391 Rect r = new Rect();392 paint.getTextBounds(s, 0, s.length(), r);393 394 if (len < r.width()) {395 len = r.width();396 }397 }398 return len;399 }400 401 public void setVisible(boolean visible) {402 _visible = visible;403 }404 405 public boolean getVisible() {406 return _visible;407 }408 409 public void setType(EnAxisType type) {410 _type = type;411 if (type == EnAxisType.X || type == EnAxisType.X2) {412 _grid.setType(EnGridType.X);413 } else {414 _grid.setType(EnGridType.Y);415 }416 }417 418 public EnAxisType getType() {419 return _type;420 }421 422 public void setTitle(String name) {423 _title = name;424 }425 426 public String getTitle() {427 return _title;428 }429 430 public void setMax(double max) {431 _dMax = max;432 }433 434 public double getMax() {435 return _dMax;436 }437 438 public void setMin(double min) {439 _dMin = min;440 if (_autoOrigin) {441 // if (_origin > _dMin) {442 _origin = _dMin;443 // }444 }445 }446 447 public double getMin() {448 return _dMin;449 }450 451 public void setOrigin(double origin) {452 _origin = origin;453 if (!_autoOrigin) {454 if (_origin < _dMin) {455 _dMin = _origin;456 }457 if (_origin > _dMax) {458 _dMax = _origin;459 }460 }461 }462 463 public double getOrigin() {464 return _origin;465 }466 467 public void setAutoOrigin(boolean autoOrigin) {468 _autoOrigin = autoOrigin;469 }470 471 public Grid getGrid() {472 return _grid;473 }474 475 public void setRange(double max, double min) {476 _dMax = max;477 _dMin = min;478 }479 480 public abstract List getLabels(double start, double end,481 int approxNumLabels);482 483 public abstract double[] getRealData(Object object);484 485 public abstract double WorldToPhysical(double val);486 487 public abstract String getValueFormat(double val);488 489 protected boolean IsAvailPoint(double val){490 if (val < _dMin || val > _dMax) {491 return false;492 }493 return true;494 }495 496 }
6.曲线类:AbstractSeries --> 线性曲线LineSeries、阶梯曲线StepSeries、散点曲线ScattSeries、柱状曲线BarSeries、饼状曲线PieSeries。
浮云E绘图的通用曲线控件源码定制之设计实现篇就写这么多,该曲线控件功能丰富,扩展性强,有java版和C++版本,功能下一篇文章将继续写用曲线控件源码定制重点和难点,并将提供Java版源码工程下载。
关键词:
- 通用曲线控件源码定制之设计实现篇(功能丰富灵活) 浮云E绘图
- 创新引领高质量发展,平安银行信用卡“A+进阶”升级三部曲收官
- 更换安装灯具近2000套 淮安市区多条道路路灯“换装升级”
- 避暑游催热中国“夏日经济”
- 第二次青藏科考队精确测量珠峰顶部积雪厚度
- 小米10s中框材质
- 韩日领导人将在北约峰会期间举行会谈
- 外媒:德国准备“阻止”乌克兰入北约进程,担心北约与俄罗斯开战
- 俄外交部:美国应对集束弹药造成的平民死伤负责
- 美国地质局调查报告:美国近一半自来水含有毒物质
- 日媒:预计日本政府或于8月开始核污染水排海
- 应急管理部派出工作组赶赴湖北宜昌五峰县山体滑坡救援现场
- 乌克兰国防部承认实施对克里米亚大桥的袭击
- 元宇宙迈入2.0时代 世界人工智能大会论坛热议产业创新
- 双预警持续发布!关注雷暴大风冰雹和高温
- 渝万高铁最高墩主墩承台浇筑完成
- 卢氏县法院:加强执行权运行机制改革 持续优化法治化营商环境
- 信用卡还款日(信用卡还款日是什么意思)
- 凉拌狗肉手撕狗肉(腌制好的)的做法?
- 潜水员戴夫找回逃跑的海马任务攻略