1. 程式人生 > >一步一步教你寫股票走勢圖——分時圖五(自定義標記)

一步一步教你寫股票走勢圖——分時圖五(自定義標記)

分時圖開發到現在,基本已經完成了,這部分開發完,就會開始著手k線圖部分的開發。於是我們做了最後的對比,看下還有什麼地方需要增加修改,最後發現,還有標記檢視沒有,先看下什麼是標記檢視(博主不知道這個名字叫啥,自己起的):

這裡寫圖片描述

我們可以看到高亮線的左右兩邊分別標記了y軸的左右兩邊的資料, 而且左右滑動的時候,他們位置會隨著交界點的變化而變。

本節將會介紹下如何去自定義左右兩個標記,相信小夥伴們理解之後,中間的白點也是可以自己任意新增的,不妨在本篇結束後,自己動手試一試!

好了,我們開始本章節的內容吧!

相信有的小夥伴們在剛使用MP這個庫的時候,一定見過如下的東東:

這裡寫圖片描述

沒錯,這就是標記檢視,選中某條資料的情況下,出現的檢視,我們就仿照它就沒錯。

看原始碼

MarkerView

public abstract class MarkerView extends RelativeLayout {

    /**
     * Constructor. Sets up the MarkerView with a custom layout resource.
     *
     * @param context
     * @param layoutResource the layout resource to use for the MarkerView
     */
    public MarkerView(Context context, int
layoutResource) { super(context); setupLayoutResource(layoutResource); } /** * Sets the layout resource for a custom MarkerView. * * @param layoutResource */ private void setupLayoutResource(int layoutResource) { View inflated = LayoutInflater.from(getContext()).inflate(layoutResource, this
); inflated.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); inflated.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); // measure(getWidth(), getHeight()); inflated.layout(0, 0, inflated.getMeasuredWidth(), inflated.getMeasuredHeight()); } /** * Draws the MarkerView on the given position on the screen with the given Canvas object. * * @param canvas * @param posx * @param posy */ public void draw(Canvas canvas, float posx, float posy) { // take offsets into consideration posx += getXOffset(posx); posy += getYOffset(posy); // translate to the correct position and draw canvas.translate(posx, posy); draw(canvas); canvas.translate(-posx, -posy); } /** * This method enables a specified custom MarkerView to update it's content everytime the MarkerView is redrawn. * * @param e The Entry the MarkerView belongs to. This can also be any subclass of Entry, like BarEntry or * CandleEntry, simply cast it at runtime. * @param highlight the highlight object contains information about the highlighted value such as it's dataset-index, the * selected range or stack-index (only stacked bar entries). */ public abstract void refreshContent(Entry e, Highlight highlight); /** * Use this to return the desired offset you wish the MarkerView to have on the x-axis. By returning -(getWidth() / * 2) you will center the MarkerView horizontally. * * @param xpos the position on the x-axis in pixels where the marker is drawn * @return */ public abstract int getXOffset(float xpos); /** * Use this to return the desired position offset you wish the MarkerView to have on the y-axis. By returning * -getHeight() you will cause the MarkerView to be above the selected value. * * @param ypos the position on the y-axis in pixels where the marker is drawn * @return */ public abstract int getYOffset(float ypos); }

建構函式中要傳入顯示的佈局檔案,draw會根據傳入的position定位,refreshContent更新裡面的文字。原始碼讀起來很簡單,只有幾個方法而已,我們在例項的時候只需傳入我們的佈局即可,於是我們模仿了一個寫法:

public class MyMarkerView extends MarkerView {
    private TextView markerTv;
    public MyMarkerView(Context context, int layoutResource) {
        super(context, layoutResource);
        markerTv = (TextView) findViewById(R.id.marker_tv);
    }

    @Override
    public void refreshContent(Entry e, Highlight highlight) {
   }

    @Override
    public int getXOffset(float xpos) {
        return 0;
    }

    @Override
    public int getYOffset(float ypos) {
        return 0;
    }
}


……

 private void setMarkerView(){
        MyMarkerView myMarkerView = new MyMarkerView(MinutesActivity.this, R.layout.mymarkerview);
        lineChart.setMarkerView(myMarkerView);
    }

最後圖表進行設定markerview,我們看下效果圖:
這裡寫圖片描述

問題:

  • 位置不對,預設只有一個,要顯示兩個標記
  • 觸控柱狀圖時,上面不顯示標記

    MrrkerView自身偏離Offset完成不了這樣的需求,那麼我們只能從表的重寫入手了。

在MyLineChart中重寫drawMarkerw方法


