MPAndroidChart繪制K線圖(三)

MPAndroidChart繪制K線圖(一)高亮線自定義
MPAndroidChart繪制K線圖(二)動(dòng)態(tài)時(shí)間格式+高亮?xí)r底部滑動(dòng)時(shí)間刻度
MPAndroidChart繪制K線圖(三)長(zhǎng)按高亮,雙擊事件绑榴,縮放中心點(diǎn)變換样勃,圖表聯(lián)動(dòng)窖逗,跨表縮放失效

更新GitHub地址
自定義股線圖StockChart

三假哎、觸控事件處理
1.長(zhǎng)按高亮荸型,雙擊監(jiān)聽(tīng)

只需要監(jiān)聽(tīng)Chart的觸控事件险绘,處理長(zhǎng)按事件和雙擊事件即可换可;觸控事件是優(yōu)先處理OnTouchListener的,所以自定義OnTouchListener設(shè)置給Chart奠旺。

public class FingerTouchListener implements View.OnTouchListener {
    private BarLineChartBase mChart;
    private GestureDetector mDetector;
    private TouchCallback mListener;
    private boolean mIsLongPress = false;

    public FingerTouchListener(BarLineChartBase chart, TouchCallback listener) {
        mChart = chart;
        mListener = listener;
//   android自帶的GestureDetector可以滿足雙擊和長(zhǎng)按監(jiān)聽(tīng)
        mDetector = new GestureDetector(mChart.getContext(), new GestureDetector.SimpleOnGestureListener() {
            @Override
            public void onLongPress(MotionEvent e) {
                super.onLongPress(e);
                mIsLongPress = true;
                if (mListener != null) {
                    mListener.enableHighlight();
                }
                Highlight h = mChart.getHighlightByTouchPoint(e.getX(), e.getY());
                if (h != null && h.getDataIndex() >= 0) {
                    h.setDraw(e.getX(), e.getY());
//   長(zhǎng)按時(shí)觸發(fā)該chart高亮
                    mChart.highlightValue(h, true);
                    mChart.disableScroll();
                }
            }

            @Override
            public boolean onDoubleTap(MotionEvent e) {
                if (mListener != null) {
//  雙擊事件回調(diào)
                  mListener.onDoubleTap();
                }
                return super.onDoubleTap(e);
            }
        });
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
//  優(yōu)先使用mDetector處理onTouchEvent
        mDetector.onTouchEvent(event);
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            mIsLongPress = false;
        }
        if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) {
            mIsLongPress = false;
//  長(zhǎng)按事件結(jié)束了蜘澜,需要取消高亮
            mChart.highlightValue(null, true);
            if (mListener != null) {
                mListener.disableHighlight();
            }
            mChart.enableScroll();
        }
        if (mIsLongPress && event.getAction() == MotionEvent.ACTION_MOVE) {
            if (mListener != null) {
                mListener.enableHighlight();
            }
            Highlight h = mChart.getHighlightByTouchPoint(event.getX(), event.getY());
            if (h != null && h.getDataIndex() >= 0) {
                h.setDraw(event.getX(), event.getY());
                mChart.highlightValue(h, true);
                mChart.disableScroll();
            }
            return true;
        }
        return false;
    }

    public interface TouchCallback {
        void enableHighlight();

        void disableHighlight();

        void onDoubleTap();
    }
}

注意里面的 mChart.highlightValue(h, true)的第二個(gè)參數(shù),代表是否需要回調(diào)高亮監(jiān)聽(tīng)响疚,如果設(shè)置false鄙信,那么Chart中設(shè)置的setOnChartValueSelectedListener就不會(huì)調(diào)用(后面說(shuō)到高亮同步時(shí)會(huì)說(shuō)到),這里需要設(shè)置為true忿晕。

2.基于y軸縮放和基于觸控點(diǎn)縮放

