android 分时图x轴宽度,一步一步教你写股票走势图——分时图二(自定义xy轴)...

目录

一步一步教你写股票走势图——分时图一(概述)

一步一步教你写股票走势图——分时图二(自定义xy轴)

一步一步教你写股票走势图——分时图三(对齐图表、自定义柱状图高亮)

一步一步教你写股票走势图——分时图四(高亮联动)

一步一步教你写股票走势图——分时图五(自定义标记)

一步一步教你写股票走势图——K线图一(概述)

一步一步教你写股票走势图——K线图二(图表联动)

一步一步教你写股票走势图——K线图三(添加均线)

一步一步教你写股票走势图——K线图四(高亮联动一)

一步一步教你写股票走势图——K线图五(高亮联动二)

一步一步教你写股票走势图——商业版

demo更新地址https://github.com/AndroidJiang/StockChart

今天要研究的是分时图的自定义xy轴,为什么要自定义xy轴呢?因为我们原生的mp库满足不了我们分时图的需求,所以就得改啦!

X轴定义

正常的分时图x轴是这样的效果

9f884e1828175bfb052f4a1720c09021.png

如果用原生mp,效果如下

4f2da30dc6d18d3a4444705aa8e60cd2.png

看到差别了吧,怎么才能让中间显示11:30/13:00呢?

重写XAxis

将要显示的labels封装进去,方便set和get,不封装也行,这样好看点。

public class MyXAxis extends XAxis {

private SparseArray labels;

public SparseArray getXLabels() {

return labels;

}

public void setXLabels(SparseArray labels) {

this.labels = labels;

}

}

- 重写XAxisRenderer

这是什么?这里面都是渲染x轴,关于x轴的绘制,都在这个类里面,包括labels,lines以及添加的limitline(如果看到这,觉得不适应的话,建议多看看mp的源码,笔者也是从看源码开始的,看不懂多看看,慢慢理解,这些是程序员必须得走的路)。可以根据库自带的类去改。比如:

XAxisRendererBarChart.class

@Override

protected void drawLabels(Canvas c, float pos, PointF anchor) {

final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();

// pre allocate to save performance (dont allocate in loop)

float[] position = new float[] {

0f, 0f

};

BarData bd = mChart.getData();

int step = bd.getDataSetCount();

for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) {

position[0] = i * step + i * bd.getGroupSpace()

+ bd.getGroupSpace() / 2f;

// consider groups (center label for each group)

if (step > 1) {

position[0] += ((float) step - 1f) / 2f;

}

mTrans.pointValuesToPixel(position);

if (mViewPortHandler.isInBoundsX(position[0]) && i >= 0

&& i < mXAxis.getValues().size()) {

String label = mXAxis.getValues().get(i);

if (mXAxis.isAvoidFirstLastClippingEnabled()) {

// avoid clipping of the last

if (i == mXAxis.getValues().size() - 1) {

float width = Utils.calcTextWidth(mAxisLabelPaint, label);

if (position[0] + width / 2.f > mViewPortHandler.contentRight())

position[0] = mViewPortHandler.contentRight() - (width / 2.f);

// avoid clipping of the first

} else if (i == 0) {

float width = Utils.calcTextWidth(mAxisLabelPaint, label);

if (position[0] - width / 2.f < mViewPortHandler.contentLeft())

position[0] = mViewPortHandler.contentLeft() + (width / 2.f);

}

}

drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees);

}

}

}

我们自己重写后:

@Override

protected void drawLabels(Canvas c, float pos, PointF anchor) {

float[] position = new float[]{

0f, 0f

};

int count = mXAxis.getXLabels().size();

for (int i = 0; i < count; i++) {

/*获取label对应key值,也就是x轴坐标0,60,121,182,242*/

int ix = mXAxis.getXLabels().keyAt(i);

position[0] = ix;

/*在图表中的x轴转为像素,方便绘制text*/

mTrans.pointValuesToPixel(position);

String label = mXAxis.getXLabels().valueAt(i);

c.drawText(label, position[0],

pos + 10,

mAxisLabelPaint);

}

}

