Android/IOS-使用贝赛尔曲线画曲线图与填充图

一、按照惯例先放上效果图
在这里插入图片描述

二、从这个效果图来看需要画下列几部分

  • 座标轴: 画座标轴,这个相信比较简单,画2条直线即可,但是如果座标轴的刻度随着输入的数据而变化的话,估计需要花点时间了。这样封装的目的就是为了在固定的区域可以展示不同的数据范围。如上图,横轴表示的是1-31之间的数,竖轴表示的是0-6000之间的数。但是来了一组横轴数据范围为10-100每个刻度为10,竖轴范围为0-1200每个刻度为 200的数组该如何表示呢?
  • 座标轴计算的方式为,
  • 轴刻度: max(轴数据列表中)/(轴数据列表长度),
  • 轴范围为:(0,max(轴数据列表中))

座标轴及刻度确定好了之后就需要画曲线了,这里采用的是二阶贝赛尔曲线(关于贝赛尔曲线的知识就需要自己去查了)

  • 画曲线原理,先获取到这些点的座标,然后依次将座标用贝赛尔连接起来即可。确定竖轴座标的计算方式为,

1.先计算每个点的座标

 private Point[] getPoints() {

        Point[] points = new Point[yRawData.size()];		//yRawData为y轴数据列表
        for (int i = 0; i < yRawData.size(); i++) {

            int ph = bheight - (int) (bheight * (yRawData.get(i) / maxValue));//maxValue为y轴数据列表最大值

            points[i] = new Point(xList.get(i), ph + marginTop);
        }
        return points;
    }

2.将各座标用贝赛尔连接起来

Point startp = new Point();
        Point endp = new Point();
        Paint linePaint = new Paint();
        linePaint.setStyle(Paint.Style.FILL);
        linePaint.setStrokeWidth((float) 2.3);
        linePaint.setColor(Color.parseColor("#def0fe"));//设置曲线的颜色的
        linePaint.setAlpha(125);
        linePaint.setPathEffect(null);

 for (int i = 0; i < mPoints.length - 1; i++) {
            startp = mPoints[i];
            endp = mPoints[i + 1];
            int wt = (startp.x + endp.x) / 2;
            Point p3 = new Point();
            Point p4 = new Point();
            p3.y = startp.y;
            p3.x = wt;
            p4.y = endp.y;
            p4.x = wt;

            Path path = new Path();
            path.moveTo(startp.x, startp.y);
            path.cubicTo(p3.x, p3.y, p4.x, p4.y, endp.x, endp.y);
            canvas.drawPath(path, mPaint);
        }

三、画填充曲线(注意填充曲线为一个封闭的路径,下面的注释很重要,需要仔细研读)

 Path path2 = new Path();
        path2.moveTo(mPoints[0].x, mPoints[0].y);
        Point lastPoint = new Point();
        lastPoint.x = mPoints[mPoints.length - 1].x;
        lastPoint.y = mPoints[mPoints.length - 1].y;
for (int i = 0; i < mPoints.length; i++) {
            if (i != 0) {

                Point prePoint = new Point();
                Point nowPoint = new Point();

                prePoint.x = mPoints[i - 1].x;
                prePoint.y = mPoints[i - 1].y;

                nowPoint.x = mPoints[i].x;
                nowPoint.y = mPoints[i].y;

                path2.cubicTo((nowPoint.x + prePoint.x) / 2, prePoint.y,
                        (nowPoint.x + prePoint.x) / 2, nowPoint.y,
                        nowPoint.x, nowPoint.y);

            }

        }

        //下面3个稳点非常重要,目的是为了形成一个封闭的路径
        path2.lineTo(lastPoint.x, bheight + marginTop);          //移到最后一个点
        path2.lineTo(mPoints[0].x, bheight + marginTop);        //再移到与最后一个点同x座标的 但y座标为0处
        path2.lineTo(mPoints[0].x, mPoints[0].y);                  //再回到原点
        canvas.drawPath(path2, linePaint);

四.源码为:

package com.imobpay.viewlibrary.view;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.imobpay.viewlibrary.R;

import java.util.ArrayList;

/**
 * Created by liuxiaobing
 * Date on 2018/11/7
 * Copyright 2013 - 2018 QianTuo Inc. All Rights Reserved
 * Desc: 直线或者曲线报表视图
 */

public class QTXXCurvReportView extends View {


    private  int CIRCLE_SIZE = 10;

    private enum Linestyle {
        Line, Curve
    }

    private Context mContext;
    private Paint mPaint;
    private Paint indexLinePaint;
    private Paint midCirclePaint;
    private Resources res;
    //private DisplayMetrics dm;

    /**
     * data
     */
    private Linestyle mStyle = Linestyle.Curve;

    private int canvasHeight;
    private int canvasWidth;
    private int bheight = 0;
    private int blwidh;
    private boolean isMeasure = true;
    /**
     * Y轴最大值
     */
    private int maxValue;
    /**
     * Y轴间距值
     */
    private int averageValue;
    private int marginTop = 20;
    private int marginBottom = 40;

    /**
     * 曲线上总点数
     */
    private Point[] mPoints;
    /**
     * 纵坐标值
     */
    private ArrayList<Double> yRawData;
    /**
     * 横坐标值
     */
    private ArrayList<String> xRawDatas;
    private ArrayList<Integer> xList = new ArrayList<Integer>();// 记录每个x的值
    private int spacingHeight;
    private boolean startDrawIndexLine = false;
    private float curTouchPosX = 50.0f;
    private float offsetY = 0.0f;
    private float xyAxisFontSize = 0.0f;
    private float indexTrigleEdgeLenght = 0.0f;
    private Path indexRectPath = null;
    private String daySumTips = null;
    private int curValueOfXData = -1;
    private float topIndexLineMarginTop = 0;
    private boolean startGlobalRender = false;



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

    public QTXXCurvReportView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public QTXXCurvReportView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    private void initView(Context context) {
        this.mContext = context;
        this.res = mContext.getResources();
        this.mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(1);
        marginBottom = dip2px(20);
        marginTop = dip2px(140);
        offsetY = dip2px(140);
        xyAxisFontSize = sp2px(10);
        CIRCLE_SIZE = dip2px(5);
        indexTrigleEdgeLenght = dip2px(8);
        indexRectPath = new Path();
        daySumTips = res.getString(R.string.daySumTips);
        topIndexLineMarginTop = indexTrigleEdgeLenght * 5;
        mPaint.setTextSize(xyAxisFontSize);

        indexLinePaint = new Paint();
        indexLinePaint.setStyle(Paint.Style.FILL);
        indexLinePaint.setStrokeWidth(dip2px(1));
        indexLinePaint.setColor(res.getColor(R.color.color_E9A79B));

        midCirclePaint = new Paint();
        midCirclePaint.setStyle(Paint.Style.FILL);
        midCirclePaint.setStrokeWidth(dip2px(2));
        midCirclePaint.setColor(res.getColor(R.color.color_E9A79B));    //初始为大圆圈的颜色
    }

//    @Override
//    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//        this.canvasHeight = getHeight();
//        this.canvasWidth = getWidth();
//        if (bheight == 0)
//            bheight = (int) (canvasHeight - marginBottom - dip2px(140));
//        blwidh = dip2px(30);
//    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        if (isMeasure) {
            this.canvasHeight = getHeight();
            this.canvasWidth = getWidth();
            if (bheight == 0)
                bheight = (int) (canvasHeight - marginBottom - dip2px(140));
            blwidh = dip2px(30);
            isMeasure = false;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if(!startGlobalRender) return;
//        mPaint.setColor(res.getColor(R.color.color_9F9F9F));
        mPaint.setStrokeWidth(1);
        drawAllXLine(canvas);
        drawAllYLine(canvas);
        mPoints = getPoints();

        mPaint.setColor(res.getColor(R.color.color_1f96f2));
        mPaint.setStrokeWidth(dip2px(1.5f));
        mPaint.setStyle(Paint.Style.STROKE);
        if (mStyle == Linestyle.Curve) {
            drawScrollLine(canvas);
        } else {
            drawLine(canvas);
        }

