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地址

猜你喜欢

转载自blog.csdn.net/qq_23025319/article/details/84401002