MPAndroidChart之LineChart(2)MarkerView

MarkerView


MPAndroidChart 系列:


MPAndroidChart 之LineChart(1)

MPAndroidChart之LineChart(2)MarkerView

MPAndroidChart之LinChart(3)scale縮放


對於MPAndroidChart的基本屬性和一些常見的設置前面的一篇博客MPAndroidChart之LineChart(1)大部分說到了,這篇將要實現MarkerView這個東西,我理解是提示覆蓋物,類似百度地圖的覆蓋物同一個意思,至於其他啥啥的,可以評論裏吐口水大笑


下面是官方demo裏LineChart圖表,那個99提示就是MarkerView




而我要實現自己的makerview,如下gif圖的左邊、右邊、底部隨着左右滑動顯示的提示:





我們先實現官方那種makerview,這個很簡單,這個其他博客也挺多都說了,我這裏還是說一下


1、首先定義好你要顯示的markerview的樣子是什麼樣的xml,比如

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
   
        <TextView
            android:id="@+id/tvContent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="7dp"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp"
            android:text=""
            android:textSize="12dp"
            android:textColor="@android:color/white"
            android:ellipsize="end"
            android:singleLine="true"
            android:textAppearance="?android:attr/textAppearanceSmall" />


</RelativeLayout>

想定義什麼樣的markview就在上面的xml裏面定義就好了


2、接下來是繼承markerview然後重寫refreshContent方法和getOffset方法


package com.mpandroidchartcsdn.mychart;

import android.content.Context;
import android.widget.TextView;

import com.github.mikephil.charting.components.MarkerView;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.utils.MPPointF;
import com.github.mikephil.charting.utils.Utils;
import com.mpandroidchartcsdn.R;

public class MyMarkerView extends MarkerView {

    private TextView tvContent;

    public MyMarkerView(Context context) {
        super(context, R.layout.custom_marker_view);

        tvContent = (TextView) findViewById(R.id.tvContent);
    }

    // callbacks everytime the MarkerView is redrawn, can be used to update the
    // content (user-interface) 每次 MarkerView 重繪此方法都會被調用,併爲您提供更新它顯示的內容的機會
    @Override
    public void refreshContent(Entry e, Highlight highlight) {
        //這裏就設置你想顯示到makerview上的數據,Entry可以得到X、Y軸座標,也可以e.getData()獲取其他你設置的數據
        tvContent.setText("" + Utils.formatNumber(e.getY(), 0, true));
        super.refreshContent(e, highlight);
    }


    /*
 * offset 是以點到的那個點作為 (0,0) 中心然後往右下角畫出來 該方法是讓markerview現實到座標的上方
 * 所以如果要顯示在點的上方
 * X=寬度的一半,負數
 * Y=高度的負數
  */
    @Override
    public MPPointF getOffset() {
        // Log.e("ddd", "width:" + (-(getWidth() / 2)) + "height:" + (-getHeight()));
        return new MPPointF(-(getWidth() / 2), -getHeight());
    }

}




3、在使用你自己定義的MyMarkerView


        MyMarkerView myMarkerView = new MyMarkerView(this);
        myMarkerView.setChartView(mLineChart);
        mLineChart.setMarker(myMarkerView);

OK,官方demo有,其他博客也很多如何定義MarkerView,這裏就不細說了。



接下來說上圖gif裏的左右和底部makerview的實現,繫好安全帶,拖拉機要啓動了....


上圖gif裏的實現是參考這篇博客“股票走勢圖”系列來實現的,但是這位兄弟博客的股票走勢圖系列使用的MPAndroidChart版本很老了,6月份寫的,現在我使用的最新版本3.0.2好多舊的api也不知道換成什麼了,而且也只是貼了下代碼。廢話結束,下面開始gif裏的markview的實現。



我們前面已經實現官方那種普通的markerview,它是顯示在LineChart裏面的,而我們下面要實現的是在軸的旁邊,比如:“我想實現左邊Y軸上顯示markerview,如gif的左邊一樣”,那我們肯定要找畫markerview的源碼了,Ok