        //画点
        mPaint.setStyle(Paint.Style.FILL);
        for (int i = 0; i < mPoints.length; i++) {
            canvas.drawCircle(mPoints[i].x, mPoints[i].y, CIRCLE_SIZE / 4 , mPaint);
        }
        drawTopLine(canvas);
        drawIndexLine(canvas,curTouchPosX);
        drawMidCircle(canvas,curTouchPosX);

    }

    /**
     * 画所有横向表格,包括X轴
     */
    private void drawAllXLine(Canvas canvas) {
        mPaint.setColor(res.getColor(R.color.color_9F9F9F));
        for (int i = 0; i < spacingHeight + 1; i++) {
            canvas.drawLine(blwidh,
                    bheight - (bheight / spacingHeight) * i + marginTop,
                    (canvasWidth - blwidh) + (canvasWidth - blwidh) / xRawDatas.size(),
                    bheight - (bheight / spacingHeight) * i + marginTop, mPaint);// Y坐标
            canvas.drawText(String.valueOf(averageValue * i),
                    blwidh / 2,
                    bheight - (bheight / spacingHeight) * i + marginTop,mPaint);
        }
    }

    /**
     * 画所有纵向表格,包括Y轴
     */
    private void drawAllYLine(Canvas canvas) {
        for (int i = 0; i < xRawDatas.size(); i++) {
            xList.add(blwidh + (canvasWidth - blwidh) / xRawDatas.size() * i);  //曲线数据对应的x 轴刻度

            canvas.drawLine(blwidh + (canvasWidth - blwidh) / xRawDatas.size() * i,
                    marginTop,
                    blwidh + (canvasWidth - blwidh) / xRawDatas.size() * i,
                    bheight + marginTop, mPaint);
            if (i % 5 == 0) {

                mPaint.setColor(res.getColor(R.color.color_949494));
                canvas.drawText(xRawDatas.get(i),
                        blwidh + (canvasWidth - blwidh) / xRawDatas.size() * i,
                        bheight + dip2px(15) + (int)offsetY,mPaint);
            }


        }
    }

    private void drawScrollLine(Canvas canvas) {
        Point startp = new Point();
        Point endp = new Point();
        Paint linePaint = new Paint();
        linePaint.setStyle(Paint.Style.FILL);
        linePaint.setStrokeWidth((float) 2.3);
        linePaint.setColor(Color.parseColor("#def0fe"));//设置曲线的颜色的
        linePaint.setAlpha(125);
        linePaint.setPathEffect(null);

        Path path2 = new Path();
        path2.moveTo(mPoints[0].x, mPoints[0].y);
        Point lastPoint = new Point();
        lastPoint.x = mPoints[mPoints.length - 1].x;
        lastPoint.y = mPoints[mPoints.length - 1].y;

        for (int i = 0; i < mPoints.length - 1; i++) {
            startp = mPoints[i];
            endp = mPoints[i + 1];
            int wt = (startp.x + endp.x) / 2;
            Point p3 = new Point();
            Point p4 = new Point();
            p3.y = startp.y;
            p3.x = wt;
            p4.y = endp.y;
            p4.x = wt;

            Path path = new Path();
            path.moveTo(startp.x, startp.y);
            path.cubicTo(p3.x, p3.y, p4.x, p4.y, endp.x, endp.y);
            canvas.drawPath(path, mPaint);
        }

        for (int i = 0; i < mPoints.length; i++) {
            if (i != 0) {

                Point prePoint = new Point();
                Point nowPoint = new Point();

                prePoint.x = mPoints[i - 1].x;
                prePoint.y = mPoints[i - 1].y;

                nowPoint.x = mPoints[i].x;
                nowPoint.y = mPoints[i].y;

                path2.cubicTo((nowPoint.x + prePoint.x) / 2, prePoint.y,
                        (nowPoint.x + prePoint.x) / 2, nowPoint.y,
                        nowPoint.x, nowPoint.y);

            }

        }

        //下面3个稳点非常重要,目的是为了形成一个封闭的路径
        path2.lineTo(lastPoint.x, bheight + marginTop);          //移到最后一个点
        path2.lineTo(mPoints[0].x, bheight + marginTop);        //再移到与最后一个点同x座标的 但y座标为0处
        path2.lineTo(mPoints[0].x, mPoints[0].y);                  //再回到原点
        canvas.drawPath(path2, linePaint);
    }

    private void drawLine(Canvas canvas) {
        Point startp = new Point();
        Point endp = new Point();
        for (int i = 0; i < mPoints.length - 1; i++) {
            startp = mPoints[i];
            endp = mPoints[i + 1];
            canvas.drawLine(startp.x, startp.y, endp.x, endp.y, mPaint);
        }
    }



    private void drawText(String text, int x, int y, Canvas canvas) {
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
        p.setTextSize(dip2px(12));
        p.setColor(Color.BLUE);
        p.setTextAlign(Paint.Align.LEFT);
        canvas.drawText(text, x, y, p);
    }

    private Point[] getPoints() {

        Point[] points = new Point[yRawData.size()];
        for (int i = 0; i < yRawData.size(); i++) {

            int ph = bheight - (int) (bheight * (yRawData.get(i) / maxValue));

            points[i] = new Point(xList.get(i), ph + marginTop);
        }
        return points;
    }

    public void setData(ArrayList<Double> yRawData, ArrayList<String> xRawData, int maxValue, int averageValue) {
        this.maxValue = maxValue;
        this.averageValue = averageValue;
        this.mPoints = new Point[yRawData.size()];
        this.xRawDatas = xRawData;
        this.yRawData = yRawData;
        this.spacingHeight = maxValue / averageValue;
        startGlobalRender = true;
    }

    public void setTotalvalue(int maxValue) {
        this.maxValue = maxValue;
    }

    public void setPjvalue(int averageValue) {
        this.averageValue = averageValue;
    }

    public void setMargint(int marginTop) {
        this.marginTop = marginTop;
    }

    public void setMarginb(int marginBottom) {
        this.marginBottom = marginBottom;
    }

    public void setMstyle(Linestyle mStyle) {
        this.mStyle = mStyle;
    }

    public void setBheight(int bheight) {
        this.bheight = bheight;
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    private int dip2px(float dpValue) {
        float scale = this.mContext.getResources().getDisplayMetrics().density;
        return (int)(dpValue*scale+0.5f);
    }
    /** sp转换px */
    public int sp2px(int spValue) {
        float fontScale = this.mContext.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {

        curTouchPosX = event.getX();
        switch (event.getAction()){

            case MotionEvent.ACTION_DOWN:
                startDrawIndexLine = true;
                break;

            case MotionEvent.ACTION_MOVE:
                startDrawIndexLine = true;
                break;

            case MotionEvent.ACTION_UP:
                startDrawIndexLine = false;
                break;
        }
        postInvalidate();
        return true;
    }

    private void drawIndexLine(Canvas canvas,float x){
        if(x < (blwidh - 5) || x > (canvasWidth - blwidh + (canvasWidth - blwidh) / xRawDatas.size() + 2)) return;
        if(startDrawIndexLine){

            canvas.drawLine(x, 0 + indexTrigleEdgeLenght - indexLinePaint.getStrokeWidth() + topIndexLineMarginTop,
                    x, bheight + marginTop ,
                    indexLinePaint);
            float tt = getValueByPosX(x);
            //System.out.println("330-----------:"+tt);

            //画指示3角形
            indexLinePaint.setColor(Color.WHITE);
            indexLinePaint.setStrokeWidth(dip2px(1) * 1.5f);
            canvas.drawLine(x + indexTrigleEdgeLenght,
                    indexLinePaint.getStrokeWidth()  + topIndexLineMarginTop,
                    x - indexTrigleEdgeLenght,
                    indexLinePaint.getStrokeWidth()  + topIndexLineMarginTop, indexLinePaint);

            indexLinePaint.setStrokeWidth(dip2px(1));
            indexLinePaint.setColor(res.getColor(R.color.color_E9A79B));
            canvas.drawLine(x - indexTrigleEdgeLenght,
                    indexLinePaint.getStrokeWidth() + topIndexLineMarginTop,
                    x,
                    indexLinePaint.getStrokeWidth() +  indexTrigleEdgeLenght *  0.866f + topIndexLineMarginTop
                    , indexLinePaint);

            canvas.drawLine(x,
                    indexLinePaint.getStrokeWidth() +  indexTrigleEdgeLenght * 0.866f + topIndexLineMarginTop,
                    x + indexTrigleEdgeLenght,
                    indexLinePaint.getStrokeWidth()+ topIndexLineMarginTop,
                    indexLinePaint);

            drawDetailMaker(canvas,x,tt);

        }
    }

    private void drawDetailMaker(Canvas canvas,float x,float value){

        if(value <= -1) return;
        indexLinePaint.setColor(res.getColor(R.color.color_F26341));
        canvas.drawRect(x - 6 * indexTrigleEdgeLenght ,
                indexTrigleEdgeLenght * 2 + topIndexLineMarginTop,
                x + 6 * indexTrigleEdgeLenght,
                indexTrigleEdgeLenght * 10 + topIndexLineMarginTop,indexLinePaint);

        indexLinePaint.setColor(Color.WHITE);
        float baseline = (indexTrigleEdgeLenght * 13 + topIndexLineMarginTop - (indexLinePaint.descent() - indexLinePaint.ascent())) / 2 - indexLinePaint.ascent();
        String tmp = null;
        if((curValueOfXData + 1) < 10){
            tmp = "0"+ (curValueOfXData + 1);
        }

        String date = String.format("%s 年%s 月%s 日","2018","11",tmp);
        String valueStr = String.format("%f",value);

        int textWidth = (int) indexLinePaint.measureText(date);
        int textWidth2 = (int) indexLinePaint.measureText(daySumTips);
        int textWidth3 = (int) indexLinePaint.measureText(valueStr);

        indexLinePaint.setTextSize(xyAxisFontSize);
        canvas.drawText(date,x - textWidth / 2,baseline,indexLinePaint);
        canvas.drawText(daySumTips,x - textWidth2 / 2,baseline * 1.2f,indexLinePaint);
        canvas.drawText(valueStr,x - textWidth3 / 2,baseline * 1.4f,indexLinePaint);
    }

    private void drawTopLine(Canvas canvas){
        indexLinePaint.setColor(res.getColor(R.color.color_E9A79B));
        indexLinePaint.setStrokeWidth(dip2px(1));
        canvas.drawLine(blwidh,
                indexLinePaint.getStrokeWidth() + topIndexLineMarginTop,
                (canvasWidth - blwidh) + (canvasWidth - blwidh) / xRawDatas.size() ,
                indexLinePaint.getStrokeWidth() + + topIndexLineMarginTop, indexLinePaint);
    }

    private void drawMidCircle(Canvas canvas,float x){
        float value = getValueByPosX(x);
        if(startDrawIndexLine && value != -1){
            for (int i = 0; i < mPoints.length; i++) {
                if(Math.abs(x - mPoints[i].x) <= 4){
                    midCirclePaint.setColor(res.getColor(R.color.color_E9A79B));
                    canvas.drawCircle(mPoints[i].x, mPoints[i].y, CIRCLE_SIZE * 1.5f , midCirclePaint);
                    midCirclePaint.setColor(Color.WHITE);
                    canvas.drawCircle(mPoints[i].x, mPoints[i].y, CIRCLE_SIZE , midCirclePaint);
                    midCirclePaint.setColor(res.getColor(R.color.color_F26341));
                    canvas.drawCircle(mPoints[i].x, mPoints[i].y, CIRCLE_SIZE / 1.5f , midCirclePaint);
                    break;
                }

            }
        }
    }



    private float getValueByPosX(float x){

        float dayUnit = (canvasWidth - 2 * blwidh) / (xRawDatas.size());
        curValueOfXData = (int) ((x - blwidh)  / dayUnit);
        if(curValueOfXData < yRawData.size() && curValueOfXData >= 0){

            return yRawData.get(curValueOfXData).floatValue();
        }
        return -1;

    }


}

使用方式:

package com.qtpay.imobpay.xiaojine.activity.income;

import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.NumberPicker;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.imobpay.baseibrary.presenter.AccountPresenter;
import com.imobpay.networklibrary.model.MarketBusinessReport;
import com.imobpay.viewlibrary.view.QTXXCurvReportView;
import com.qtpay.imobpay.xiaojine.R;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * Created by liuxiaobing
 * Date on 2018/11/7
 * Copyright 2013 - 2018 QianTuo Inc. All Rights Reserved
 * Desc: 月度收益报表
 */

public class MonthlyReportActivity extends AccountPresenter {

    private RelativeLayout mSelectDateLayout;
    private QTXXCurvReportView mReportView;
    private TextView txtDate;
    ArrayList<Double> yList;
    ArrayList<String> xRawDatas;
    private final String[] months = {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"};
    private String[] years;
    private View pickDateView;
    private PopupWindow pickDateWindow;
    private NumberPicker yearPicker;
    private NumberPicker monthPicker;
    private int curYear;
    private int curMonth;
    private MainHandler mHandler;

    @Override
    public void init() {
        config("activity_monthly_report", getString(R.string.managment_report));
        setTitleViewBackground("color_FEDF33");
        setTitleName(getString(R.string.managment_report), "black");
    }

    @Override
    public void initView() {
        initXAxisData();
        getCurYearMonth();
        mHandler = new MainHandler();
        mSelectDateLayout = (RelativeLayout) findViewById(R.id.layout_pickDate);
        mReportView = (QTXXCurvReportView) findViewById(R.id.view_monthlyreport);
        txtDate = (TextView) findViewById(R.id.txt_pickDate);
        String tmp = curMonth < 10 ? "0" + curMonth : curMonth + "";
        String date = String.format("%d年%s月", curYear, tmp);
        txtDate.setText(date);
        mSelectDateLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
//                mReportView.setData(yList, xRawDatas, 1000, 1000 / yList.size());
//                mReportView.postInvalidate();
                showDatePopwindow(view);

            }
        });

    }

    private void initXAxisData() {
        yList = new ArrayList<>();
        //testcode
//        yList.add(100.00);
//        yList.add(250.0);
//        yList.add(300.0);
//        yList.add(540.0);
//        yList.add(1000.0);
        xRawDatas = new ArrayList<String>();
        for (int i = 1; i <= 31; i++) {
            xRawDatas.add(i + "");
        }
    }

    private void showDatePopwindow(View rootView) {
        if (pickDateView == null) {
            pickDateView = LayoutInflater.from(this).inflate(R.layout.dialog_monthly_report, null, false);
        }
        if (pickDateWindow == null) {
            pickDateWindow = new PopupWindow(pickDateView, dip2px(180), dip2px(200), true);
            pickDateWindow.setOutsideTouchable(true);
            pickDateWindow.setTouchable(true);


            yearPicker = (NumberPicker) pickDateView.findViewById(R.id.year);
            monthPicker = (NumberPicker) pickDateView.findViewById(R.id.month);
            monthPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
            yearPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
            years = new String[curYear - 2013 + 1];
            for (int i = 0; i <= (curYear - 2013); i++) {
                years[i] = (i + 2013) + "";
            }
            yearPicker.setDisplayedValues(years);
            yearPicker.setMinValue(2013);
            yearPicker.setMaxValue(Integer.parseInt(years[years.length - 1]));
            yearPicker.setValue(1);
            yearPicker.setWrapSelectorWheel(false);

            yearPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
                @Override
                public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
                    curYear = newVal;
                }
            });

            monthPicker.setDisplayedValues(months);
            monthPicker.setMinValue(1);
            monthPicker.setMaxValue(12);
            monthPicker.setValue(1);
            monthPicker.setWrapSelectorWheel(false);

            monthPicker.setOnScrollListener(new NumberPicker.OnScrollListener() {
                @Override
                public void onScrollStateChange(NumberPicker view, int scrollState) {
                    if (scrollState == SCROLL_STATE_IDLE) {
                        System.out.println("65------stop:" + view.getValue());
                        curMonth = view.getValue();
                        if (mHandler.hasMessages(23)) {
                            mHandler.removeMessages(23);
                        }
                        mHandler.sendEmptyMessageDelayed(23, 1000);
                    }
                }
            });
        }
        pickDateWindow.showAsDropDown(rootView, 0, 0);

    }

    private class MainHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 23) {
                String tmp = curMonth < 10 ? "0" + curMonth : curMonth + "";
                String date = String.format("%d年%s月", curYear, tmp);
                txtDate.setText(date);
                queryMontlhyReport(curYear, curMonth);

            }
        }
    }

    @Override
    public void queryMontlhyReportSuccess(Object resultData) {
        super.queryMontlhyReportSuccess(resultData);
        MarketBusinessReport report = (MarketBusinessReport) resultData;

        List<MarketBusinessReport.DataBean.ResultListBean> dataList = report.getData().getResultList();
        if (dataList.size() <= 0) return;
        yList.clear();
        for (int i = 0; i < dataList.size(); i++) {
            MarketBusinessReport.DataBean.ResultListBean bean = dataList.get(i);
            yList.add(Double.parseDouble(bean.getTradeAmount()));
        }
        mReportView.setData(yList, xRawDatas, Collections.max(yList).intValue(), Collections.max(yList).intValue() / yList.size());
        mReportView.postInvalidate();
        pickDateWindow.dismiss();
    }

    private int dip2px(float dpValue) {
        float scale = this.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    private void getCurYearMonth() {
        Calendar cal = Calendar.getInstance();
        curYear = cal.get(Calendar.YEAR);
        curMonth = cal.get(Calendar.MONTH);
    }

}