默認(rèn)情況下雙指縮放時(shí)是基于雙指中心點(diǎn)縮放装诡,但是需求希望:當(dāng)數(shù)據(jù)較少,不滿一屏幕時(shí)践盼,雙指縮放基于左側(cè)y軸鸦采。查看源碼看了一下其縮放策略,在BarLineChartTouchListener中

     if (event.getPointerCount() >= 2) { // two finger zoom
                ......
                // 通過(guò)這個(gè)getTrans將觸控點(diǎn)轉(zhuǎn)換為縮放中心點(diǎn)
                MPPointF t = getTrans(mTouchPointCenter.x, mTouchPointCenter.y);
                ViewPortHandler h = mChart.getViewPortHandler();
                ......
                    if (canZoomMoreY || canZoomMoreX) {

                        mMatrix.set(mSavedMatrix);
                        mMatrix.postScale(scaleX, scaleY, t.x, t.y);
                ......

// 查看getTrans方法
    public MPPointF getTrans(float x, float y) {

        ViewPortHandler vph = mChart.getViewPortHandler();

        float xTrans = x - vph.offsetLeft();
        float yTrans = 0f;

        // check if axis is inverted
        if (inverted()) {
            yTrans = -(y - vph.offsetTop());
        } else {
            yTrans = -(mChart.getMeasuredHeight() - y - vph.offsetBottom());
        }

        return MPPointF.getInstance(xTrans, yTrans);
    }

確認(rèn)確實(shí)是getTrans方法中處理的咕幻,要實(shí)現(xiàn)基于y軸縮放只需要將getTrans中的x值置為0即可渔伯,同理需要基于右側(cè)y軸,將x值強(qiáng)制設(shè)置為最大值肄程。這里需要繼承BarLineChartTouchListener锣吼,重寫的getTrans方法,然后在初始化時(shí)實(shí)例化設(shè)置給chart蓝厌。

    @Override
    public MPPointF getTrans(float x, float y) {
        if (mListener != null) {
            int transEdge = mListener.getTransEdge();
            if (transEdge == TRANS_EDGE_LEFT) {
                x = 0;
            } else if (transEdge == TRANS_EDGE_RIGHT) {
                x = mChart.getWidth();
            }
        }
        return super.getTrans(x, y);
    }
3.圖表聯(lián)動(dòng)

chart提供了public void setOnChartGestureListener(OnChartGestureListener l)用于監(jiān)聽(tīng)各種手勢(shì)事件(縮放 平移 觸摸開(kāi)始 觸摸結(jié)束 長(zhǎng)按 雙擊 單擊 滑動(dòng)等)玄叠,同步兩個(gè)chart只需要監(jiān)聽(tīng)chart的縮放平移,同步設(shè)置給另外一張圖表拓提。

public class SyncChartGestureListener implements OnChartGestureListener {
    private Chart srcChart;
    private Chart[] dstCharts;

    public SyncChartGestureListener(Chart srcChart, Chart[] dstCharts) {
        this.srcChart = srcChart;
        this.dstCharts = dstCharts;
    }

    @Override
    public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { }

    @Override
    public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {}

    @Override
    public void onChartLongPressed(MotionEvent me) {}

    @Override
    public void onChartDoubleTapped(MotionEvent me) {}

    @Override
    public void onChartSingleTapped(MotionEvent me) {}

    @Override
    public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { }

    @Override
    public void onChartScale(MotionEvent me, float scaleX, float scaleY) {
        syncCharts();
    }

    @Override
    public void onChartTranslate(MotionEvent me, float dX, float dY) {
        syncCharts();
    }

    /**
     * 同步目標(biāo)chart和當(dāng)前chart的顯示效果
     */
    public void syncCharts() {
        Matrix srcMatrix;
        float[] srcVals = new float[9];
        Matrix dstMatrix;
        float[] dstVals = new float[9];

        srcMatrix = srcChart.getViewPortHandler().getMatrixTouch();
        srcMatrix.getValues(srcVals);

        for (Chart dstChart : dstCharts) {
            if (dstChart.getVisibility() == View.VISIBLE) {
                dstMatrix = dstChart.getViewPortHandler().getMatrixTouch();
                dstMatrix.getValues(dstVals);
                dstVals[Matrix.MSCALE_X] = srcVals[Matrix.MSCALE_X];
                dstVals[Matrix.MTRANS_X] = srcVals[Matrix.MTRANS_X];
                dstMatrix.setValues(dstVals);
                dstChart.getViewPortHandler().refresh(dstMatrix, dstChart, true);
            }
        }
    }
}
4.高亮聯(lián)動(dòng)

其中一個(gè)圖表高亮?xí)r另一個(gè)圖表也在相同的位置高亮读恃,有可用的api:setOnChartValueSelectedListener用于監(jiān)聽(tīng)高亮事件,比如給主表設(shè)置監(jiān)聽(tīng),同步設(shè)置給副表

 setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
            @Override
            public void onValueSelected(Entry e, Highlight h) {
                syncChart.highlightValue(h);
// highlightValue(h)調(diào)用的時(shí)highlightValue(highlight, false)寺惫, 即syncChart不會(huì)再重復(fù)回調(diào)高亮事件疹吃,
//否則可能導(dǎo)致兩個(gè)圖標(biāo)的高亮事件會(huì)無(wú)休止的循環(huán)調(diào)用
            }

            @Override
            public void onNothingSelected() {
                syncChart.highlightValue(null);
            }
        });