代码不做解释,请看注释。

659e88f19b0631656895fdbb7e7fa79a.png

ok,效果差不多实现,不过发现x轴左右两个数据越界了,我们可以继续优化,修改如下:

/*x轴越界*/

if (mViewPortHandler.isInBoundsX(position[0])) {

String label = mXAxis.getXLabels().valueAt(i);

/*文本长度*/

int labelWidth = Utils.calcTextWidth(mAxisLabelPaint, label);

/*右出界*/

if ((labelWidth / 2 + position[0]) > mChart.getViewPortHandler().contentRight()) {

position[0] = mChart.getViewPortHandler().contentRight() - labelWidth / 2;

} else if ((position[0] - labelWidth / 2) < mChart.getViewPortHandler().contentLeft()) {//左出界

position[0] = mChart.getViewPortHandler().contentLeft() + labelWidth / 2;

}

c.drawText(label, position[0],

pos+10,

mAxisLabelPaint);

}

ad2acadd6b7956f9e4680b9241d4d625.png

ok,x轴坐标部分定义基本完成!关于x轴线的绘制,原理和坐标一样,首先参考XAxisRendererBarChart的绘制:

@Override

public void renderGridLines(Canvas c) {

if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled())

return;

float[] position = new float[] {

0f, 0f

};

mGridPaint.setColor(mXAxis.getGridColor());

mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth());

BarData bd = mChart.getData();

int step = bd.getDataSetCount();

for (int i = mMinX; i < mMaxX; i += mXAxis.mAxisLabelModulus) {

position[0] = i * step + i * bd.getGroupSpace() - 0.5f;

mTrans.pointValuesToPixel(position);

if (mViewPortHandler.isInBoundsX(position[0])) {

c.drawLine(position[0], mViewPortHandler.offsetTop(), position[0],

mViewPortHandler.contentBottom(), mGridPaint);

}

}

}

我们里面造一个出来:

/*x轴垂直线*/

@Override

public void renderGridLines(Canvas c) {

if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled())

return;

float[] position = new float[]{

0f, 0f

};

mGridPaint.setColor(mXAxis.getGridColor());

mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth());

mGridPaint.setPathEffect(mXAxis.getGridDashPathEffect());

int count = mXAxis.getXLabels().size();

if (!mChart.isScaleXEnabled()) {

count -= 1;

}

for (int i = 0; i < count; i ++) {

int ix = mXAxis.getXLabels().keyAt(i);

position[0] = ix;

mTrans.pointValuesToPixel(position);

c.drawLine(position[0], mViewPortHandler.offsetTop(), position[0],

mViewPortHandler.contentBottom(), mGridPaint);

}

}

注:一定要知道库自带的mViewPortHandler,它能告诉你大多数你想知道的东西,有哪些,请自行查看源码。

- 重写LineChart

重写的目的是为了将某些参数传入到自定义的类中,也就是我们刚刚定义的两个类,我们里面的那些参数都是通过自定义LineChart穿传进去的,如果不重写,也是可以的,只不过activity的代码会更多而已,不便于查看。

mXAxis = new MyXAxis();

mXAxisRenderer = new MyXAxisRenderer(mViewPortHandler, (MyXAxis) mXAxis, mLeftAxisTransformer,this);

关于X轴自定义部分已经结束,如有问题,请留言或者邮件我。github上原作者项目有人提问能不能在x轴放图片,如果上面的代码理解了,图片当然不成问题。下面我们搞一搞Y轴。

Y轴定义

y轴有什么要搞的啊,我们来看一下分时图的成交量图,也就是柱状图。

c33d0c7fff47b7e128e42d65a4bdcc30.png

