| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- import 'package:flutter/material.dart';
- import '../entity/candle_entity.dart';
- import '../k_chart_widget.dart' show MainState;
- import 'base_chart_renderer.dart';
- enum VerticalTextAlignment { left, right }
- //For TrendLine
- double? trendLineMax;
- double? trendLineScale;
- double? trendLineContentRec;
- class MainRenderer extends BaseChartRenderer<CandleEntity> {
- late double mCandleWidth;
- late double mCandleLineWidth;
- List<MainState> stateLi;
- bool isLine;
- //绘制的内容区域
- late Rect _contentRect;
- double _contentPadding = 5.0;
- List<int> maDayList;
- final ChartStyle chartStyle;
- final ChartColors chartColors;
- final double mLineStrokeWidth = 1.0;
- double scaleX;
- late Paint mLinePaint;
- final VerticalTextAlignment verticalTextAlignment;
- MainRenderer(
- Rect mainRect,
- double maxValue,
- double minValue,
- double topPadding,
- this.stateLi,
- this.isLine,
- int fixedLength,
- this.chartStyle,
- this.chartColors,
- this.scaleX,
- this.verticalTextAlignment,
- [this.maDayList = const [5, 10, 20]])
- : super(
- chartRect: mainRect,
- maxValue: maxValue,
- minValue: minValue,
- topPadding: topPadding,
- fixedLength: fixedLength,
- gridColor: chartColors.gridColor) {
- mCandleWidth = this.chartStyle.candleWidth;
- mCandleLineWidth = this.chartStyle.candleLineWidth;
- mLinePaint = Paint()
- ..isAntiAlias = true
- ..style = PaintingStyle.stroke
- ..strokeWidth = mLineStrokeWidth
- ..color = this.chartColors.kLineColor;
- _contentRect = Rect.fromLTRB(
- chartRect.left,
- chartRect.top + _contentPadding,
- chartRect.right,
- chartRect.bottom - _contentPadding);
- if (maxValue == minValue) {
- maxValue *= 1.5;
- minValue /= 2;
- }
- scaleY = _contentRect.height / (maxValue - minValue);
- }
- @override
- void drawText(Canvas canvas, CandleEntity data, double x) {
- if (isLine == true) return;
- for (int i = 0; i < stateLi.length; ++i) {
- TextSpan? span;
- if (stateLi[i] == MainState.MA) {
- span = TextSpan(
- children: _createMATextSpan(data),
- );
- } else if (stateLi[i] == MainState.BOLL) {
- span = TextSpan(
- children: [
- if (data.up != 0)
- TextSpan(
- text: "BOLL:${format(data.mb)} ",
- style: getTextStyle(this.chartColors.ma5Color)),
- if (data.mb != 0)
- TextSpan(
- text: "UB:${format(data.up)} ",
- style: getTextStyle(this.chartColors.ma10Color)),
- if (data.dn != 0)
- TextSpan(
- text: "LB:${format(data.dn)} ",
- style: getTextStyle(this.chartColors.ma30Color)),
- ],
- );
- } else if (stateLi[i] == MainState.SAR) {
- span = TextSpan(
- text: "SAR:${format(data.sar)}",
- style: getTextStyle(this.chartColors.sarColor),
- );
- }
- if (span == null) return;
- TextPainter tp =
- TextPainter(text: span, textDirection: TextDirection.ltr);
- tp.layout();
- Offset offset = Offset(x, chartRect.top - topPadding + i * 12);
- canvas.drawRect(
- Rect.fromLTRB(
- offset.dx - 2,
- offset.dy - 2,
- tp.width + offset.dx + 2,
- tp.height + offset.dy + 2,
- ),
- Paint()..color = this.chartColors.bgColor);
- tp.paint(canvas, offset);
- }
- }
- List<InlineSpan> _createMATextSpan(CandleEntity data) {
- List<InlineSpan> result = [];
- for (int i = 0; i < (data.maValueList?.length ?? 0); i++) {
- if (data.maValueList?[i] != 0) {
- var item = TextSpan(
- text: "MA${maDayList[i]}:${format(data.maValueList![i])} ",
- style: getTextStyle(this.chartColors.getMAColor(i)));
- result.add(item);
- }
- }
- return result;
- }
- @override
- void drawChart(CandleEntity lastPoint, CandleEntity curPoint, double lastX,
- double curX, Size size, Canvas canvas) {
- if (isLine) {
- drawPolyline(lastPoint.close, curPoint.close, canvas, lastX, curX);
- } else {
- drawCandle(curPoint, canvas, curX);
- /// draw chart main state
- for (int i = 0; i < stateLi.length; ++i) {
- if (stateLi[i] == MainState.MA) {
- drawMaLine(lastPoint, curPoint, canvas, lastX, curX);
- } else if (stateLi[i] == MainState.BOLL) {
- drawBollLine(lastPoint, curPoint, canvas, lastX, curX);
- } else if (stateLi[i] == MainState.SAR) {
- drawSAR(lastPoint, curPoint, canvas, lastX, curX);
- }
- }
- }
- }
- Shader? mLineFillShader;
- Path? mLinePath, mLineFillPath;
- Paint mLineFillPaint = Paint()
- ..style = PaintingStyle.fill
- ..isAntiAlias = true;
- //画折线图
- drawPolyline(double lastPrice, double curPrice, Canvas canvas, double lastX,
- double curX) {
- // drawLine(lastPrice + 100, curPrice + 100, canvas, lastX, curX, ChartColors.kLineColor);
- mLinePath ??= Path();
- // if (lastX == curX) {
- // mLinePath.moveTo(lastX, getY(lastPrice));
- // } else {
- //// mLinePath.lineTo(curX, getY(curPrice));
- // mLinePath.cubicTo(
- // (lastX + curX) / 2, getY(lastPrice), (lastX + curX) / 2, getY(curPrice), curX, getY(curPrice));
- // }
- if (lastX == curX) lastX = 0; //起点位置填充
- mLinePath!.moveTo(lastX, getY(lastPrice));
- mLinePath!.cubicTo((lastX + curX) / 2, getY(lastPrice), (lastX + curX) / 2,
- getY(curPrice), curX, getY(curPrice));
- //画阴影
- mLineFillShader ??= LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- tileMode: TileMode.clamp,
- colors: [
- this.chartColors.lineFillColor,
- this.chartColors.lineFillInsideColor
- ],
- ).createShader(Rect.fromLTRB(
- chartRect.left, chartRect.top, chartRect.right, chartRect.bottom));
- mLineFillPaint..shader = mLineFillShader;
- mLineFillPath ??= Path();
- mLineFillPath!.moveTo(lastX, chartRect.height + chartRect.top);
- mLineFillPath!.lineTo(lastX, getY(lastPrice));
- mLineFillPath!.cubicTo((lastX + curX) / 2, getY(lastPrice),
- (lastX + curX) / 2, getY(curPrice), curX, getY(curPrice));
- mLineFillPath!.lineTo(curX, chartRect.height + chartRect.top);
- mLineFillPath!.close();
- canvas.drawPath(mLineFillPath!, mLineFillPaint);
- mLineFillPath!.reset();
- canvas.drawPath(mLinePath!,
- mLinePaint..strokeWidth = (mLineStrokeWidth / scaleX).clamp(0.1, 1.0));
- mLinePath!.reset();
- }
- void drawMaLine(CandleEntity lastPoint, CandleEntity curPoint, Canvas canvas,
- double lastX, double curX) {
- for (int i = 0; i < (curPoint.maValueList?.length ?? 0); i++) {
- if (i == 3) {
- break;
- }
- if (lastPoint.maValueList?[i] != 0) {
- drawLine(lastPoint.maValueList?[i], curPoint.maValueList?[i], canvas,
- lastX, curX, this.chartColors.getMAColor(i));
- }
- }
- }
- void drawBollLine(CandleEntity lastPoint, CandleEntity curPoint,
- Canvas canvas, double lastX, double curX) {
- if (lastPoint.up != 0) {
- drawLine(lastPoint.up, curPoint.up, canvas, lastX, curX,
- this.chartColors.ma10Color);
- }
- if (lastPoint.mb != 0) {
- drawLine(lastPoint.mb, curPoint.mb, canvas, lastX, curX,
- this.chartColors.ma5Color);
- }
- if (lastPoint.dn != 0) {
- drawLine(lastPoint.dn, curPoint.dn, canvas, lastX, curX,
- this.chartColors.ma30Color);
- }
- }
- void drawSAR(CandleEntity lastPoint, CandleEntity curPoint, Canvas canvas,
- double lastX, double curX) {
- final sar = curPoint.sar;
- if (sar == null) return;
- final halfHL = (curPoint.high + curPoint.low) / 2;
- late final color;
- if (sar == halfHL) {
- color = this.chartColors.avgColor;
- } else if (sar < halfHL) {
- color = this.chartColors.upColor;
- } else {
- color = this.chartColors.dnColor;
- }
- drawCircle(canvas, curX, sar, color);
- }
- void drawCandle(CandleEntity curPoint, Canvas canvas, double curX) {
- var high = getY(curPoint.high);
- var low = getY(curPoint.low);
- var open = getY(curPoint.open);
- var close = getY(curPoint.close);
- double r = mCandleWidth / 2;
- double lineR = mCandleLineWidth / 2;
- if (open >= close) {
- // 实体高度>= CandleLineWidth
- if (open - close < mCandleLineWidth) {
- open = close + mCandleLineWidth;
- }
- chartPaint.color = this.chartColors.upColor;
- canvas.drawRect(
- Rect.fromLTRB(curX - r, close, curX + r, open), chartPaint);
- canvas.drawRect(
- Rect.fromLTRB(curX - lineR, high, curX + lineR, low), chartPaint);
- } else if (close > open) {
- // 实体高度>= CandleLineWidth
- if (close - open < mCandleLineWidth) {
- open = close - mCandleLineWidth;
- }
- chartPaint.color = this.chartColors.dnColor;
- canvas.drawRect(
- Rect.fromLTRB(curX - r, open, curX + r, close), chartPaint);
- canvas.drawRect(
- Rect.fromLTRB(curX - lineR, high, curX + lineR, low), chartPaint);
- }
- }
- @override
- void drawVerticalText(canvas, textStyle, int gridRows) {
- double rowSpace = chartRect.height / gridRows;
- for (var i = 0; i <= gridRows; ++i) {
- double value = (gridRows - i) * rowSpace / scaleY + minValue;
- TextSpan span = TextSpan(text: "${format(value)}", style: textStyle);
- TextPainter tp =
- TextPainter(text: span, textDirection: TextDirection.ltr);
- tp.layout();
- double offsetX;
- switch (verticalTextAlignment) {
- case VerticalTextAlignment.left:
- offsetX = 0;
- break;
- case VerticalTextAlignment.right:
- offsetX = chartRect.width - tp.width;
- break;
- }
- if (i == 0) {
- tp.paint(canvas, Offset(offsetX, topPadding));
- } else {
- tp.paint(
- canvas, Offset(offsetX, rowSpace * i - tp.height + topPadding));
- }
- }
- }
- @override
- void drawGrid(Canvas canvas, int gridRows, int gridColumns) {
- // final int gridRows = 4, gridColumns = 4;
- double rowSpace = chartRect.height / gridRows;
- for (int i = 0; i <= gridRows; i++) {
- canvas.drawLine(Offset(0, rowSpace * i + topPadding),
- Offset(chartRect.width, rowSpace * i + topPadding), gridPaint);
- }
- double columnSpace = chartRect.width / gridColumns;
- for (int i = 0; i <= columnSpace; i++) {
- canvas.drawLine(Offset(columnSpace * i, 0),
- Offset(columnSpace * i, chartRect.bottom), gridPaint);
- }
- }
- @override
- double getY(double y) {
- //For TrendLine
- updateTrendLineData();
- return (maxValue - y) * scaleY + _contentRect.top;
- }
- void updateTrendLineData() {
- trendLineMax = maxValue;
- trendLineScale = scaleY;
- trendLineContentRec = _contentRect.top;
- }
- }
|