這樣寫在主表和副表都只有一條數(shù)據(jù)線時(shí)是沒(méi)問(wèn)題的,但是主表或者副表有多條指標(biāo)線時(shí)就會(huì)發(fā)現(xiàn)同步高亮有時(shí)會(huì)失效肌蜻。debug一下就會(huì)發(fā)現(xiàn)其實(shí)Highlight對(duì)象中封裝了不光是高亮點(diǎn)的x,y值必尼,還有高亮的數(shù)據(jù)dataSet在Data中的索引蒋搜,例如主表高亮數(shù)據(jù)索引為1,而在副表中只有一條數(shù)據(jù)線判莉,最大索引為0豆挽,那就會(huì)出現(xiàn)同步高亮失敗。
這里處理辦法就多了, 可以這樣子券盅,根據(jù)自己的需求帮哈,重新new Highlight,給他們?cè)O(shè)置對(duì)應(yīng)的dataIndex(有時(shí)候還需要設(shè)置DataSetIndex锰镀,new對(duì)象時(shí)最后一個(gè)參數(shù)就是DataSetIndex)娘侍,讓多個(gè)數(shù)據(jù)都高亮即可。

                Highlight highlight = new Highlight(h.getX(), Float.NaN, 0);
                highlight.setDataIndex(0);
                Highlight highlight1 = new Highlight(h.getX(), Float.NaN, 0);
                highlight1.setDataIndex(1);
                syncChart.highlightValues(new Highlight[]{highlight, highlight1});

也可以改造一下highlightValues相關(guān)的方法泳炉,不考慮index即可(沒(méi)測(cè)試過(guò))憾筏。(而我的項(xiàng)目中由于底部標(biāo)簽的需要,不使用庫(kù)的highlight[], 單獨(dú)維護(hù)了一個(gè)highlight花鹅,重載了highlightValues方法)

5. 跨表縮放失效

