1. 程式人生 > >k線相關-深度圖篇

k線相關-深度圖篇

先上圖:

這裡需要理解下,深度圖左右兩邊,綠色代表買部分,紅色代表賣部分,買部分從中間到最左邊,price依次遞減,賣的價格從中間到最右邊價格依次遞減,縱座標這裡給的是累計的交易量;

理解了要繪製的深度圖,我們就可以開始繪製了。

style部分主要主要涉及買賣部分的line ,背景,字型大小,字型顏色等,以及深度圖單點以及長按等屬性;

直接上程式碼:

 <declare-styleable name="DepthView">

        <!--買-->
        <attr name="dvBuyLineColor" format="color"/>
        <attr name="dvBuyBGColor" format="color"/>
        <attr name="dvBuyLineStrokeWidth" format="integer"/>

        <!--賣 -->
        <attr name="dvSellLineColor" format="color"/>
        <attr name="dvSellBGColor" format="color"/>
        <attr name="dvSellLineStrokeWidth" format="integer"/>

        <!--橫座標 -->
        <attr name="dvAbscissaColor" format="color"/>
        <attr name="dvAbscissaTextSize" format="integer"/>

        <!--縱座標 -->
        <attr name="dvOrdinateColor" format="color"/>
        <attr name="dvOrdinateTextSize" format="integer"/>
        <attr name="dvOrdinateNum" format="integer"/>


        <attr name="dvInfoBgColor" format="color"/>
        <attr name="dvInfoTextSize" format="integer"/>
        <attr name="dvInfoTextColor" format="color"/>
        <attr name="dvInfoLineCol" format="color"/>
        <!--遊標線寬度、交界圓點半徑、價格文字說明、數量文字說明-->
        <attr name="dvInfoLineWidth" format="float" />
        <attr name="dvInfoPointRadius" format="integer"/>
        <attr name="dvInfoPriceTitle" format="string"/>
        <attr name="dvInfoVolumeTitle" format="string"/>



    </declare-styleable>

資料來源部分 :

bean十分簡單,主要就price和volume兩個值,另外為了方便繪製,新增x,y兩個座標值;

public class DepthBean implements Comparable<DepthBean> {

    private double price;//委託價
    private double volume;//委託量
    private int tradeType;
    private String coinName;
    private float xValue;
    private float yValue;


    public DepthBean(double price, double volume, int tradeType, String coinName) {
        this.price = price;
        this.volume = volume;
        this.tradeType = tradeType;
        this.coinName = coinName;
    }



    public String getCoinName() {
        return coinName;
    }

    public void setCoinName(String coinName) {
        this.coinName = coinName;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public double getVolume() {
        return volume;
    }

    public void setVolume(double volume) {
        this.volume = volume;
    }

    public int getTradeType() {
        return tradeType;
    }

    public void setTradeType(int tradeType) {
        this.tradeType = tradeType;
    }

    public float getxValue() {
        return xValue;
    }

    public void setxValue(float xValue) {
        this.xValue = xValue;
    }

    public float getyValue() {
        return yValue;
    }

    public void setyValue(float yValue) {
        this.yValue = yValue;
    }

    @Override
    public int compareTo(@NonNull DepthBean o) {
        double diff=this.price-o.price;
        if (diff>0){

            return 1;
        }else if (diff<0){

            return -1;
        }else{

            return 0;
        }
    }

    @Override
    public String toString() {
        return "DepthBean{price="+price+",volume="+volume+",coinName="+coinName+",tradeType="+tradeType+"}";
    }
}

 

onlayout

 @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        leftStart=getPaddingLeft()+10;
        rightEnd=getMeasuredWidth()-getPaddingRight()-1;
        topStart=getPaddingTop()+20;
        bottomEnd=getMeasuredHeight() - getPaddingBottom() - 1;


        double maxBuyVolume;
        double minBuyVolume;
        double maxSellVolume;
        double minSellVolume;

        if (!buyList.isEmpty()){
            maxBuyVolume=buyList.get(0).getVolume();
            minBuyVolume=buyList.get(buyList.size()-1).getVolume();
        }else{
            maxBuyVolume=minBuyVolume=0;
        }

