在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...** 一.修 ...
随机推荐
- 【SQL.基础构建-第二节(2/4)】
-- Tips:查询基础 --一.SELECT 语句基础-- 1.查询指定列:SELECT 关键字--语法:--SELECT <列名>, ... -- 希望查询列的名称- ...
- 还原Vue.js的data内的数组和对象
最近学习Vue.js发现其为了实现对data内的数组和对象进行双向绑定,将数组和对象进行了封装. 如下的对象 todos: [ { id: 1, title: ...
- Genymotion下载慢或者下载失败的解决办法
转.原文地址:http://blog.csdn.net/sean_css/article/details/52674091 办法如下: 1.首先点击界面上的 + 号(Add)按钮,选择你要下载的模拟器 ...
- p2p项目总结
1.关于ajax请求所要注意的地方:$.psot(url,json,callback,type) (1)url路径问题,在html中写绝对路径不能用EL表达式,EL表达式只能在jsp中使用 (2)js ...
- 简明shell入门
- ES6(解构赋值)
解构赋值 1.什么是解构赋值? 在语法上,就是赋值的作用,解构为(左边一种解构.右边一种解构,左右一一对应进入赋值) 2.解构赋值的分类. 1.左右为数组即为数组解构赋值:2.左右为对象即为对象解构赋 ...
- snmp爆破(python脚本)
snmp用来获取信息,然后利用获取的信息来进一步的渗透. 命令行有 snmpwalk -v 2c -c public ip system -c是密码,默认的密码是public 利用工具可以找windo ...
- [Luogu 3810]三维偏序
Description 有 $ n $ 个元素,第 $ i $ 个元素有 $ a_i $ .$ b_i $ .$ c_i $ 三个属性,设 $ f(i) $ 表示满足 $ a_j \leq a_i $ ...
- Codeforces Round #438 C. Qualification Rounds
Description Snark and Philip are preparing the problemset for the upcoming pre-qualification round f ...
- bzoj 4006: [JLOI2015]管道连接
Description 小铭铭最近进入了某情报部门,该部门正在被如何建立安全的通道连接困扰. 该部门有 n 个情报站,用 1 到 n 的整数编号.给出 m 对情报站 ui;vi 和费用 wi,表示情 ...