六、IOS实现原理是使用CAShapeLayer+UIBezierPath,具体原理为android的翻译,这里就不详细解说了直接放出源码吧
.h文件:

//
//  QTXXCurvChartView.h
//  IMobPay
//
//  Created by liuxiaobing on 2018/11/6.
//  Copyright © 2018 QTPay. All rights reserved.
//

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface QTXXCurvChartView : UIView

/** 点数据 */
@property (nonatomic,strong) NSArray *dataArrOfPoint;
/** Y轴坐标数据 */
@property (nonatomic, strong) NSArray *dataArrOfY;
/** X轴坐标数据 */
@property (nonatomic, strong) NSArray *dataArrOfX;

@property(nonatomic,strong) UIBezierPath* bezierPath;

@property(nonatomic,strong) NSString* yearStr;
@property(nonatomic,strong) NSString* monthStr;

@end

NS_ASSUME_NONNULL_END

.m文件

//
//  QTXXCurvChartView.m
//  IMobPay
//
//  Created by liuxiaobing on 2018/11/6.
//  Copyright © 2018 QTPay. All rights reserved.
//

#import "QTXXCurvChartView.h"

@interface QTXXCurvChartView ()
@property (nonatomic, strong) UIView  *contentView;
@property (nonatomic,strong)UIView *lineChartView;
@property (nonatomic,strong)NSMutableArray *pointCenterArr;
@property (nonatomic,assign) BOOL startDrawLine;
@property (nonatomic,assign) CGPoint curPos;
@property (nonatomic,assign) NSInteger lineHeight;
@property (nonatomic,strong) NSMutableDictionary* posAndValue; //当前座标代表的值
@property (nonatomic,assign) NSInteger topIndexLineWidth;
@property(nonatomic,assign) NSInteger topIndexLineTopMargin;
@property(nonatomic,strong) NSString* selectDaySum; //选中的消费金额
@property(nonatomic,strong) UIBezierPath* fingerMoveLinePath;
@property(nonatomic,strong) UIBezierPath* midCirclePath;
@property(nonatomic,strong) UIBezierPath* outterCirclePath;
@property(nonatomic,strong) UIBezierPath* innerCirclePath;