        if (!sellList.isEmpty()){
            maxSellVolume=sellList.get(sellList.size()-1).getVolume();
            minSellVolume=sellList.get(0).getVolume();

        }else{
            maxSellVolume=minSellVolume=0;
        }

        //獲得最大最小的交易量的值
        maxVolume=Math.max(maxBuyVolume,maxSellVolume);
        minVolume=Math.min(minBuyVolume,minSellVolume);

        //buylist 不空,取買最低價 ,否則取賣最高價
        if (!buyList.isEmpty()){
            leftPriceStr=setPrecision(buyList.get(0).getPrice(),priceScale);
        }else if (!sellList.isEmpty()){
            leftPriceStr=setPrecision(sellList.get(0).getPrice(),priceScale);
        }else{

        }


        //selllist 不空 取賣最低價, 否則取買最高價
        if (!sellList.isEmpty()){
            rightPriceStr=setPrecision(sellList.get(sellList.size()-1).getPrice(),priceScale);
        }else if (!buyList.isEmpty()){
            rightPriceStr=setPrecision(buyList.get(buyList.size()-1).getPrice(),priceScale);
        }

        strokePaint.getTextBounds(leftPriceStr,0,leftPriceStr.length(),textRect);
        //深度圖除文字外的高度,
        depthImgHeight = bottomEnd - topStart - textRect.height() - 18;



        avgHeightPerVolume= depthImgHeight / (maxVolume - minVolume);
        //x軸每一刻度的值
        avgWidthPerSize= (rightEnd - leftStart) / (buyList.size() + sellList.size());
        avgVolumeSpace = maxVolume / ordinateNum;
        avgOrdinateSpace = depthImgHeight / ordinateNum;


        //計算x,y座標
        for (int i=0;i<buyList.size();i++){
            buyList.get(i).setxValue(leftStart+(float) avgWidthPerSize*i);
            buyList.get(i).setyValue(topStart + (float) ((maxVolume - buyList.get(i).getVolume()) * avgHeightPerVolume));
        }


        for (int i=0;i<sellList.size();i++){
            sellList.get(i).setxValue(leftStart+(float) avgWidthPerSize*(i+buyList.size()));
            sellList.get(i).setyValue(topStart + (float) ((maxVolume - sellList.get(i).getVolume()) * avgHeightPerVolume));
        }

    }

座標軸+橫縱座標

onlayout中已經計算了所需要的一些資料,這裡直接用

 //座標軸,橫縱座標的值
    public void drawCoordinate(Canvas canvas){

        //橫座標
        strokePaint.setStrokeWidth(1);
        strokePaint.setColor(abscissaColor);
        strokePaint.setTextSize(20);

        strokePaint.getTextBounds(rightPriceStr,0,rightPriceStr.length(),textRect);
        canvas.drawText(leftPriceStr,leftStart+2,bottomEnd-5,strokePaint);
        canvas.drawText(rightPriceStr,rightEnd-textRect.width(),bottomEnd-5,strokePaint);


        double centerPrice=0;
        if (!buyList.isEmpty()&&!buyList.isEmpty()){
            centerPrice= (buyList.get(buyList.size()-1).getPrice()+sellList.get(0).getPrice())/2;
        }else if (!buyList.isEmpty()){
            centerPrice=buyList.get(buyList.size()-1).getPrice();
        }else if (!sellList.isEmpty()){
            centerPrice=sellList.get(buyList.size()-1).getPrice();
        }

        canvas.drawText(setPrecision(centerPrice,priceScale),getMeasuredWidth()/2-30,bottomEnd-5,strokePaint);


        //縱座標
        strokePaint.setStrokeWidth(0);
        strokePaint.setColor(ordinateColor);
        strokePaint.setTextSize(20);

        strokePaint.getTextBounds(maxVolume+"",0,(maxVolume+"").length(),textRect);

        for (int i=0;i<ordinateNum;i++){
            String text=setPrecision(maxVolume-avgVolumeSpace*(i),volumeScale);
            canvas.drawText(text,leftStart+2,(float) (topStart+textRect.height()+i*avgOrdinateSpace),strokePaint);
        }
    }

 繪製買賣線以及背景