public void setHighlightValue(Entry e, Highlight h) {
        this.e = e;
        if (mData == null)
            mIndicesToHighlight = null;
        else {
            mIndicesToHighlight = new Highlight[]{
                    h};
        }
        invalidate();
    }
  @Override
    protected void drawMarkers(Canvas canvas) {
          if (!mDrawMarkerViews || !valuesToHighlight())
            return;
        for (int i = 0; i < mIndicesToHighlight.length; i++) {
            Highlight highlight = mIndicesToHighlight[i];
            int xIndex = mIndicesToHighlight[i].getXIndex();
            int dataSetIndex = mIndicesToHighlight[i].getDataSetIndex();
            float deltaX = mXAxis != null
                    ? mXAxis.mAxisRange
                    : ((mData == null ? 0.f : mData.getXValCount()) - 1.f);
            if (xIndex <= deltaX && xIndex <= deltaX * mAnimator.getPhaseX()) {
                Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]);
                // make sure entry not null
                if (e == null || e.getXIndex() != mIndicesToHighlight[i].getXIndex())
                    continue;
                float[] pos = getMarkerPosition(e, highlight);
                // check bounds
                if (!mViewPortHandler.isInBounds(pos[0], pos[1]))
                    continue;

                float yValForXIndex1 = minuteHelper.getDatas().get(mIndicesToHighlight[i].getXIndex()).cjprice;
                float yValForXIndex2 = minuteHelper.getDatas().get(mIndicesToHighlight[i].getXIndex()).per;

                myMarkerViewLeft.setData(yValForXIndex1);
                myMarkerViewRight.setData(yValForXIndex2);
                myMarkerViewLeft.refreshContent(e, mIndicesToHighlight[i]);
                myMarkerViewRight.refreshContent(e, mIndicesToHighlight[i]);
                /*修復bug*/
                // invalidate();
                /*重新計算大小*/
                myMarkerViewLeft.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                myMarkerViewLeft.layout(0, 0, myMarkerViewLeft.getMeasuredWidth(),
                        myMarkerViewLeft.getMeasuredHeight());
                myMarkerViewRight.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                myMarkerViewRight.layout(0, 0, myMarkerViewRight.getMeasuredWidth(),
                        myMarkerViewRight.getMeasuredHeight());

                myMarkerViewLeft.draw(canvas, mViewPortHandler.contentLeft() - myMarkerViewLeft.getWidth(), pos[1] - myMarkerViewLeft.getHeight() / 2);
                myMarkerViewRight.draw(canvas, mViewPortHandler.contentRight(), pos[1] - myMarkerViewRight.getHeight() / 2);
            }
        }

    }

然後在activity中

 private void setMarkerView(MinuteHelper mData){
        MyLeftMarkerView leftMarkerView = new MyLeftMarkerView(MinutesActivity.this, R.layout.mymarkerview);
        MyRightMarkerView rightMarkerView = new MyRightMarkerView(MinutesActivity.this, R.layout.mymarkerview);
        lineChart.setMarker(leftMarkerView, rightMarkerView, mData);
    }

同時圖表的選中也得進行修改傳入引數

  lineChart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
            @Override
            public void onValueSelected(Entry e, int dataSetIndex, Highlight h) {
                barChart.highlightValues(new Highlight[]{h});
                lineChart.setHighlightValue(e,h);
            }
            @Override
            public void onNothingSelected() {

            }
        });
        barChart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
            @Override
            public void onValueSelected(Entry e, int dataSetIndex, Highlight h) {
              //  barChart.highlightValues(new Highlight[]{h});
                lineChart.setHighlightValue(e, new Highlight(h.getXIndex(),0));//此函式已經返回highlightBValues的變數,並且重新整理,故上面方法可以註釋
               // barChart.setHighlightValue(h);
            }

            @Override
            public void onNothingSelected() {

            }
        });

看得比較亂的同學,可以直接檢視demo程式碼,這裡只是簡單的放出程式碼而已,邏輯上估計還有點亂。博主不得不承認,在處理自定義標記的部分程式碼寫的有點渣,中間碰到很多坑,最後也是碰巧實現了效果,博主很納悶,為啥要從activity中傳入Entry的引數才行,而在MyLineChart中直接引用系統的Entry和mData卻行不通,希望哪位大神研究後能給出答案,或者在處理自定義標記這邊有更好見解的歡迎留言或者聯絡我。下面我們看下效果圖吧(考慮到是仿照自選股app,所以顏色上也基本參考了它):
(注:以上bug,6.17日已修復,上面程式碼已替換最新)

這裡寫圖片描述

總結:分時圖寫到這裡基本告一段落了,大概的功能也都能實現了,如果有什麼問題,請留言,博主一定盡力解決,同時發現demo有bug啥的或者更好的處理方式,也歡迎聯絡我。端午後將會更新大家期待已久的K線圖,歡迎大家star我的專案https://github.com/AndroidJiang/StockChart,最後祝大家端午快樂!。