@property(nonatomic,strong) CAShapeLayer* fingerMoveLineLayer;
@property(nonatomic,strong) CAShapeLayer* outerCircleLayer;
@property(nonatomic,strong) CAShapeLayer* midCircleLayer;
@property(nonatomic,strong) CAShapeLayer* innerCircleLayer;


@end


@implementation QTXXCurvChartView

-(instancetype)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]){
        self.startDrawLine = false;
        self.curPos = CGPointZero;
        self.posAndValue = [[NSMutableDictionary alloc] init];
        self.topIndexLineTopMargin = 10;
        
        
        //左上角按钮
        //        _titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 10, 200, 20)];
        //        _titleLabel.font = [UIFont systemFontOfSize:12];
        //        _titleLabel.textAlignment = NSTextAlignmentLeft;
        //        _titleLabel.textColor = [UIColor colorWithRed:122/255.0 green:122/255.0  blue:122/255.0  alpha:1];
        //        _titleLabel.backgroundColor = [UIColor clearColor];
        //        [self addSubview:_titleLabel];
        
        //下面按钮
        //        _bottomLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 20)];
        //        _bottomLabel.center = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height - 10 - 20 / 2);
        //        _bottomLabel.font = [UIFont systemFontOfSize:12];
        //        _bottomLabel.textAlignment = NSTextAlignmentCenter;
        //        _bottomLabel.textColor = [UIColor colorWithRed:0/255.0 green:165/255.0  blue:87/255.0  alpha:1];
        //        _bottomLabel.backgroundColor = [UIColor clearColor];
        //        [self addSubview:_bottomLabel];
        
        
        //中间区域
        _contentView = [[UIView alloc]initWithFrame:CGRectMake(0, 40, self.bounds.size.width, self.bounds.size.height - 40)];
        _contentView.backgroundColor = [UIColor clearColor];
        [self addSubview:_contentView];
        _contentView.backgroundColor = [UIColor clearColor];
        [self addLineChartView];
        self.pointCenterArr = [NSMutableArray array];
        
    }
    return self;
    
}
- (void)layoutSubviews{
    self.lineHeight = [self bounds].size.height;
    self.topIndexLineWidth = [self bounds].size.width;
}
#pragma mark - 外部赋值
//外部Y坐标轴赋值
-(void)setDataArrOfY:(NSArray *)dataArrOfY
{
    _dataArrOfY = dataArrOfY;
    [self addYAxisViews];
}

//外部X坐标轴赋值
-(void)setDataArrOfX:(NSArray *)dataArrOfX
{
    _dataArrOfX = dataArrOfX;
    [self addXAxisViews];
    [self addLinesView];
}

//点数据
-(void)setDataArrOfPoint:(NSArray *)dataArrOfPoint
{
    _dataArrOfPoint = dataArrOfPoint;
    [self addPointView];
    [self addBezierLine];
}

#pragma mark - UI
- (void)addLineChartView{
    _lineChartView = [[UIView alloc]initWithFrame:CGRectMake(5, 50, _contentView.bounds.size.width - 15, _contentView.bounds.size.height - 100)];
    _lineChartView.layer.masksToBounds = YES;
    _lineChartView.layer.borderWidth = 0.5;
    _lineChartView.layer.borderColor = [UIColor colorWithRed:216/255.0 green:216/255.0  blue:216/255.0  alpha:1].CGColor;
    _lineChartView.backgroundColor = [UIColor clearColor];
    [_contentView addSubview:_lineChartView];
    [_lineChartView setClipsToBounds:false];
}

-(void)addYAxisViews{
    CGFloat height = _lineChartView.bounds.size.height / (_dataArrOfY.count - 1);
    for (int i = 0;i< _dataArrOfY.count ;i++ )
    {
        if([_dataArrOfY[i] integerValue] == 0){continue;}
        UILabel *leftLabel = [[UILabel alloc]initWithFrame:CGRectMake(5, height * i - height / 2 + 10, 80, 20)];
        leftLabel.font = [UIFont systemFontOfSize:10];
        leftLabel.textColor = [UIColor colorWithRed:74/255.0 green:74/255.0  blue:74/255.0  alpha:1];
        leftLabel.textAlignment = NSTextAlignmentLeft;
        leftLabel.text = _dataArrOfY[i];
        leftLabel.backgroundColor = [UIColor clearColor];
        [_lineChartView addSubview:leftLabel];
    }
}

-(void)addXAxisViews
{
    CGFloat white = _lineChartView.bounds.size.height /( _dataArrOfY.count - 1);
    CGFloat height = _lineChartView.bounds.size.width /( _dataArrOfX.count - 1);
    for (int i = 0;i< _dataArrOfX.count;i++ )
    {
        if([_dataArrOfX[i] integerValue] % 5 != 0){continue;}
        UILabel *leftLabel = [[UILabel alloc]initWithFrame:CGRectMake(
                                                                      i*height - 1.5 * height +_lineChartView.frame.origin.x,
                                                                      white+_lineChartView.bounds.size.height, 30, 20)];
        leftLabel.font = [UIFont systemFontOfSize:10];
        leftLabel.textColor = [UIColor colorWithRed:74/255.0 green:74/255.0  blue:74/255.0  alpha:1];
        leftLabel.text = _dataArrOfX[i];
        leftLabel.textAlignment = NSTextAlignmentCenter;
        leftLabel.backgroundColor = [UIColor clearColor];
        [_contentView addSubview:leftLabel];
    }
    
}

-(void)addLinesView
{
    CGFloat white = _lineChartView.bounds.size.height /( _dataArrOfY.count - 1);
    CGFloat height = _lineChartView.bounds.size.width /( _dataArrOfX.count - 1);
    //横格
    for (int i = 0;i < _dataArrOfY.count - 1 ;i++ ){
        UIView *hengView = [[UIView alloc] initWithFrame:CGRectMake(0, white * (i + 1),_lineChartView.bounds.size.width , 0.5)];
        hengView.backgroundColor = [UIColor colorWithRed:216/255.0 green:216/255.0  blue:216/255.0  alpha:1];
        [_lineChartView addSubview:hengView];
    }
    //竖格
    for (int i = 0;i< _dataArrOfX.count - 2 ;i++ ){
        
        UIView *shuView = [[UIView alloc]initWithFrame:CGRectMake(height * (i + 1), 0, 0.5, _lineChartView.bounds.size.height)];
        shuView.backgroundColor = [UIColor colorWithRed:216/255.0 green:216/255.0  blue:216/255.0  alpha:1];
        [_lineChartView addSubview:shuView];
    }
}

