一步一步教你寫股票走勢圖——分時圖二(自定義xy軸)


目錄
一步一步教你寫股票走勢圖——分時圖一(概述)
一步一步教你寫股票走勢圖——分時圖二(自定義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


最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末弃理,一起剝皮案震驚了整個濱河市溃论,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌痘昌,老刑警劉巖钥勋,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異辆苔,居然都是意外死亡算灸,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門姑子,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人测僵,你說我怎么就攤上這事街佑。” “怎么了捍靠?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵沐旨,是天一觀的道長。 經(jīng)常有香客問我榨婆,道長磁携,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任良风,我火速辦了婚禮谊迄,結果婚禮上,老公的妹妹穿的比我還像新娘烟央。我一直安慰自己统诺,他們只是感情好,可當我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布疑俭。 她就那樣靜靜地躺著粮呢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钞艇。 梳的紋絲不亂的頭發(fā)上啄寡,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天溜嗜,我揣著相機與錄音株扛,去河邊找鬼漓概。 笑死溜歪,一個胖子當著我的面吹牛瑞凑,可吹牛的內(nèi)容都是我干的扼褪。 我是一名探鬼主播砂碉,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼五辽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蹋岩?” 一聲冷哼從身側響起赖草,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎剪个,沒想到半個月后秧骑,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡扣囊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年乎折,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侵歇。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡骂澄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惕虑,到底是詐尸還是另有隱情坟冲,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布溃蔫,位于F島的核電站健提,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏伟叛。R本人自食惡果不足惜私痹,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望统刮。 院中可真熱鬧紊遵,春花似錦、人聲如沸侥蒙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辉哥。三九已至桦山,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間醋旦,已是汗流浹背恒水。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留饲齐,地道東北人钉凌。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像捂人,于是被迫代替她去往敵國和親御雕。 傳聞我的和親對象是個殘疾皇子矢沿,可洞房花燭夜當晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容