買部分,根據buyList獲取每個點x,y軸資料,y軸相同,

   //繪製邊線以及背景
    private void drawLineAndBg(Canvas canvas){
        //左側買
        if (buyList!=null){
            linePath.reset();
            bgPath.reset();
            for (int i=0;i<buyList.size();i++){
                if (i == 0) {
                    linePath.moveTo(buyList.get(i).getxValue(),buyList.get(i).getyValue());
                    bgPath.moveTo(buyList.get(i).getxValue(),buyList.get(i).getyValue());
                }else{
                    linePath.lineTo(buyList.get(i).getxValue(),buyList.get(i).getyValue());
                    bgPath.lineTo(buyList.get(i).getxValue(),buyList.get(i).getyValue());
                }
            }

            if (!buyList.isEmpty() && topStart + (float) ((maxVolume - buyList.get(buyList.size()-1).getVolume()) * avgHeightPerVolume) < topStart + depthImgHeight) {
                bgPath.lineTo(leftStart + (float) avgWidthPerSize * (buyList.size()-1), (float) (topStart + depthImgHeight));
            }
            bgPath.lineTo(leftStart, (float) (topStart + depthImgHeight));
            bgPath.close();

            //整個買部分的範圍
            fillPaint.setColor(buyBgColor);
            canvas.drawPath(bgPath, fillPaint);


            //邊線
            strokePaint.setColor(buyLineColor);
            strokePaint.setTextSize(20);
            strokePaint.setStrokeWidth(4);
            canvas.drawPath(linePath,strokePaint);


        }

        //右側賣

        if (sellList!=null){
            linePath.reset();
            bgPath.reset();

            for (int i=0;i<sellList.size();i++){
                if (i == 0) {
                    linePath.moveTo(sellList.get(i).getxValue(),sellList.get(i).getyValue());
                    bgPath.moveTo(sellList.get(i).getxValue(),sellList.get(i).getyValue());
                }else{
                    linePath.lineTo(sellList.get(i).getxValue(),sellList.get(i).getyValue());
                    bgPath.lineTo(sellList.get(i).getxValue(),sellList.get(i).getyValue());
                }
            }

            bgPath.lineTo(sellList.get(sellList.size()-1).getxValue(), (float) (topStart + depthImgHeight));
            if (!sellList.isEmpty() && topStart + (float) ((maxVolume - sellList.get(0).getVolume()) * avgHeightPerVolume)< (float) (topStart + depthImgHeight)) {
                bgPath.lineTo(sellList.get(0).getxValue(), (float) (topStart + depthImgHeight));
            }
            bgPath.close();
            fillPaint.setColor(sellBgColor);
            canvas.drawPath(bgPath, fillPaint);



            strokePaint.setColor(sellLineColor);
            strokePaint.setTextSize(20);
            strokePaint.setStrokeWidth(4);
            canvas.drawPath(linePath,strokePaint);
        }

    }

詳情彈框