#pragma mark - 点和根据点画贝塞尔曲线
-(void)addPointView
{
    //区域高
    CGFloat height = self.lineChartView.bounds.size.height;
    //y轴最小值
    float arrmin = [_dataArrOfY[_dataArrOfY.count - 1] floatValue];
    //y轴最大值
    float arrmax = [_dataArrOfY[0] floatValue];
    //区域宽
    CGFloat width = self.lineChartView.bounds.size.width;
    //X轴间距
    float Xmargin = width / (_dataArrOfX.count - 1 );
    
    for (int i = 0; i<_dataArrOfPoint.count; i++)
    {
        //nowFloat是当前值
        float nowFloat = [_dataArrOfPoint[i] floatValue];
        //点点的x就是(竖着的间距 * i),y坐标就是()
        UIView *v = [[UIView alloc] initWithFrame:CGRectMake((Xmargin)*i - 2 / 2, height - (nowFloat - arrmin)/(arrmax - arrmin) * height - 2 / 2 , 2, 2)];
        v.backgroundColor = [UIColor blueColor];
        [_lineChartView addSubview:v];
        
        NSValue *point = [NSValue valueWithCGPoint:v.center];
        [self.pointCenterArr addObject:point];
        
        //将具体那一天的的view 与 值关联起来,通过这个值获取view
        //获取视图对象,继而获取 视图对象的座标,来控制中间l圆的动态座标
        [self.posAndValue setObject:v forKey:_dataArrOfPoint[i]];
    }
    
}

-(void)addBezierLine{
    //取得起点
    CGPoint p1 = [[self.pointCenterArr objectAtIndex:0] CGPointValue];
    UIBezierPath *beizer = [UIBezierPath bezierPath];
    [beizer moveToPoint:p1];
    //添加线
    for (int i = 0;i<self.pointCenterArr.count;i++ ){
        if (i != 0){
            CGPoint prePoint = [[self.pointCenterArr objectAtIndex:i-1] CGPointValue];
            CGPoint nowPoint = [[self.pointCenterArr objectAtIndex:i] CGPointValue];
            [beizer addCurveToPoint:nowPoint controlPoint1:CGPointMake((nowPoint.x+prePoint.x)/2, prePoint.y) controlPoint2:CGPointMake((nowPoint.x+prePoint.x)/2, nowPoint.y)];
        }
    }
    //显示线
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.path = beizer.CGPath;
    shapeLayer.fillColor = [UIColor clearColor].CGColor;
    shapeLayer.strokeColor = [self colorWithHexString:@"#1f96f2"].CGColor;
    shapeLayer.lineWidth = 2;
    shapeLayer.zPosition = -2;
    [_lineChartView.layer addSublayer:shapeLayer];
    
    
    
    //设置动画
    CABasicAnimation *anmi = [CABasicAnimation animation];
    anmi.keyPath = @"strokeEnd";
    anmi.fromValue = [NSNumber numberWithFloat:0];
    anmi.toValue = [NSNumber numberWithFloat:1.0f];
    anmi.duration =2.0f;
    anmi.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    anmi.autoreverses = NO;
    [shapeLayer addAnimation:anmi forKey:@"stroke"];
    
    
    //遮罩层相关
    UIBezierPath *bezier1 = [UIBezierPath bezierPath];
    bezier1.lineCapStyle = kCGLineCapRound;
    bezier1.lineJoinStyle = kCGLineJoinMiter;
    [bezier1 moveToPoint:p1];
    CGPoint lastPoint;
    for (int i = 0;i<self.pointCenterArr.count;i++ )
    {
        if (i != 0)
        {
            CGPoint prePoint = [[self.pointCenterArr objectAtIndex:i-1] CGPointValue];
            CGPoint nowPoint = [[self.pointCenterArr objectAtIndex:i] CGPointValue];
            [bezier1 addCurveToPoint:nowPoint controlPoint1:CGPointMake((nowPoint.x+prePoint.x)/2, prePoint.y) controlPoint2:CGPointMake((nowPoint.x+prePoint.x)/2, nowPoint.y)];
            if (i == self.pointCenterArr.count-1)
            {
                lastPoint = nowPoint;
            }
        }
    }
    //获取最后一个点的X值
    CGFloat lastPointX = lastPoint.x;
    CGPoint lastPointX1 = CGPointMake(lastPointX,_lineChartView.bounds.size.height);
    [bezier1 addLineToPoint:lastPointX1];
    //回到原点
    [bezier1 addLineToPoint:CGPointMake(p1.x, _lineChartView.bounds.size.height)];
    [bezier1 addLineToPoint:p1];
    
    CAShapeLayer *shadeLayer = [CAShapeLayer layer];
    shadeLayer.path = bezier1.CGPath;
    shadeLayer.fillColor = [UIColor colorWithRed:245/255.0 green:166/255.0  blue:35/255.0  alpha:1].CGColor;
    [_lineChartView.layer addSublayer:shadeLayer];
    
    
    //渐变图层
    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
    gradientLayer.frame = CGRectMake(0, 0, 0, _lineChartView.bounds.size.height);
    gradientLayer.startPoint = CGPointMake(0, 0);
    gradientLayer.endPoint = CGPointMake(0, 1);
    gradientLayer.cornerRadius = 5;
    gradientLayer.masksToBounds = YES;
    gradientLayer.zPosition = -4;
    gradientLayer.colors = @[(__bridge id)[UIColor colorWithRed:222/255.0 green:240/255.0  blue:254/255.0  alpha:0.8].CGColor,(__bridge id)[UIColor colorWithRed:222/255.0 green:240/255.0  blue:254/255.0  alpha:0.6].CGColor];
    gradientLayer.locations = @[@(0.5f)];
    
    CALayer *baseLayer = [CALayer layer];
    [baseLayer addSublayer:gradientLayer];
    [baseLayer setMask:shadeLayer];
    [_lineChartView.layer addSublayer:baseLayer];
    
    CABasicAnimation *anmi1 = [CABasicAnimation animation];
    anmi1.keyPath = @"bounds";
    anmi1.duration = 2.0f;
    anmi1.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 2*lastPoint.x, _lineChartView.bounds.size.height)];
    anmi1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    anmi1.fillMode = kCAFillModeForwards;
    anmi1.autoreverses = NO;
    anmi1.removedOnCompletion = NO;
    [gradientLayer addAnimation:anmi1 forKey:@"bounds"];
    
    
    self.fingerMoveLinePath = [UIBezierPath bezierPath];
    self.midCirclePath = [UIBezierPath bezierPath];
    self.fingerMoveLineLayer = [CAShapeLayer layer];
    self.midCircleLayer = [CAShapeLayer layer];
    self.outerCircleLayer = [CAShapeLayer layer];
    self.innerCircleLayer = [CAShapeLayer layer];
    
}

- (void)drawRect:(CGRect)rect{
    [super drawRect:rect];
    [self drawTopIndexLine:self.curPos];
    [self drawDetailMaker:self.curPos content:self.selectDaySum];
    //[self drawMidleIndexCircle:self.curPos];
    [self drawFingerMoveLine:self.curPos];
}

