在MPAndroidChart库K线图的基础上画均线
CombinedChart
可以直接使用MPAndroidChart库里面提供的CombinedChart实现组合图形
Demo:CombinedChartDemo
——————分割线(如果想在一个图形上实现,可以参考下面的实现方式)——————
推荐直接使用CombinedChart实现
在GandleStickChart的基础上画均线
之前出过一篇MPAndroidChart的K线图上添加均线,但是在画均线的逻辑上有点问题,画出的均线永远是屏幕上显示的数据的均线,而不是全部数据的均线
在此调整了一下画均线的逻辑,并在上个版本上做了优化,使滑动更流畅,效果图如下:
画K线图的原理
在说画均线之前,先简单说说画K线图的流程
因为公司项目没有开源,现在只是简单聊下思路,贴下简单的代码(都是开源库里有的代码)
如果你已经用过并阅读过MPAndroidChart的代码,那么下面的东西,你一定能看懂。
步骤:
- 首先,库已经给我们封装了一个自定义控件,叫做CandleStickChart,专门是用来画K线的,我们把库引入到工程以后,再自定义一个类继承CandleStickChart控件,然后在我们自定义的控件里随意定义我们自己的K线图,就好了。
- 自定义的内容一般就是描述文字,X轴、Y轴的一些位置和样式,滑动、缩放动画,等等
自定义完我们的控件以后,就要显示数据了,如果你下载过Demo,你应该看过显示数据部分的实现,传递的数据一个是X轴的ArrayList和一个是Y轴的ArrayList,X轴一般是时间维度,Y轴传递的是每个时间维度要显示的数据对象,包括index、最高、最低、开盘、收盘,类似这样:
yVals1.add(new CandleEntry(i, high, low, open, close));
数据传过去以后,就到了画数据的部分了,画数据的核心方法是CandleStickChartRenderer.java 的 drawDataSet(Canvas c, CandleDataSet dataSet)方法,在这个方法里,通过mRenderPaint画K线的每个节点(显示在屏幕上的节点)
- 动画,缩放等等,我们暂时就不操心了。
整个K线从无到有,大概就是这样的一个流程被画出来,那么如何添加均线呢
画均线思路
通过上面步骤的第三步,想要画均线,每个节点只传开盘、收盘、最高、最低,明显是不够的,那么在传递数据的时候,就需要在外面把每个节点的均值,算出来(均线的计算方法可以参考之前的文章),一块传递过去(用于画均线),修改了一下CandleEntry的数据结构,添加了5节点均线值、10节点均线值、30节点均线值的属性,并添加了get、set方法,如果没有值,用-1表示,如下
Code
public class CandleEntry extends Entry {
……
private float ma_5 = 0f;
private float ma_10 = 0f;
private float ma_30 = 0f;
public CandleEntry(int xIndex, float shadowH, float shadowL, float open, float close,float ma5,float ma10,float ma30) {
super((shadowH + shadowL) / 2f, xIndex);
this.mShadowHigh = shadowH;
this.mShadowLow = shadowL;
this.mOpen = open;
this.mClose = close;
this.ma_5 = ma5;
this.ma_10 = ma10;
this.ma_30 = ma30;
}
public float getMa_5() {
return ma_5;
}
public void setMa_5(float ma_5) {
this.ma_5 = ma_5;
}
public float getMa_10() {
return ma_10;
}
public void setMa_10(float ma_10) {
this.ma_10 = ma_10;
}
public float getMa_30() {
return ma_30;
}
public void setMa_30(float ma_30) {
this.ma_30 = ma_30;
}
……
}
每个节点的均值计算以及数据传递的代码大概是下面这个样子:
Code
for (int i = 0; i < kLineInfo.getData().size(); i++) {
float high = kLineInfo.getData().get(i).getHigh();
float low = kLineInfo.getData().get(i).getLow();
float open = kLineInfo.getData().get(i).getOpen();
float close = kLineInfo.getData().get(i).getClose();
float ma5 = -1;
if (i >= 4) {
ma5 = 0;
for (int a = i - 4; a <= i; a++) {
ma5 += kLineInfo.getData().get(a).getClose();
}
ma5 /= 5;
}
float ma10 = -1;
if (i >= 9) {
ma10 = 0;
for (int a = i - 9; a <= i; a++) {
ma10 += kLineInfo.getData().get(a).getClose();
}
ma10 /= 10;
}
float ma30 = -1;
if (i >= 29) {
ma30 = 0;
for (int a = i - 29; a <= i; a++) {
ma30 += kLineInfo.getData().get(a).getClose();
}
ma30 /= 30;
}
yVals1.add(new CandleEntry(i, high, low, open, close, ma5, ma10, ma30));
}
显示
在画均线的之前,需要通过如下代码将传递过来的数据转换成在页面上显示的具体位置,存储到buffer里:
因为我们自己添加了ma5、ma10、ma30的属性,所以在转换的过程我们也要做一定的处理
Code
package ……;
import ……;
public class CandleBodyBuffer extends AbstractBuffer<CandleEntry> {
……
private void addBody(float left, float top, float right, float bottom, float ma5, float ma10, float ma30) {
buffer[index++] = left;
buffer[index++] = top;
buffer[index++] = right;
buffer[index++] = bottom;
buffer[index++] = ma5;
buffer[index++] = ma5;
buffer[index++] = ma10;
buffer[index++] = ma10;
buffer[index++] = ma30;
buffer[index++] = ma30;
}
@Override
public void feed(List<CandleEntry> entries) {
int size = (int) Math.ceil((mTo - mFrom) * phaseX + mFrom);
for (int i = mFrom; i < size; i++) {
CandleEntry e = entries.get(i);
addBody(e.getXIndex() - 0.5f + mBodySpace, e.getClose() * phaseY, e.getXIndex() + 0.5f - mBodySpace, e.getOpen() * phaseY, e.getMa_5() * phaseY, e.getMa_10() * phaseY, e.getMa_30() * phaseY);
}
reset();
}
}
Code
package ……;
import ……;
public class CandleShadowBuffer extends AbstractBuffer<CandleEntry> {
public CandleShadowBuffer(int size) {
super(size);
}
private void addShadow(float x1, float y1, float x2, float y2, float ma5, float ma10, float ma30) {
buffer[index++] = x1;
buffer[index++] = y1;
buffer[index++] = x2;
buffer[index++] = y2;
buffer[index++] = ma5;
buffer[index++] = ma5;
buffer[index++] = ma10;
buffer[index++] = ma10;
buffer[index++] = ma30;
buffer[index++] = ma30;
}
@Override
public void feed(List<CandleEntry> entries) {
int size = (int)Math.ceil((mTo - mFrom) * phaseX + mFrom);
for (int i = mFrom; i < size; i++) {
CandleEntry e = entries.get(i);
addShadow(e.getXIndex(), e.getHigh() * phaseY, e.getXIndex(), e.getLow() * phaseY, e.getMa_5() * phaseY, e.getMa_10() * phaseY, e.getMa_30() * phaseY);
}
reset();
}
}
在DataRenderer.java里定义一个画均线的画笔
protected Paint mMAPaint;
public DataRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(viewPortHandler);
……
mMAPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mMAPaint.setStrokeWidth(1);
mMAPaint.setColor(0xFFC7238B);
}
画均线
@Override
public void initBuffers() {
CandleData candleData = mChart.getCandleData();
mShadowBuffers = new CandleShadowBuffer[candleData.getDataSetCount()];
mBodyBuffers = new CandleBodyBuffer[candleData.getDataSetCount()];
for (int i = 0; i < mShadowBuffers.length; i++) {
CandleDataSet set = candleData.getDataSetByIndex(i);
mShadowBuffers[i] = new CandleShadowBuffer(set.getValueCount() * 10);
mBodyBuffers[i] = new CandleBodyBuffer(set.getValueCount() * 10);
}
}
protected void drawDataSet(Canvas c, CandleDataSet dataSet) {
……
int range = (maxx - minx) * 10;
……
float tempMA5X = -1;
float tempMA5Y = -1;
float tempMA10X = -1;
float tempMA10Y = -1;
float tempMA30X = -1;
float tempMA30Y = -1;
// draw the body
for (int j = 0; j < range; j += 10) {
……
float leftBody = bodyBuffer.buffer[j];
float open = bodyBuffer.buffer[j + 1];
float rightBody = bodyBuffer.buffer[j + 2];
float close = bodyBuffer.buffer[j + 3];
float ma5 = bodyBuffer.buffer[j + 5];
float ma10 = bodyBuffer.buffer[j + 7];
float ma30 = bodyBuffer.buffer[j + 9];
……
// 画5均线
if (e.getMa_5() != -1) {
if (tempMA5X != -1 && tempMA5Y != -1) {
// 画线
mMAPaint.setColor(0xFFC7238B);
c.drawLine(tempMA5X, tempMA5Y, (leftBody + rightBody) / 2, ma5, mMAPaint);
}
// 赋值
tempMA5X = (leftBody + rightBody) / 2;
tempMA5Y = ma5;
}
// 画10均线
if (e.getMa_10() != -1) {
if (tempMA10X != -1 && tempMA10Y != -1) {
// 画线
mMAPaint.setColor(0xFFDFAA2A);
c.drawLine(tempMA10X, tempMA10Y, (leftBody + rightBody) / 2, ma10, mMAPaint);
}
// 赋值
tempMA10X = (leftBody + rightBody) / 2;
tempMA10Y = ma10;
}
// 画30均线
if (e.getMa_30() != -1) {
if (tempMA30X != -1 && tempMA30Y != -1) {
// 画线
mMAPaint.setColor(0xFF268BC6);
c.drawLine(tempMA30X, tempMA30Y, (leftBody + rightBody) / 2, ma30, mMAPaint);
}
// 赋值
tempMA30X = (leftBody + rightBody) / 2;
tempMA30Y = ma30;
}
}
}
到此,搞定!解决了上一个方案重复绘制均线,导致滑动不是很流畅的问题。
说明
对于上面buffer为什么存了2遍
buffer[index++] = ma5;
buffer[index++] = ma5;
buffer[index++] = ma10;
buffer[index++] = ma10;
buffer[index++] = ma30;
buffer[index++] = ma30;
因为之前,里边只有4个属性,现在加了3个属性以后,他转换的计算有点问题
以前的方法可能是针对这4个属性转换的,我debug观察了下转换后的结果,数组的奇数角标和偶数角标位置的计算逻辑是不一样的
那么为了不改它太深层的东西(怕影响到其他图形,也不想给自己添加麻烦),我干脆就都赋值了两遍
但是我取值的时候,只取我想要的
float ma5 = bodyBuffer.buffer[j + 5];
float ma10 = bodyBuffer.buffer[j + 7];
float ma30 = bodyBuffer.buffer[j + 9];
this all!!
在MPAndroidChart库K线图的基础上画均线的更多相关文章
- MPAndroidChart的K线图上添加均线
MPAndroidChart的K线图上添加均线 效果图 均线计算方法: 通常说的5日均线,10日均线,其实就是根据当前K线节点的时间维度来说的,当前每个节点代表一天,那么上面的均线就叫做日均线(几日均 ...
- 如何看K线图基础知识
在日K线图中一般白线.黄线.紫线.绿线依次分别表示:5.10.20.60日移动平均线,但这并不是固定的,会根据设置的不同而不同,比如你也可以在系统里把它们设为5.15.30.60均线. 你看K线图的上 ...
- IOS 股票K线图、分时图
IOS 股票K线图.分时图,网上开源项目很少,质量也是参差不齐:偶尔搜索到看似有希望的文章,点进去,还是个标题党:深受毒害.经过一段时间的探索,终于在开源基础上完成了自己的股票K线图.分时图: 先放出 ...
- Android开源图表图形库K线图
Android开源图表图形库K线图 web端k线图一般使用TradingView,android原生的一般是在MPAndroidChart 基础上做开发的,目前看到一个比较好的K线开源组件是KChar ...
- 如何从零绘制k线图 -- 原生js canvas图表绘制
样式如下图 源码地址: https://github.com/sutianbinde/charts 编写这个需要具备canvas基础,如果没有canvas基础可以学习我前面的cnavas基础博客. 具 ...
- 【带着canvas去流浪(5)】绘制K线图
目录 一. 任务说明 二. 重点提示 三. 示例代码 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文 ...
- vue使用tradingview开发K线图相关问题
vue使用tradingview开发K线图相关问题 1.TradingView中文开发文档https://b.aitrade.ga/books/tradingview/CHANGE-LOG.html2 ...
- 关于k Line Chart (k线图)
K Line Chart python实现k线图的代码,之前找过matplotlib中文文档但是画k线图的finance方法已经弃用了.所以自己在网上搜寻一下加上改编,很好的实现出k线图, 代码如下: ...
- Vue中引入TradingView制作K线图
**前言: 本文使用的是1.10版本 , 可通过TradingView.version()查看当前版本. 附上开发文档地址:https://zlq4863947.gitbooks.i...** 一.修 ...
随机推荐
- 1028阿里RDS如何恢复云数据库MySQL的备份文件到自建数据库
参照 https://help.aliyun.com/knowledge_detail/41817.html 恢复云数据库MySQL的备份文件到自建数据库 更新时间:2017-07-27 14:52: ...
- Oracle表空间的管理方式
解释说明:表空间是一个逻辑概念:=> oracle 逻辑概念段区块管理方式: number one => tablespace number two=> segments Oracl ...
- “百度杯”CTF比赛 九月场_123(文件备份,爆破,上传)
题目在i春秋ctf训练营 翻看源码,发现提示: 打开user.php,页面一片空白,参考大佬的博客才知道可能会存在user.php.bak的备份文件,下载该文件可以得到用户名列表 拿去burp爆破: ...
- [POI 2004]SZP
Description Byteotian 中央情报局 (BIA) 雇佣了许多特工. 他们每个人的工作就是监视另一名特工.Byteasar 国王需要进行一次秘密行动,所以他要挑选尽量多的信得过的特工. ...
- 【NOIP2014TG】solution
链接:https://www.luogu.org/problem/lists?name=&orderitem=pid&tag=83|31 D1T1(rps) 题意:给你一个周期,以及胜 ...
- hdu 4288 离线线段树+间隔求和
Coder Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Su ...
- poj3580 splay树 REVOVLE循环
SuperMemo Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 12795 Accepted: 3989 Case T ...
- 【Tensorflow系列】使用Inception_resnet_v2训练自己的数据集并用Tensorboard监控
[写在前面] 用Tensorflow(TF)已实现好的卷积神经网络(CNN)模型来训练自己的数据集,验证目前较成熟模型在不同数据集上的准确度,如Inception_V3, VGG16,Inceptio ...
- Vue2学习(1)
学习Vue2的computed 属性和 watcher 主要将computed 和methods和watcher作比较,对其各自的相关优缺点作了介绍. computed 属性会基于它所依赖的数据进行缓 ...
- MyBatis 传入参数之parameterType
在MyBatis的select,insert,update,delete这些元素中都提到了parameterType这个属性.MyBatis现在使用parameterType有基本类型和JAVA复 ...