詳情繪製比較簡單,主要有兩部分判斷,第一部分,根據使用者點選的位置,獲取對應的位置的bean,獲得對應的price,x,y等資料;

   private float clickDownX;
    private float clickDownY;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                clickDownX=event.getX();
                clickDownY=event.getY();

                break;

            case MotionEvent.ACTION_UP:
                float diffX=Math.abs(event.getX()-clickDownX);
                float diffY=Math.abs(event.getY()-clickDownY);

                if (diffX<moveLimitDistance&&diffY<moveLimitDistance){
                    isShowinfos=true;
                    getClickBean(clickDownX);
                    if (clickBean!=null){
                        invalidate();
                    }
                }
                break;
        }
        return true;
    }

  private DepthBean clickBean;

    //獲取點選位置的bean
    public void  getClickBean(float xValue){
        clickBean=null;

        if (sellList.isEmpty()){
            for (int i=0;i<buyList.size();i++){
                if (i+1<buyList.size()&&xValue>=(leftStart + (float) avgWidthPerSize * i)&&xValue<(leftStart + (float) avgWidthPerSize * (i+1))){
                    clickBean=buyList.get(i);
                    break;
                }else if (i==buyList.size()-1&&xValue>= (leftStart + (float) avgWidthPerSize * i)){
                    clickBean=buyList.get(i);
                    break;
                }
            }
        }else if (xValue<(leftStart + (float) avgWidthPerSize * buyList.size())){
            for (int i=0;i<buyList.size();i++){
                if (i+1<buyList.size()&&xValue>=(leftStart + (float) avgWidthPerSize * i)&&xValue<(leftStart + (float) avgWidthPerSize * (i+1))){
                    clickBean=buyList.get(i);
                    break;
                }else if (i==buyList.size()-1&&xValue>= (leftStart + (float) avgWidthPerSize * i)&&xValue<(leftStart + (float) avgWidthPerSize * (i+1))){
                    clickBean=buyList.get(i);
                    break;
                }
            }
        }else{
            for (int i=0;i<sellList.size();i++){
                if (i+1<sellList.size()&&xValue>=sellList.get(i).getxValue()&&xValue<sellList.get(i+1).getxValue()){
                    clickBean=sellList.get(i);
                    break;
                }else if (i==sellList.size()-1&&xValue>= sellList.get(i).getxValue()){
                    clickBean=sellList.get(i);
                    break;
                }
            }

        }
    }

繪製詳情彈框部分,主要繪製基準線,背景框,以及對應的price以及volume等資料;

基準線通過上面onTouchEvent獲取的clickBean,從而獲取x軸座標即可繪製。

背景框根據繪製文案的最大寬度+padding與clickbean的x比較確定在基準左右。

  //繪製詳情資訊
    public void drawDetailsInfo(Canvas canvas){
        if (isShowinfos&&clickBean!=null){
            //準線
            strokePaint.setStrokeWidth(1);
            strokePaint.setTextSize(30);
            strokePaint.setColor(sellLineColor);

            canvas.drawLine(clickDownX,topStart,clickDownX+2,bottomEnd,strokePaint);


            String priceStr="價格:      "+setPrecision(clickBean.getPrice(),priceScale);
            String volume  ="累計交易量:"+setPrecision(clickBean.getVolume(),volumeScale);
            strokePaint.setStrokeWidth(1);
            strokePaint.setTextSize(30);
            strokePaint.setColor(infoTextCol);

            strokePaint.getTextBounds(priceStr,0,priceStr.length(),textRect);
            float priceStrWidth=textRect.width();
            float priceStrHeight=textRect.height();

            strokePaint.getTextBounds(volume,0,volume.length(),textRect);
            float volumeStrWidth=textRect.width();
            float volumeStrHeight=textRect.height();

            float maxWidth=Math.max(priceStrWidth,volumeStrWidth);
            float maxHeight=Math.max(priceStrHeight,volumeStrHeight);

            float bgLeft,bgRight,bgBottom,bgTop;

            //根據x座標判斷,繪製的線上的左邊還是右邊
            if (clickBean.getxValue()<maxWidth+topStart+60){
                bgLeft=clickBean.getxValue();
                bgRight=clickBean.getxValue()+maxWidth+60;
            }else{
                bgLeft=clickBean.getxValue()-maxWidth-60;
                bgRight=clickBean.getxValue();
            }

            //根據y座標判斷
            if (topStart+depthImgHeight-clickBean.getyValue()<maxHeight+60){
                bgTop=clickBean.getyValue()-maxHeight-60;
                bgBottom=clickBean.getyValue();

            }else{
                bgBottom=clickBean.getyValue()+maxHeight+60;
                bgTop=clickBean.getyValue();
            }

            fillPaint.setColor(infoBgCol);
            canvas.drawRect(bgLeft,bgTop,bgRight,bgBottom,fillPaint);
            canvas.drawText(priceStr,bgLeft+20,bgTop+40,strokePaint);
            canvas.drawText(volume,bgLeft+20,bgTop+45+priceStrHeight,strokePaint);
        }
    }

 

github地址