我靠,这跟原生库差十万八千里啊,说像的话,也行,可以设置一个只显示最大和最小, setShowOnlyMinMax(true);那么y轴我们的效果图就是这个样子的

cdad36c49c899dc7403ecfe2de9f5d0f.png

样子是像了点,但是跟需求还是不一样啊。好了,我们还是乖乖自定义吧。步骤和x轴一模一样,先来个YAxis

public class MyYAxis extends YAxis {

private String minValue;

public MyYAxis() {

super();

}

public MyYAxis(AxisDependency axis) {

super(axis);

}

public void setShowMaxAndUnit(String minValue) {

setShowOnlyMinMax(true);

this.minValue = minValue;

}

public String getMinValue(){

return minValue;

}

}

很简单,我们仍是稍微封装了几个方法。下面我们来定义Renderer,我们同样找系统现成的类,YAxisRendererRadarChart,里面重写了computeAxisValues方法,代码较多,这里不贴出来了,我们研究下,发现参数传入了最大最小值,我们找到这样一段代码是有用的

if (mYAxis.isShowOnlyMinMaxEnabled()) {

mYAxis.mEntryCount = 2;

mYAxis.mEntries = new float[2];

mYAxis.mEntries[0] = yMin;

mYAxis.mEntries[1] = yMax;

}

刚好符合我们的需求,这个类就是相当于提供y轴的数据。

我们重写也是类似的

@Override

protected void computeAxisValues(float min, float max) {

/*只显示最大最小情况下*/

if (mYAxis.isShowOnlyMinMaxEnabled()) {

mYAxis.mEntryCount = 2;

mYAxis.mEntries = new float[2];

mYAxis.mEntries[0] = min;

mYAxis.mEntries[1] = max;

return;

}

}

数据有了,我们就来画y轴labels。

@Override

protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) {

/*当有最小text的时候*/

if (!TextUtils.isEmpty(mYAxis.getMinValue()) && mYAxis.isShowOnlyMinMaxEnabled()) {

for (int i = 0; i < mYAxis.mEntryCount; i++) {

/*获取对应位置的值*/

String text = mYAxis.getFormattedLabel(i);

if (i == 0) {

text = mYAxis.getMinValue();

}

if (i == 1) {

c.drawText(text, fixedPosition, mViewPortHandler.contentTop() + offset * 2.5f + 3, mAxisLabelPaint);

} else if (i == 0) {

c.drawText(text, fixedPosition, mViewPortHandler.contentBottom() - 3, mAxisLabelPaint);

}

}

}

}

代码真的很简单,完全高仿库自带的。

源码:

protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) {

// draw

for (int i = 0; i < mYAxis.mEntryCount; i++) {

String text = mYAxis.getFormattedLabel(i);

if (!mYAxis.isDrawTopYLabelEntryEnabled() && i >= mYAxis.mEntryCount - 1)

return;

c.drawText(text, fixedPosition, positions[i * 2 + 1] + offset, mAxisLabelPaint);

}

}

如果发现位置显示的不好,可自行调整。

最后是重写BarChart,这里不写了,和LineChart一样,可以查看demo源码,下面贴出效果图:

24dc7abad8d0fed12ac97a0107493b50.png

ok,本篇关于xy轴的自定义就讲到这里,如果有疑问,欢迎留言,笔者将尽自己所能,帮助小伙伴们!下一篇将讲解折线图和柱状图的对齐以及柱状图的自定义高亮(因为库本来的高亮不满足需求嘛)。

目录

一步一步教你写股票走势图——分时图一(概述)

一步一步教你写股票走势图——分时图二(自定义xy轴)

一步一步教你写股票走势图——分时图三(对齐图表、自定义柱状图高亮)

一步一步教你写股票走势图——分时图四(高亮联动)

一步一步教你写股票走势图——分时图五(自定义标记)

一步一步教你写股票走势图——K线图一(概述)

一步一步教你写股票走势图——K线图二(图表联动)