-(void) drawFingerMoveLine:(CGPoint) curPos{
    if(!self.startDrawLine) return;
    UIView* curValueView = [self.posAndValue objectForKey:self.selectDaySum];
    CGPoint newPos = [self convertPoint:curPos toView:_lineChartView];
    if(curPos.x < 0 || !curValueView){
        [self.fingerMoveLineLayer removeFromSuperlayer];
    }else{
        if(self.fingerMoveLineLayer){
            [self.fingerMoveLineLayer removeFromSuperlayer];
            self.fingerMoveLineLayer.path = nil;
            self.fingerMoveLineLayer.fillColor = [UIColor clearColor].CGColor;
            self.fingerMoveLineLayer.strokeColor = [UIColor clearColor].CGColor;
            
        }
        self.fingerMoveLinePath = [UIBezierPath bezierPath];
        [self.fingerMoveLinePath moveToPoint:CGPointMake(newPos.x, -10)];
        [self.fingerMoveLinePath addLineToPoint:CGPointMake(newPos.x, _lineChartView.bounds.size.height)];
        self.fingerMoveLineLayer.path = self.fingerMoveLinePath.CGPath;
        self.fingerMoveLineLayer.fillColor = [UIColor redColor].CGColor;
        self.fingerMoveLineLayer.strokeColor = [UIColor redColor].CGColor;
        self.fingerMoveLineLayer.lineWidth = 1;
        [_lineChartView.layer addSublayer:self.fingerMoveLineLayer];
    }
    
    
    if(!curValueView) {
        [self.outerCircleLayer removeFromSuperlayer];
        [self.midCircleLayer removeFromSuperlayer];
        [self.innerCircleLayer removeFromSuperlayer];
        return;
    }
    // 图层中心点、大小(中心点和大小构成frame)
    if(self.outerCircleLayer){
        [self.outerCircleLayer removeFromSuperlayer];
        self.outerCircleLayer.path = nil;
        self.outerCircleLayer.fillColor = [UIColor clearColor].CGColor;
        self.outerCircleLayer.strokeColor = [UIColor clearColor].CGColor;
        self.outterCirclePath = nil;
    }
    self.outterCirclePath = [UIBezierPath bezierPath];
    [self.outterCirclePath addArcWithCenter:CGPointMake(newPos.x, curValueView.center.y)
                                     radius:10
                                 startAngle:0
                                   endAngle:M_PI*2
                                  clockwise:YES];
    self.outerCircleLayer.fillColor = [UIColor colorWithRed:239.0/255.0f green:180.0 / 255.f blue:167.0 / 255.0f alpha:1.0f].CGColor;
    self.outerCircleLayer.path = self.outterCirclePath.CGPath;
    [_lineChartView.layer addSublayer:self.outerCircleLayer];
    
    //中u圈
    self.midCirclePath = [UIBezierPath bezierPath];
    if(self.midCircleLayer){
        [self.midCircleLayer removeFromSuperlayer];
        self.midCircleLayer.path = nil;
        self.midCircleLayer.fillColor = [UIColor clearColor].CGColor;
        self.midCircleLayer.strokeColor = [UIColor clearColor].CGColor;
    }
    [self.midCirclePath addArcWithCenter:CGPointMake(newPos.x, curValueView.center.y)
                                  radius:6
                              startAngle:0
                                endAngle:M_PI*2
                               clockwise:YES];
    self.midCircleLayer.fillColor = [UIColor colorWithRed:1.0f green:1.f blue:1.0f alpha:1.0f].CGColor;
    self.midCircleLayer.path = self.midCirclePath.CGPath;
    [_lineChartView.layer addSublayer:self.midCircleLayer];
    //内圈
    self.innerCirclePath = [UIBezierPath bezierPath];
    if(self.innerCircleLayer){
        [self.innerCircleLayer removeFromSuperlayer];
        self.innerCircleLayer.path = nil;
        self.innerCircleLayer.fillColor = [UIColor clearColor].CGColor;
        self.innerCircleLayer.strokeColor = [UIColor clearColor].CGColor;
    }
    [self.innerCirclePath addArcWithCenter:CGPointMake(newPos.x, curValueView.center.y)
                                    radius:3
                                startAngle:0
                                  endAngle:M_PI*2
                                 clockwise:YES];
    self.innerCircleLayer.fillColor = [UIColor colorWithRed:243.0/255.0f green:99.0 / 255.0f blue:66.0 / 255.0f alpha:1.0f].CGColor;
    self.innerCircleLayer.path = self.innerCirclePath.CGPath;
    [_lineChartView.layer addSublayer:self.innerCircleLayer];
    
}



/**
 画中间索引线
 
 @param point x座标
 */
-(void)drawIndexLine:(CGPoint) point{
    if(self.startDrawLine){
        
        CGContextRef context=UIGraphicsGetCurrentContext();
        CGContextMoveToPoint(context, point.x, self.topIndexLineTopMargin + 5);
        CGContextAddLineToPoint(context, point.x, self.lineHeight);
        CGContextClosePath(context);
        CGContextSetLineWidth(context, 0.8);
        [[UIColor redColor]setStroke];//设置红色边框
        //[[UIColor greenColor]setFill];//设置绿色填充
        //[[UIColor blueColor]set];//同时设置填充和边框色
        CGContextDrawPath(context, kCGPathFillStroke);
        
    }
}

/**
 画顶部指示线
 
 @param point 当前座标
 */
-(void) drawTopIndexLine:(CGPoint)point{
    
    if(!self.startDrawLine) return;
    CGContextRef context=UIGraphicsGetCurrentContext();
    CGContextMoveToPoint(context, 0, self.topIndexLineTopMargin);
    CGContextAddLineToPoint(context, self.topIndexLineWidth, self.topIndexLineTopMargin);
    CGContextClosePath(context);
    CGContextSetLineWidth(context, 0.8);
    [[UIColor redColor]setStroke];//设置红色边框
    CGContextDrawPath(context, kCGPathFillStroke);
    
    UIView* curValueView = [self.posAndValue objectForKey:self.selectDaySum];
    if(!curValueView || point.x < 0) return;
    //画索引三角形,使用3条线组成
    CGContextMoveToPoint(context, point.x - 5, self.topIndexLineTopMargin);
    CGContextAddLineToPoint(context, point.x, 15);
    
    CGContextMoveToPoint(context, point.x, 15);
    CGContextAddLineToPoint(context, point.x + 5, self.topIndexLineTopMargin);
    [[UIColor redColor]setStroke];//设置红色边框
    CGContextClosePath(context);
    CGContextSetLineWidth(context, 0.8);
    CGContextDrawPath(context, kCGPathFillStroke);
    
    //画三角形中“|”
    CGContextMoveToPoint(context, point.x, self.topIndexLineTopMargin + 4);
    CGContextAddLineToPoint(context, point.x, self.topIndexLineTopMargin + 11);
    CGContextClosePath(context);
    CGContextSetLineWidth(context, 0.8);
    CGContextDrawPath(context, kCGPathFillStroke);
    
    CGContextMoveToPoint(context, point.x + 5, self.topIndexLineTopMargin);
    CGContextAddLineToPoint(context, point.x - 5, self.topIndexLineTopMargin);
    [[UIColor whiteColor]setStroke];//设置红色边框
    CGContextClosePath(context);
    CGContextSetLineWidth(context, 0.8);
    CGContextDrawPath(context, kCGPathFillStroke);
    
}


/**
 画中间指示l圆
 
 @param point 当前手指滑动的座标
 */
-(void) drawMidleIndexCircle:(CGPoint) point{
    if(!self.startDrawLine || self.selectDaySum == nil) return;
    
    UIView* curValueView = [self.posAndValue objectForKey:self.selectDaySum];
    CGPoint curValue = curValueView.center;
    
    CGPoint parentPos = [_lineChartView convertPoint:curValue toView:self];
    
    //先画外面大圆
    CGRect frame = CGRectMake(point.x - 10, parentPos.y - 10, 20, 20);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextAddEllipseInRect(context, frame);
    [[UIColor colorWithRed:239.0/255.0f green:180.0 / 255.f blue:167.0 / 255.0f alpha:0.5f] set];
    CGContextFillPath(context);
    
    //画中间圆
    CGRect midCirlceRect = CGRectMake(point.x - 7, parentPos.y - 7, 14, 14);
    CGContextAddEllipseInRect(context, midCirlceRect);
    [[UIColor colorWithRed:1.00f green:1.00f blue:1.00f alpha:1.0f] set];
    CGContextFillPath(context);
    
    //画m内圆点
    CGRect innerCirlceRect = CGRectMake(point.x - 5, parentPos.y - 5, 10, 10);
    CGContextAddEllipseInRect(context, innerCirlceRect);
    [[UIColor colorWithRed:243.0/255.0f green:99.0 / 255.0f blue:66.0 / 255.0f alpha:1.0f] set];
    CGContextFillPath(context);
}

/**
 画浮动显示框
 
 @param point 当前手指座标
 @param text 当前内容
 */