順着我們繼承的MarKerView裏的方法refreshContent找到Chart類裏的drawMarkers方法,恩,一看方法名大概就是它了,我們來看看它裏面有些什麼


  /**
     * draws all MarkerViews on the highlighted positions
     */
    protected void drawMarkers(Canvas canvas) {

        // if there is no marker view or drawing marker is disabled
        if (mMarker == null || !isDrawMarkersEnabled() || !valuesToHighlight())
            return;

        for (int i = 0; i < mIndicesToHighlight.length; i++) {

            Highlight highlight = mIndicesToHighlight[i];

            IDataSet set = mData.getDataSetByIndex(highlight.getDataSetIndex());

            Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]);
            int entryIndex = set.getEntryIndex(e);

            // make sure entry not null
            if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX())
                continue;

            float[] pos = getMarkerPosition(highlight);

            // check bounds
            if (!mViewPortHandler.isInBounds(pos[0], pos[1]))
                continue;

            // callbacks to update the content
            mMarker.refreshContent(e, highlight);

            // draw the marker
            mMarker.draw(canvas, pos[0], pos[1]);
        }
    }


如果MPAndroidChart基本的設置熟悉的話,應該看得懂得,不懂就把每個不知道的打印出來,多試幾次就懂了,這裏我們一眼看過去最引人注意的就是mMarker.refreshContent(e,highlight)和mMarker.draw(canvas,pos[0],pos[1])了,恩,沒錯,refreshContent(e,highlight)就是回調更新要顯示的內容,這個暫時不管它,那只剩下mMarker.draw(canvas,pos[0],pos[1])了,馬丹,就是你了,找了那麼久,之前我說不懂參數是什麼就打印多試試就明白了的。


canvas:畫筆,pos[0]:x軸座標,pos[1]:y軸座標,

上面有X軸和Y軸座標,那我想實現gif座標那樣的markerview,是不是把X軸設置0就可以了呢?OK,接下來我們試試把X軸換成0看看Y軸左邊顯示markerview的樣子。


左邊顯示markerview步驟:


1、MarkerView xml


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tvContent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#a0a0a0"
        android:paddingLeft="1dp"
        android:paddingRight="1dp"
        android:text=""
        android:textColor="@android:color/white"
        android:textSize="12sp" />

</RelativeLayout>


2、繼承MarkerView的類,和實現官方的那種markerview 一模一樣,什麼都沒有變


package com.mpandroidchartcsdn.mychart;

import android.content.Context;
import android.widget.TextView;

import com.github.mikephil.charting.components.MarkerView;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.utils.MPPointF;
import com.github.mikephil.charting.utils.Utils;
import com.mpandroidchartcsdn.R;

public class LeftMarkerView extends MarkerView {

    private TextView tvContent;

    public LeftMarkerView(Context context) {
        super(context, R.layout.custom_marker_view);

        tvContent = (TextView) findViewById(R.id.tvContent);
    }

    // callbacks everytime the MarkerView is redrawn, can be used to update the
    // content (user-interface) 每次 MarkerView 重繪此方法都會被調用,併爲您提供更新它顯示的內容的機會
    @Override
    public void refreshContent(Entry e, Highlight highlight) {
        //這裏就設置你想顯示到makerview上的數據,Entry可以得到X、Y軸座標,也可以e.getData()獲取其他你設置的數據
        tvContent.setText("" + Utils.formatNumber(e.getY(), 0, true));
        super.refreshContent(e, highlight);
    }

}


3、這種需求我們最好不要去改源碼,畢竟不是bug之類或者沒辦法什麼的,so,重寫drawMarkers方法就好了


package com.mpandroidchartcsdn.mychart;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;

import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;

/**
 * Created by tujingwu on 2017/5/4
 * .
 */

public class MyLineChart2 extends LineChart {

    private LeftMarkerView myMarkerViewLeft;

    public MyLineChart2(Context context) {
        super(context);
    }

