目錄
一步一步教你寫股票走勢圖——分時圖一(概述)
一步一步教你寫股票走勢圖——分時圖二(自定義xy軸)
一步一步教你寫股票走勢圖——分時圖三(對齊圖表、自定義柱狀圖高亮)
一步一步教你寫股票走勢圖——分時圖四(高亮聯(lián)動)
一步一步教你寫股票走勢圖——分時圖五(自定義標記)
demo更新地址https://github.com/AndroidJiang/StockChart
今天要研究的是分時圖的自定義xy軸,為什么要自定義xy軸呢纱扭?因為我們原生的mp庫滿足不了我們分時圖的需求玻墅,所以就得改啦山林!
X軸定義
正常的分時圖x軸是這樣的效果
如果用原生mp润脸,效果如下
看到差別了吧漠魏,怎么才能讓中間顯示11:30/13:00呢倔矾?
- 重寫XAxis
將要顯示的labels封裝進去,方便set和get柱锹,不封裝也行哪自,這樣好看點。
public class MyXAxis extends XAxis {
private SparseArray<String> labels;
public SparseArray<String> getXLabels() {
return labels;
}
public void setXLabels(SparseArray<String> labels) {
this.labels = labels;
}
}
- 重寫XAxisRenderer
這是什么禁熏?這里面都是渲染x軸壤巷,關于x軸的繪制,都在這個類里面瞧毙,包括labels胧华,lines以及添加的limitline(如果看到這寄症,覺得不適應的話,建議多看看mp的源碼矩动,筆者也是從看源碼開始的瘸爽,看不懂多看看,慢慢理解铅忿,這些是程序員必須得走的路)×橥簦可以根據(jù)庫自帶的類去改檀训。比如:
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);
}
}
代碼不做解釋峻凫,請看注釋。
ok览露,效果差不多實現(xiàn)荧琼,不過發(fā)現(xiàn)x軸左右兩個數(shù)據(jù)越界了,我們可以繼續(xù)優(yōu)化差牛,修改如下:
/*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);
}
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,它能告訴你大多數(shù)你想知道的東西侦讨,有哪些驶冒,請自行查看源碼。
- 重寫LineChart
重寫的目的是為了將某些參數(shù)傳入到自定義的類中韵卤,也就是我們剛剛定義的兩個類骗污,我們里面的那些參數(shù)都是通過自定義LineChart穿傳進去的,如果不重寫沈条,也是可以的需忿,只不過activity的代碼會更多而已,不便于查看蜡歹。
mXAxis = new MyXAxis();
mXAxisRenderer = new MyXAxisRenderer(mViewPortHandler, (MyXAxis) mXAxis, mLeftAxisTransformer,this);
關于X軸自定義部分已經(jīng)結束贴谎,如有問題,請留言或者郵件我季稳。github上原作者項目有人提問能不能在x軸放圖片擅这,如果上面的代碼理解了,圖片當然不成問題景鼠。下面我們搞一搞Y軸仲翎。
Y軸定義
y軸有什么要搞的啊痹扇,我們來看一下分時圖的成交量圖,也就是柱狀圖溯香。
我靠鲫构,這跟原生庫差十萬八千里啊,說像的話玫坛,也行结笨,可以設置一個只顯示最大和最小, setShowOnlyMinMax(true);那么y軸我們的效果圖就是這個樣子的
樣子是像了點湿镀,但是跟需求還是不一樣啊炕吸。好了,我們還是乖乖自定義吧勉痴。步驟和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;
}
}
很簡單,我們?nèi)允巧晕⒎庋b了幾個方法蒸矛。下面我們來定義Renderer,我們同樣找系統(tǒng)現(xiàn)成的類瀑罗,YAxisRendererRadarChart,里面重寫了computeAxisValues方法雏掠,代碼較多斩祭,這里不貼出來了,我們研究下乡话,發(fā)現(xiàn)參數(shù)傳入了最大最小值停忿,我們找到這樣一段代碼是有用的
if (mYAxis.isShowOnlyMinMaxEnabled()) {
mYAxis.mEntryCount = 2;
mYAxis.mEntries = new float[2];
mYAxis.mEntries[0] = yMin;
mYAxis.mEntries[1] = yMax;
}
剛好符合我們的需求,這個類就是相當于提供y軸的數(shù)據(jù)蚊伞。
我們重寫也是類似的
@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;
}
}
數(shù)據(jù)有了席赂,我們就來畫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);
}
}
如果發(fā)現(xiàn)位置顯示的不好,可自行調(diào)整掠拳。
最后是重寫B(tài)arChart癞揉,這里不寫了,和LineChart一樣溺欧,可以查看demo源碼喊熟,下面貼出效果圖:
ok,本篇關于xy軸的自定義就講到這里姐刁,如果有疑問芥牌,歡迎留言,筆者將盡自己所能聂使,幫助小伙伴們壁拉!下一篇將講解折線圖和柱狀圖的對齊以及柱狀圖的自定義高亮(因為庫本來的高亮不滿足需求嘛)谬俄。
目錄
一步一步教你寫股票走勢圖——分時圖一(概述)
一步一步教你寫股票走勢圖——分時圖二(自定義xy軸)
一步一步教你寫股票走勢圖——分時圖三(對齊圖表、自定義柱狀圖高亮)
一步一步教你寫股票走勢圖——分時圖四(高亮聯(lián)動)
一步一步教你寫股票走勢圖——分時圖五(自定義標記)
demo更新地址https://github.com/AndroidJiang/StockChart