-(void) drawDetailMaker:(CGPoint)point content:(NSString*) text{
    if(!self.startDrawLine || self.selectDaySum == nil) return;
    CGMutablePathRef path = CGPathCreateMutable();
    CGRect rectangle = CGRectMake(point.x - 50, 2 * self.topIndexLineTopMargin,100, 60.0f); //指定矩形
    CGPathAddRect(path,NULL, rectangle);//将矩形添加到路径中
    CGContextRef currentContext = UIGraphicsGetCurrentContext();//获取上下文
    CGContextAddPath(currentContext, path);//将路径添加到上下文
    [[UIColor colorWithRed:242.0/255.0f green:99.0/255.0f blue:65.0 / 255.0f alpha:1.0f] setFill];
    //[[UIColor colorWithRed:0.20f green:0.60f blue:0.80f alpha:1.0f] setStroke];
    CGContextSetLineWidth(currentContext,1.0f);
    CGContextDrawPath(currentContext, kCGPathFillStroke);
    CGPathRelease(path);
    
    
    //绘制文本
    CGContextSetLineWidth(currentContext, 1.0);
    //CGContextSetRGBFillColor (currentContext, 1.0f, 1.0f, 1.0f, 1.0f);
    
    //段落格式
    NSMutableParagraphStyle *textStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
    textStyle.lineBreakMode = NSLineBreakByWordWrapping;
    textStyle.alignment = NSTextAlignmentCenter;
    UIFont  *font = [UIFont systemFontOfSize:14.0];
    NSDictionary *attributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName:textStyle,
                                 NSForegroundColorAttributeName:[UIColor whiteColor]
                                 };//构建属性集合
    NSString* value = [NSString stringWithFormat:@"%@/%@/%ld\n%@\n%@",@"2018",@"05",([self getDay:point.x] + 1),@"每日利润",text];
    CGSize strSize = [value sizeWithAttributes:attributes];  //获得size
    CGFloat marginTop = (rectangle.size.height - strSize.height) / 2;
    //垂直居中要自己计算
    CGRect r = CGRectMake(rectangle.origin.x, rectangle.origin.y + marginTop,rectangle.size.width, strSize.height);
    [value drawInRect:r withAttributes:attributes];
    
}

//当有一个或多个手指触摸事件在当前视图或window窗体中响应
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    NSSet *allTouches = [event allTouches];
    UITouch *touch = [allTouches anyObject];
    self.curPos = [touch locationInView:[touch view]];
    self.startDrawLine = true;
    [self setNeedsDisplay];
    self.selectDaySum = [self getValueByPosX:self.curPos.x];
}
-(void) touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self setNeedsDisplay];
    self.curPos = [self getPosByUIEvent:event];
    self.startDrawLine = true;
    self.selectDaySum = [self getValueByPosX:self.curPos.x];
    
}

-(void) touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //[self setNeedsDisplay];
    //self.startDrawLine = false;
    
}

-(CGPoint) getPosByUIEvent:(UIEvent*)event{
    NSSet *allTouches = [event allTouches];
    UITouch *touch = [allTouches anyObject];
    CGPoint point = [touch locationInView:[touch view]];
    return point;
}

-(NSString*) getValueByPosX:(CGFloat) x{
    NSString* value = nil;
    CGFloat dayUnit = _lineChartView.bounds.size.width /( _dataArrOfX.count - 1);   //一天所占用的格子
    NSInteger index = x / dayUnit;
    if(index < [_dataArrOfPoint count] && index >= 0){
        value = [_dataArrOfPoint objectAtIndex:index];
    }
    return value;
}

- (UIColor *) colorWithHexString: (NSString *)color{
    
    NSString *cString = [[color stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString];
    
    // String should be 6 or 8 characters
    if ([cString length] < 6) {
        return [UIColor clearColor];
    }
    // 判断前缀
    if ([cString hasPrefix:@"0X"])
        cString = [cString substringFromIndex:2];
    if ([cString hasPrefix:@"#"])
        cString = [cString substringFromIndex:1];
    if ([cString length] != 6)
        return [UIColor clearColor];
    // 从六位数值中找到RGB对应的位数并转换
    NSRange range;
    range.location = 0;
    range.length = 2;
    //R、G、B
    NSString *rString = [cString substringWithRange:range];
    range.location = 2;
    NSString *gString = [cString substringWithRange:range];
    range.location = 4;
    NSString *bString = [cString substringWithRange:range];
    // Scan values
    unsigned int r, g, b;
    [[NSScanner scannerWithString:rString] scanHexInt:&r];
    [[NSScanner scannerWithString:gString] scanHexInt:&g];
    [[NSScanner scannerWithString:bString] scanHexInt:&b];
    
    return [UIColor colorWithRed:((float) r / 255.0f) green:((float) g / 255.0f) blue:((float) b / 255.0f) alpha:1.0f];
    
}

-(CGPoint) getPointByPoint:(CGFloat)x{
    NSString* value = [self getValueByPosX:x];
    UIView* view = [self.posAndValue objectForKey:value];
    return view.center;
}
-(NSInteger) getDay:(CGFloat) x{
    CGFloat dayUnit = _lineChartView.bounds.size.width /( _dataArrOfX.count - 1);   //一天所占用的格子
    NSInteger index = x / dayUnit;
    return index;
}


@end

使用方式.h文件:

//
//  IncomeChartVC.h
//  IMobPay
//
//  Created by liuxiaobing on 2018/11/6.
//  Copyright © 2018 QTPay. All rights reserved.
//

#import "IMPBaseViewController.h"
#import "QTXXCurvChartView.h"

NS_ASSUME_NONNULL_BEGIN


/**
 每日收入报表页面
 */
@interface IncomeChartVC : IMPBaseViewController<UIPickerViewDelegate,UIPickerViewDataSource>
@property(nonatomic,copy) NSString* date;
@property(nonatomic,strong) UIPickerView* yearPickerView;
@property(nonatomic,strong) NSMutableArray* yearArr;
@property(nonatomic,strong) NSMutableArray* monthArr;
@property(nonatomic,strong) NSMutableArray* pointArr;
@property(nonatomic,strong) NSArray* dataX;
@property(nonatomic,copy)NSString* year;
@property(nonatomic,copy)NSString* month;
@property(nonatomic,copy)NSString* selectDate;
@property(nonatomic,strong) QTXXCurvChartView* curvChartView;

@end

NS_ASSUME_NONNULL_END

.m文件

//
//  IncomeChartVC.m
//  IMobPay
//
//  Created by liuxiaobing on 2018/11/6.
//  Copyright © 2018 QTPay. All rights reserved.
//

#import "IncomeChartVC.h"

@interface IncomeChartVC ()<IMPNetWorkDelegate>
@property(nonatomic,strong) UIButton* queryBtn;

@end

@implementation IncomeChartVC

- (void)viewDidLoad {
    self.titleText = @"经营报表";
    [super viewDidLoad];
    [self initView];
    [self setTopBarViewBgColor:[UIColor colorWithRed:254.0 / 255.0f green:223.0 / 255.0f blue:51.0/255.0f alpha:1.0f]];
}

-(void) initView{
    
    //按钮下面的黄色背景
    UIView* queryBgView = [[UIView alloc] init];
    [self.view addSubview:queryBgView];
    queryBgView.backgroundColor = [UIColor colorWithRed:254.0 / 255.0f green:223.0 / 255.0f blue:51.0/255.0f alpha:1.0f];
    [queryBgView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.topBarView.mas_bottom).offset(-10);
        make.size.mas_equalTo(CGSizeMake(SCREEN_WIDTH, 180));
    }];
    
    [self getCurDate];
    //self.date = @"2018 年 11 月";
    self.queryBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [self.queryBtn setTitle:self.date forState:UIControlStateNormal];
    self.queryBtn.backgroundColor = [UIColor whiteColor];
    [self.queryBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    self.queryBtn.titleLabel.font = [UIFont systemFontOfSize:18];
    self.queryBtn.layer.cornerRadius = 25;
    [self.view addSubview:self.queryBtn];
    [self.queryBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(120);
        make.centerX.equalTo(self.view.mas_centerX);
        make.size.mas_equalTo(CGSizeMake(240, 50));
    }];
    [self.queryBtn addTarget:self action:@selector(onClickListener:) forControlEvents:UIControlEventTouchUpInside];
    [self.queryBtn setImage:[UIImage imageNamed:@"down_arrow"] forState:UIControlStateNormal];
    [self buttonForAdjustIconAndTitle:self.queryBtn imageTitleSpace:10.0f];
    
    //创建日期选择下拉列表
    self.yearPickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(50, 100, 140, 100)];
    self.yearPickerView.dataSource = self;
    self.yearPickerView.delegate = self;
    [self.view addSubview:self.yearPickerView];
    self.yearArr = [NSMutableArray arrayWithObjects:@"2013",@"2014",@"2015",@"2016",@"2018", nil];
    self.monthArr = [NSMutableArray arrayWithObjects:@"01",@"02",@"03",@"04",@"05",@"06",@"07",@"08",@"09",@"10",@"11",@"12", nil];
    self.yearPickerView.hidden = true;
    [self.yearPickerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.queryBtn.mas_bottom);
        make.size.mas_equalTo(CGSizeMake(240, 120));
        make.centerX.equalTo(self.view.mas_centerX);
    }];
    
    self.dataX = @[@"1",@"2",@"3",@"4",@"5",
                       @"6",@"7",@"8",@"9",@"10",
                       @"11",@"12",@"13",@"14",@"15",
                       @"16",@"17",@"18",@"19",@"20",
                       @"21",@"22",@"23",@"24",@"25",
                       @"26",@"27",@"28",@"29",@"30",@"31"];//X轴坐标
    self.pointArr = [[NSMutableArray alloc] init];
    
    [self queryDataFromServer:self.year month:self.month];
    //test code