    public MyLineChart2(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setMyMarkerView(LeftMarkerView leftMarkerView) {
        this.myMarkerViewLeft = leftMarkerView;
    }

    @Override
    protected void drawMarkers(Canvas canvas) {
        // if there is no marker view or drawing marker is disabled 這裏記得把mMarker 改成自己的myMarkerViewLeft
        if (myMarkerViewLeft == null || !isDrawMarkersEnabled() || !valuesToHighlight())
            return;

        for (int i = 0; i < mIndicesToHighlight.length; i++) {

            Highlight highlight = mIndicesToHighlight[i];

            IDataSet set = mData.getDataSetByIndex(highlight.getDataSetIndex());

            Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]);
            int entryIndex = set.getEntryIndex(e);

            // make sure entry not null
            if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX())
                continue;

            float[] pos = getMarkerPosition(highlight);

            // check bounds
            if (!mViewPortHandler.isInBounds(pos[0], pos[1]))
                continue;

            // callbacks to update the content
            myMarkerViewLeft.refreshContent(e, highlight);
            // draw the marker  這裏我們把X軸原來的pos[0]改成0
            myMarkerViewLeft.draw(canvas, 0, pos[1]); //pos[]裏面裝的要draw的x:pos[0] draw的y:pos[1]
        }
    }
}


好的,我們把源碼裏drawMarkers裏的代碼拷過來,並且把mMarker == null換成myMarkerViewLeft,mMarker.draw(canvas,pos[0],pos[1])換成mMarker.draw(canvas,0,pos[1])。什麼事情都沒做,接下來看看效果




可能有人注意到markerview顯示的數據和Y軸對不上,這個我們沒有設置當然對不上,但是在Y軸左邊顯示Markerview我們已經實現了不是嗎生氣

不過有個問題:


4、修改


問個問題:

1、我們給X軸換成0了,雖然總體上沒什麼錯,但是作爲畫上去東西是不是很奇怪?(如果自定義view熟練點的就大概知道把0換成什麼的思路的)



就上面的問題可以這樣來解決,畢竟MPAndroidChart也給了挺多東西的,我們把上面的MyLineChart2裏的X/Y軸座標換一下,其他代碼還是沒變:


package com.mpandroidchartcsdn.mychart;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;

import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;

/**
 * Created by tujingwu on 2017/5/4
 * .
 */

public class MyLineChart2 extends LineChart {

    private LeftMarkerView myMarkerViewLeft;

    public MyLineChart2(Context context) {
        super(context);
    }

    public MyLineChart2(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setMyMarkerView(LeftMarkerView leftMarkerView) {
        this.myMarkerViewLeft = leftMarkerView;
    }

    @Override
    protected void drawMarkers(Canvas canvas) {
        // if there is no marker view or drawing marker is disabled 這裏記得把mMarker 改成自己的myMarkerViewLeft
        if (myMarkerViewLeft == null || !isDrawMarkersEnabled() || !valuesToHighlight())
            return;

        for (int i = 0; i < mIndicesToHighlight.length; i++) {

            Highlight highlight = mIndicesToHighlight[i];

            IDataSet set = mData.getDataSetByIndex(highlight.getDataSetIndex());

            Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]);
            int entryIndex = set.getEntryIndex(e);

            // make sure entry not null
            if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX())
                continue;

            float[] pos = getMarkerPosition(highlight);

            // check bounds
            if (!mViewPortHandler.isInBounds(pos[0], pos[1]))
                continue;

            // callbacks to update the content
            myMarkerViewLeft.refreshContent(e, highlight);
            // draw the marker  這裏我們把X軸原來我們設置0的換成mViewPortHandler.contentLeft() - myMarkerViewLeft.getWidth()
            //對於mViewPortHandler 官網wiki裏:https://github.com/PhilJay/MPAndroidChart/wiki/The-ViewPortHandler有說明的
            myMarkerViewLeft.draw(canvas, mViewPortHandler.contentLeft() - myMarkerViewLeft.getWidth(), 
pos[1] - myMarkerViewLeft.getHeight() / 2); //pos[]裏面裝的要draw的x:pos[0] draw的y:pos[1]
        }
    }
}




