一、按照惯例先放上效果图
二、从这个效果图来看需要画下列几部分
- 座标轴: 画座标轴,这个相信比较简单,画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