再說(shuō)一個(gè)有的童鞋可能遇到的問(wèn)題氧腰,就是主表和副表上下排列時(shí),需要同時(shí)給兩個(gè)表都設(shè)置聯(lián)動(dòng)刨肃,相應(yīng)的高亮同步時(shí)也是需要給兩個(gè)表都設(shè)置古拴。但是可能會(huì)擔(dān)心兩個(gè)表都設(shè)置聯(lián)動(dòng)會(huì)引起相互循環(huán)調(diào)用問(wèn)題,確實(shí)高亮聯(lián)動(dòng)可能會(huì)真友,setOnChartValueSelectedListener上面那段代碼里已經(jīng)說(shuō)明了原因黄痪,使用時(shí)注意即可。但是還有一個(gè)我個(gè)人認(rèn)為不好的地方是:雙指縮放時(shí)盔然,一個(gè)手指在主表一個(gè)手指在副表满力,那么縮放就會(huì)失效,因?yàn)榭s放事件只能在同一個(gè)表中處理轻纪。
我這里說(shuō)說(shuō)我個(gè)人的處理方式:直接讓主表占滿整個(gè)空間覆蓋住副表(不設(shè)背景 兩個(gè)表其實(shí)都可以看到)油额,想辦法讓主表的繪制區(qū)域剛好高于副表。這里設(shè)置了副表25%高度刻帚,而主表初始化時(shí)拿到其高度直接*0.75就可以讓主表底部空出25%的區(qū)域潦嘶,看上去就和主副表上下排列沒(méi)什么區(qū)別。更重要的時(shí)這樣操作時(shí)崇众,所有的觸控操作只要需在主表中進(jìn)行就可以了掂僵,聯(lián)動(dòng)和高亮也只需要由主表同步給副表航厚,不需要處理副表的任何觸控事件了。

  @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (w > 0 && h > 0 && w < 10000 && h < 10000) {
            mViewPortHandler.setChartDimens(w, h * 0.75f);
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锰蓬,一起剝皮案震驚了整個(gè)濱河市幔睬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌芹扭,老刑警劉巖麻顶,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異舱卡,居然都是意外死亡辅肾,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門轮锥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)矫钓,“玉大人,你說(shuō)我怎么就攤上這事舍杜⌒履龋” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵既绩,是天一觀的道長(zhǎng)杯活。 經(jīng)常有香客問(wèn)我,道長(zhǎng)熬词,這世上最難降的妖魔是什么旁钧? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮互拾,結(jié)果婚禮上歪今,老公的妹妹穿的比我還像新娘。我一直安慰自己颜矿,他們只是感情好寄猩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著骑疆,像睡著了一般田篇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上箍铭,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天泊柬,我揣著相機(jī)與錄音,去河邊找鬼诈火。 笑死兽赁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播刀崖,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼惊科,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了亮钦?” 一聲冷哼從身側(cè)響起馆截,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蜂莉,沒(méi)想到半個(gè)月后蜡娶,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡巡语,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年翎蹈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了淮菠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片男公。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖合陵,靈堂內(nèi)的尸體忽然破棺而出枢赔,到底是詐尸還是另有隱情,我是刑警寧澤拥知,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布踏拜,位于F島的核電站,受9級(jí)特大地震影響低剔,放射性物質(zhì)發(fā)生泄漏速梗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一襟齿、第九天 我趴在偏房一處隱蔽的房頂上張望姻锁。 院中可真熱鬧,春花似錦猜欺、人聲如沸位隶。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)涧黄。三九已至,卻和暖如春赋荆,著一層夾襖步出監(jiān)牢的瞬間笋妥,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工窄潭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挽鞠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像信认,于是被迫代替她去往敵國(guó)和親材义。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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

  • 公司項(xiàng)目使用RN實(shí)現(xiàn)的k線圖效果不忍直視嫁赏,調(diào)試過(guò)很多個(gè)版本都沒(méi)有達(dá)到理想的效果其掂,于是想用原生來(lái)實(shí)現(xiàn)。經(jīng)過(guò)調(diào)研選用的...
    vachex閱讀 3,367評(píng)論 1 2
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,101評(píng)論 1 32
  • 圖表控件庫(kù) MPAndroidChart 的使用 使用方法 項(xiàng)目源碼地址潦蝇,包含了很多類型的圖標(biāo) https://g...
    jinchuang閱讀 819評(píng)論 0 0
  • ORA-00001: 違反唯一約束條件 (.) 錯(cuò)誤說(shuō)明:當(dāng)在唯一索引所對(duì)應(yīng)的列上鍵入重復(fù)值時(shí)款熬,會(huì)觸發(fā)此異常。 O...
    我想起個(gè)好名字閱讀 5,314評(píng)論 0 9
  • “一個(gè)白日帶走了一點(diǎn)青春攘乒, 日子雖不能毀壞我印象里你所給我的光明 卻慢慢地使我不同了贤牛。” 的確不同了则酝,一點(diǎn)一滴的青...
    花落寂寂hjr閱讀 419評(píng)論 1 6