OK,我們來看看修改後的效果圖




OK,雖然和上面的一張gif一樣,但是我們成功把原來draw(canvas,pos[0],pos[1])換成了我們想要的,不是嗎大笑


Y軸左邊的markerview實現了,那Y軸右邊和X軸底部的MarkerView或X軸頂部的MakerView也不在話下了,下面給出Y軸左右,X軸底部實現MarkerView的


代碼


package com.mpandroidchartcsdn.mychart;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;

import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;

/**
 * Created by tujingwu on 2017/5/4
 * .
 */

public class MyLineChart2 extends LineChart {

    private MyMarkerView myMarkerViewLeft;
    private BottomMarkerView myMarkerViewRight;
    private BottomMarkerView mMyBottomMarkerView;

    public MyLineChart2(Context context) {
        super(context);
    }

    public MyLineChart2(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setMyMarkerView(LeftMarkerView leftMarkerView, RightMarkerView rightMarkerView, BottomMarkerView bottomMarkerView) {
        this.myMarkerViewLeft = leftMarkerView;
        this.myMarkerViewRight = rightMarkerView;
        this.mMyBottomMarkerView = bottomMarkerView;
    }

    @Override
    protected void drawMarkers(Canvas canvas) {
        // if there is no marker view or drawing marker is disabled 這裏記得把mMarker 改成自己的myMarkerViewLeft
        if ( myMarkerViewLeft == null || myMarkerViewRight == null || mMyBottomMarkerView == null || !isDrawMarkersEnabled() || !valuesToHighlight())
            return;

        for (int i = 0; i < mIndicesToHighlight.length; i++) {

            Highlight highlight = mIndicesToHighlight[i];

            IDataSet set = mData.getDataSetByIndex(highlight.getDataSetIndex());

            Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]);
            int entryIndex = set.getEntryIndex(e);

            // make sure entry not null
            if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX())
                continue;

            float[] pos = getMarkerPosition(highlight);

            // check bounds
            if (!mViewPortHandler.isInBounds(pos[0], pos[1]))
                continue;

            /**
             *  mIndicesToHighlight[i]裏面裝的是某條線的數據
             *如果想和該軸的數據對得上 可以自己添加個方法,像myMarkerViewLeft.setData(mIndicesToHighlight[i].getY());
             * 然後在自己定義的markerview裏接收就好
             */
            // callbacks to update the content
            myMarkerViewLeft.refreshContent(e, highlight);
            myMarkerViewRight.refreshContent(e, highlight);
            mMyBottomMarkerView.refreshContent(e, highlight);
            // draw the marker  這裏我們把X軸原來我們設置0的換成mViewPortHandler.contentLeft() - myMarkerViewLeft.getWidth()
            //對於mViewPortHandler 官網wiki裏:https://github.com/PhilJay/MPAndroidChart/wiki/The-ViewPortHandler有說明的
            myMarkerViewLeft.draw(canvas, mViewPortHandler.contentLeft() - myMarkerViewLeft.getWidth(),
pos[1] - myMarkerViewLeft.getHeight() / 2);
            myMarkerViewRight.draw(canvas, mViewPortHandler.contentRight(), pos[1] - myMarkerViewRight.getHeight() / 2);
            mMyBottomMarkerView.draw(canvas, pos[0] - mMyBottomMarkerView.getWidth() / 2, mViewPortHandler.contentBottom());
        }
    }
}




然後使用MyLineChart設置Markerview


  LeftMarkerView leftMarkerView = new LeftMarkerView(this);
        RightMarkerView rightMarkerView = new RightMarkerView(this);
        BottomMarkerView bottomMarkerView = new BottomMarkerView(this);
        mLineChart.setMyMarkerView(leftMarkerView, rightMarkerView, bottomMarkerView);


最終效果




OK,現在想怎麼顯示自己的MarkerView都行了。







發佈了35 篇原創文章 · 獲贊 33 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章