一步一步教你写股票走势图——K线图三(添加均线)

一步一步教你写股票走势图——K线图四(高亮联动一)

一步一步教你写股票走势图——K线图五(高亮联动二)

一步一步教你写股票走势图——商业版

demo更新地址https://github.com/AndroidJiang/StockChart

<think>嗯,用户让我介绍一下如何实现一个分时走势图的绘制程序,数据是从一个CSV文件里读取的。首先,我需要明确分时走势图通常指的是股票或其他金融产品在一天内的价格随时间变化的图表,通常以分钟为单位显示。所以,我需要先确保数据中包含时间戳和价格信息,可能还有成交量之类的其他指标。 接下来,我应该考虑使用Python的库,比如pandas来处理数据,matplotlib或者plotly来绘图。用户提到的数据是market_data_ap_6.csv,所以首先得用pandas读取这个文件。然后检查数据的结构,看看时间列的格式是否正确,有没有开盘价、收盘价、最高价、最低价等信息。不过分时图一般用分钟线的收盘价,或者可能是tick数据,但通常分时图是分钟级别的。 然后,需要处理时间序列。可能CSV里的时间列是字符串,需要转换成datetime类型,并设置为索引。这样在绘图的时候,x就能正确显示时间。另外,分时图通常显示的是交易时间段,比如A股是上午9:30到11:30,下午1点到3点,所以可能需要过滤掉非交易时间的数据,或者在图表上标注出来。 接下来是绘图部分。分时图通常包括价格走势线和成交量柱状图。可能需要用子图来展示,上面是价格,下面是成交量。价格部分需要绘制随时间变化的线,可能还需要计算当日的均价线,比如移动平均线。另外,可能需要添加一些辅助线,比如昨收价格,或者最高、最低点标记。 然后,关于数据的处理,比如计算分时的涨跌幅度,或者处理可能的缺失数据。比如,某些时间段没有交易数据,是否需要插值或者保持空缺?这可能会影响图表的连续性。 另外,用户可能希望图表有一些交互性,如果用plotly的话,可以生成交互式图表,但如果是静态图的话,matplotlib就够了。不过用户没有特别说明,可能需要先确认,但考虑到用户可能希望简洁,先用matplotlib实现基础功能。 还有,分时图的X通常只显示时间,去掉日期部分,因为是一天的数据。所以需要设置X的格式,只显示小时和分钟。同时,可能需要调整刻度密度,避免重叠。 然后是成交量的处理,通常成交量用柱状图表示,颜色可能根据涨跌来变化,比如上涨为红色,下跌为绿色。但分时图的成交量颜色可能和价格变化相关,所以需要计算每个时间点的价格变化,然后对应颜色。 此外,可能还需要添加一些标签和标题,使图表更清晰。比如标题设置为当天的日期,坐标标签等。可能还需要网格线,方便观察。 最后,测试代码是否正确,检查数据是否有异常,比如时间是否排序正确,是否有缺失值需要处理。可能需要用样例数据来验证,比如market_data_ap_6.csv的结构是否包含必要的列,如timestamp, price, volume等。 可能的步骤总结: 1. 导入必要的库,如pandas, matplotlib。 2. 读取CSV文件,查看数据结构。 3. 处理时间列,转换为datetime,并设置为索引。 4. 过滤数据,确保在交易时间段内。 5. 计算移动平均线或其他指标。 6. 创建图表,分两个子图,价格和成交量。 7. 绘制价格线,均线,设置X格式。 8. 绘制成交量柱状图,根据价格变化着色。 9. 添加标签、标题、图例、网格线等。 10. 调整布局,显示图表。 可能遇到的问题:时间格式不正确,数据缺失,交易时间段的判断,颜色处理等。需要逐步调试,确保每个步骤正确。比如,判断时间是否在交易时间段内,可以用布尔索引过滤数据。对于颜色,可以用np.where根据价格变化情况决定颜色数组。 现在,把这些思路整理成代码示例,确保每一步都有注释,方便用户理解。同时,提醒用户可能需要根据实际数据调整列名和参数,比如时间列的名称是否为'timestamp',价格列是否为'price'等。如果数据中没有成交列,可能需要调整成交量部分的代码,或者说明如何处理。</think>以下是一个使用Python和Matplotlib实现分时走势图绘制的示例代码,假设CSV数据包含`timestamp`(时间戳)、`price`(价格)和`volume`(成交量)字段: ```python import pandas as pd import matplotlib.pyplot as plt import matplotlib.dates as mdates from matplotlib.ticker import FuncFormatter # 读取数据 df = pd.read_csv("market_data_ap_6.csv") # 预处理时间戳 df['timestamp'] = pd.to_datetime(df['timestamp']) df = df.set_index('timestamp') # 假设数据仅包含当日交易时间(否则需过滤非交易时段) # 示例过滤(A股交易时间:9:30-11:30, 13:00-15:00) df = df.between_time('09:30', '15:00') # 计算分时均线(例如5分钟均线) df['5min_avg'] = df['price'].rolling('5T').mean() # 创建画布和子图 fig, (ax1, ax2) = plt.subplots(2, 1, gridspec_kw={'height_ratios': [3, 1]}, figsize=(12, 8)) # 绘制价格走势 ax1.plot(df.index, df['price'], label='实时价格', color='royalblue', linewidth=1.5) ax1.plot(df.index, df['5min_avg'], label='5分钟均线', color='orange', linestyle='--', linewidth=1) # 设置价格图格式 ax1.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M')) # 仅显示时分 ax1.grid(linestyle='--', alpha=0.5) ax1.set_ylabel('价格', fontsize=10) ax1.legend(loc='upper left') # 绘制成交量(颜色根据价格变化) df['price_change'] = df['price'].diff() # 计算价格变化 colors = ['green' if x >=0 else 'red' for x in df['price_change']] ax2.bar(df.index, df['volume'], color=colors, width=0.002, alpha=0.8) # 设置成交量图格式 ax2.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M')) ax2.grid(linestyle='--', alpha=0.5) ax2.set_ylabel('成交量', fontsize=10) # 优化布局 plt.subplots_adjust(hspace=0.1) # 调整子图间距 plt.xlabel('交易时间', fontsize=10) plt.suptitle('分时走势图 - ' + df.index[0].strftime('%Y-%m-%d'), y=0.95) plt.show() ``` ### 关键说明: 1. **数据要求**: - CSV文件应包含至少三列:`timestamp`(时间戳)、`price`(最新价)、`volume`(成交量) - 时间戳需要能被`pd.to_datetime`解析(如`"2023-08-01 09:35:00"`) 2. **特性说明**: - **分时均线**:使用5分钟滚动平均体现短期趋势 - **颜色区分**:成交量柱状图根据价格涨跌显示红绿颜色 - **时间格式**:自动过滤非交易时段(需根据实际市场调整时间范围) - **布局优化**:价格图与成交量图3:1高度比例,共享X时间标签 3. **扩展建议**: ```python # 如需添加昨收价参考线 last_close = 100 # 假设昨日收盘价 ax1.axhline(last_close, color='gray', linestyle='--', linewidth=1) # 如需添加涨跌箭头标注 max_point = df['price'].idxmax() ax1.annotate(f'最高价{df["price"].max()}', xy=(max_point, df['price'].max()), xytext=(max_point, df['price'].max()*1.002), arrowprops=dict(arrowstyle="->")) ``` 建议根据实际数据字段调整列名,可通过`print(df.head())`查看数据列名。如果数据包含多日数据,需要先按日期分组处理。 最终效果示例(示意图): ``` [分时走势图] 价格曲线(蓝色)和均线(橙色虚线)在上方子图 成交量柱状图(红绿交替)在下方子图 X显示交易时间(如10:30、13:00等) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值