在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...** 一.修 ...
随机推荐
- 【基础】在css中绘制三角形及相关应用
简言 本文简要阐述了用CSS边框的方法在页面上绘制三角形,包括几种典型的三角形绘制,还介绍了几个简单的应用场景.利用边框绘制三角形方法只是众多方案中的一种,大家根据项目实际,选用最适宜项目的方案. 1 ...
- 关于jsp页面加载时报错500的问题
先说一下,问题的发生,个人做了个小系统,成品以后运行了几次,没有问题,结果最后一次测试时,发现登陆页面报错了: 账号密码输入正确,经过后台登陆后,按理说是应该进入登陆成功后的jsp页面,然而结果却是: ...
- [LeetCode] 2 Keys Keyboard 两键的键盘
Initially on a notepad only one character 'A' is present. You can perform two operations on this not ...
- volatile 到i++ 原子操作 详解
1.可见性(Visibility) 可见性是指,当一个线程修改了某一个全局共享变量的数值,其他线程是否能够知道这个修改. 显然,在串行程序来说可见性的问题是不存在的.因为你在任何一个地方操作修改了某个 ...
- 【Swfit】Swift与OC两种语法写单例的区别
Swift与OC两种语法写单例的区别 例如写一个NetworkTools的单例 (1)OC写单例 + (instancetype)sharedNetworkTools { static id inst ...
- NOIWC2018 游记
day1 上午是自习,做了一些杂题,看了一下ppt,中午准备了一下行李,就出发了,提前了一个小时,谁知道被坑爹导航弄得居然到晚了一点 当走到这里的时候我愣住了 纠结了一分钟,直到有个boy走了进去,我 ...
- bzoj 1880: [Sdoi2009]Elaxia的路线
Description 最近,Elaxia和w的关系特别好,他们很想整天在一起,但是大学的学习太紧张了,他们 必须合理地安排两个人在一起的时间.Elaxia和w每天都要奔波于宿舍和实验室之间,他们 希 ...
- 手机上的ROM与RAM
ROM:read only memory翻译为只读存储器. RAM:read access memory翻译为随机存储器. 下面是一张手机的配置参数表. 简单来说,RAM就是真正意义上的内存,而ROM ...
- Linux查看CPU、内存、进程使用情况(转)
在系统维护的过程中,随时可能有需要查看 CPU 使用率,并根据相应信息分析系统状况的需要.在 CentOS 中,可以通过 top 命令来查看 CPU 使用状况.运行 top 命令后,CPU 使用状态会 ...
- vue之生命周期
vue的生命周期的过程提供了我们执行自定义逻辑的机会,好好理解它的生命周期,对我们很有帮助. 1.vue实例的生命周期(vue2.0) 2.生命周期描述:(参考截图) 3.例子 window.vm = ...