//    NSArray* dataY = @[@"1200",@"1000",@"800",@"600",@"400",@"200",@"0"];//Y轴坐标
//    self.pointArr = [NSMutableArray arrayWithObjects:@"800",@"200",@"600",@"343",@"250",@"700",
//                     @"400",@"600",@"1000",@"340",@"550",@"800", nil];
//    self.curvChartView = [self buildChartView:self.dataX yAxis:dataY sumValue:self.pointArr];
//    [self.view addSubview:self.curvChartView];
//    [self.curvChartView mas_makeConstraints:^(MASConstraintMaker *make) {
//        make.top.equalTo(self.queryBtn.mas_bottom).offset(70);
//        make.size.mas_equalTo(CGSizeMake(SCREEN_WIDTH, SCREEN_HEIGHT - 260));
//        make.left.equalTo(self.view).offset(10);
//        make.right.equalTo(self.view).offset(-10);
//    }];
    
}
-(void) onClickListener:(UIButton*) button{
    self.yearPickerView.hidden = ![self.yearPickerView isHidden];
}

-(QTXXCurvChartView*) buildChartView:(NSArray*) dataOfX yAxis: (NSArray*) dataOfY sumValue:(NSArray*) dataOfPoint{
    
    QTXXCurvChartView* chartView = [[QTXXCurvChartView alloc] initWithFrame:CGRectMake(15, 300, self.view.frame.size.width - 30, 430)];
    chartView.backgroundColor = [UIColor colorWithRed:247/255.0 green:247/255.0  blue:247/255.0  alpha:1];
    chartView.dataArrOfY = dataOfY;//Y轴坐标
    chartView.dataArrOfX = dataOfX;//X轴坐标
    chartView.dataArrOfPoint = dataOfPoint;
    return chartView;
}

-(void) buttonForAdjustIconAndTitle:(UIButton*)button imageTitleSpace:(CGFloat)space{
    
    CGFloat imageWith = button.currentImage.size.width;
    CGFloat imageHeight = button.currentImage.size.height;
    
    CGFloat labelWidth = 0.0;
    CGFloat labelHeight = 0.0;
    if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {

        labelWidth = button.titleLabel.intrinsicContentSize.width;
        labelHeight = button.titleLabel.intrinsicContentSize.height;
    } else {
        labelWidth = button.titleLabel.frame.size.width;
        labelHeight = button.titleLabel.frame.size.height;
    }
    
    UIEdgeInsets imageEdgeInsets = UIEdgeInsetsZero;
    UIEdgeInsets labelEdgeInsets = UIEdgeInsetsZero;
    
    imageEdgeInsets = UIEdgeInsetsMake(0, labelWidth+space, 0, -labelWidth-space);
    labelEdgeInsets = UIEdgeInsetsMake(0, -imageWith-space, 0, imageWith+space);
    button.titleEdgeInsets = labelEdgeInsets;
    button.imageEdgeInsets = imageEdgeInsets;
    

}


/**********pagram pikcerView代理方法********/
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView{
    return 2;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component{
    return 50.0f;
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
    NSInteger result = 0;
    switch (component) {
        case 0:
            result = self.yearArr.count;
            break;
        case 1:
            result = self.monthArr.count;
            break;
            
        default:
            break;
    }
    
    return result;
    
}


- (NSString *)pickerView:(UIPickerView *)pickerView
             titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    NSString * title = nil;
    switch (component) {
        case 0:
            title = self.yearArr[row];
            break;
        case 1:
            title = self.monthArr[row];
            break;
        default:
            break;
    }
    
    return title;
}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:
(NSInteger)row inComponent:(NSInteger)component{
    switch (component) {
        case 0:
            self.year = [self.yearArr objectAtIndex:row];
            break;
        case 1:
            self.month = [self.monthArr objectAtIndex:row];
            break;
        default:
            break;
    }
    self.selectDate = [NSString stringWithFormat:@"%@ 年%@ 月",self.year,self.month];
     [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(dissmissPickerView:) object:pickerView];
    if(component == 1){
        [self performSelector:@selector(dissmissPickerView:) withObject:pickerView afterDelay:0.8f];
    }
}

- (void)dissmissPickerView:(UIPickerView *)pickerView{
    self.yearPickerView.hidden = true;
    [self.queryBtn setTitle:self.selectDate forState:UIControlStateNormal];
    [self queryDataFromServer:self.year month:self.month];
}
/**********pagram pikcerView代理方法---end ********/

-(void) queryDataFromServer:(NSString*) year month:(NSString*) month{
    [self showWaitView:@"请稍后..." withView:[UIApplication sharedApplication].keyWindow];
    NSString* param = [NSString stringWithFormat:@"%@%@",self.year,self.month];
    [self.interface requestChartData:param];
}

#pragma mark - IMPNetWorkDelegate
- (void)impNetworkResponse:(XRURLResponse *)respone purpose:(NSString *)purpose{
    [self hideWaitView];
    if (self.respOK) {
        NSDictionary *dic=respone.response;
        [self buildChartData:dic];
    }else{
        [self popTopManager:self.respDesc showPopViewType:POPTopTypeError];
    }
}
-(void) buildChartData:(NSDictionary*)dict{
    if(!dict) return;
    NSDictionary* resultList = [dict objectForKey:@"data"];
    if(resultList != nil){
        NSArray* dataList = [resultList objectForKey:@"resultList"];
        if(!dataList || [dataList count] <= 0) return;
        
        [self.pointArr removeAllObjects];
        
        for(NSDictionary* tmp in dataList){
            //NSLog(@"225--------------:%@",[tmp objectForKey:@"tradeAmount"]);
            [self.pointArr addObject:[tmp objectForKey:@"tradeAmount"]];
        }
        
        //构建报表视图
        if([self.pointArr count] > 0){
            CGFloat maxValue = [[self.pointArr valueForKeyPath:@"@max.floatValue"] floatValue];
            CGFloat unit = maxValue / ([self.pointArr count] - 1); //动态构建y轴刻度
            NSMutableArray* yUnitArr = [[NSMutableArray alloc] init];
            for(NSInteger i = [self.pointArr count] - 1; i > 0; i --){
                NSString* tmp = [NSString stringWithFormat:@"%1.0f",round(i * unit)];
                [yUnitArr addObject:tmp];
            }
            [yUnitArr addObject:@"0"];
            if(self.curvChartView){
                [self.curvChartView removeFromSuperview];
            }
            
            self.curvChartView = [self buildChartView:self.dataX yAxis:yUnitArr sumValue:self.pointArr];
            [self.view addSubview:self.curvChartView];
            [self.curvChartView mas_makeConstraints:^(MASConstraintMaker *make) {
                make.top.equalTo(self.queryBtn.mas_bottom).offset(70);
                make.size.mas_equalTo(CGSizeMake(SCREEN_WIDTH, SCREEN_HEIGHT - 260));
                make.left.equalTo(self.view).offset(10);
                make.right.equalTo(self.view).offset(-10);
            }];
        }
        
        
        
    }
    
}

-(void) getCurDate{
    NSDate* nowDate = [NSDate date];
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSUInteger unitFlags = NSCalendarUnitYear | //年
    NSCalendarUnitMonth | //月份
    NSCalendarUnitDay | //日
    NSCalendarUnitHour |  //小时
    NSCalendarUnitMinute |  //分钟
    NSCalendarUnitSecond;  // 秒
    NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:nowDate];
    
    self.year = [NSString stringWithFormat:@"%ld",[dateComponent year]];
    NSInteger month = [dateComponent month];
    if(month < 10){
        self.month = [NSString stringWithFormat:@"0%ld",[dateComponent month]];
    }else{
        self.month = [NSString stringWithFormat:@"%ld",[dateComponent month]];
    }
    self.date = [NSString stringWithFormat:@"%ld 年 %ld 月",[dateComponent year],[dateComponent month]];
    //NSInteger year = [dateComponent year];
   // NSInteger month = [dateComponent month];
//    NSInteger day = [dateComponent day];
//    NSInteger hour = [dateComponent hour];
//    NSInteger minute = [dateComponent minute];
//    NSInteger second = [dateComponent second];
    //NSLog(@"239---------year:%@  month:%@",self.year,self.month);
}



@end

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值