android坐标图绘制曲线,Android 曲线图 绘制曲线

这篇博客详细介绍了如何在Android中自定义绘制曲线图,包括如何保留边框线、取消圆点和折线,以及如何实现曲线填充。通过重写DrawLineChart类的方法,利用Path和cubicTo()绘制曲线,并调整画笔样式来实现填充。此外,还展示了如何在DrawLine方法中添加额外的线条连接,以完成填充效果。
摘要由CSDN通过智能技术生成

Android 曲线图

5522470760c1

Paste_Image.png

这里我引用了上回介绍的 折线图

首先我们看看onDrwa 里面做了什么 从图里面我们可看到 绘制的框线方法,绘制data线方法 绘制线上的圆

从最上面的图效果来看我们不需要圆 也不需要折线,但是框线要保留 因此我们需要 把

这个几个方法 改成public ,到时候继承之后重写他 不调用父方法即可

5522470760c1

Paste_Image.png

5522470760c1

Paste_Image.png

接下来我们创建一个 类继承 DrawLineChart 重写 DrawLineCircle,DrawLine 方法

5522470760c1

这样我们就保留 了框线, 取消了圆和折线的绘制

接下来在DrawLine 里面绘制曲线

@Override

public void DrawLine(Canvas canvas) {

Path mPath=new Path();

for(int j = 0; j < mPoints.length; j++) {

Point startp = mPoints[j];

Point endp;

if (j != mPoints.length - 1) {

endp = mPoints[j + 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;

if (j == 0) {

mPath.moveTo(startp.x, startp.y);

}

/**

*添加一个立方贝塞尔曲线的最后一点,接近控制点

*(x1,y1)和(x2,y2),到最后(x3,y3)。如果没有移至()调用

*为这个轮廓,第一点是自动设置为(0,0)。

*

* @param x1的坐标1立方曲线控制点

* @param y1第一控制点的坐标一立方曲线

* @param x2上第二个控制点的坐标一立方曲线

* @param y2第二控制点的坐标一立方曲线

* @param x3的坐标三次曲线的终点

* @param y3终点坐标的三次曲线

*

*/

mPath.cubicTo(p3.x, p3.y, p4.x, p4.y, endp.x, endp.y);

}

}

/**这里直接使用了折线的画笔,你也可以在此类在定义一个画笔*/

canvas.drawPath(mPath,getBrokenLinePaint());

}

什么, 你不光要曲线,。 还要填充式的怎么办,这个时候你就需要将你的曲线连接起来 起点到终点连接起来 将画笔Style 改为FILL ,怎么连? 看图,从图上 我们可以看到

终点x,终点y 连接到 终点x,底部y

终点x,底部y 连接到 起点x,底部y

起点x 底部y 连接到 起点x,起点y

5522470760c1

Paste_Image.png

根据上面的分析 我们代码 就是这样的了

@Override

public void DrawLine(Canvas canvas) {

Path mPath=new Path();

for(int j = 0; j < mPoints.length; j++) {

Point startp = mPoints[j];

Point endp;

if (j != mPoints.length - 1) {

endp = mPoints[j + 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;

if (j == 0) {

mPath.moveTo(startp.x, startp.y);

}

/**

*添加一个立方贝塞尔曲线的最后一点,接近控制点

*(x1,y1)和(x2,y2),到最后(x3,y3)。如果没有移至()调用

*为这个轮廓,第一点是自动设置为(0,0)。

*

* @param x1的坐标1立方曲线控制点

* @param y1第一控制点的坐标一立方曲线

* @param x2上第二个控制点的坐标一立方曲线

* @param y2第二控制点的坐标一立方曲线

* @param x3的坐标三次曲线的终点

* @param y3终点坐标的三次曲线

*

*/

mPath.cubicTo(p3.x, p3.y, p4.x, p4.y, endp.x, endp.y);

}else {

/**连接到终点x,底部y*/

mPath.lineTo(startp.x,getNeedDrawHeight()+getBrokenLineTop());

/**连接到起点x,底部y*/

mPath.lineTo(mPoints[0].x,getNeedDrawHeight()+getBrokenLineTop());

/**连接到起点x,起点y*/

mPath.lineTo(mPoints[0].x,mPoints[0].y);

}

}

Paint paint=getBrokenLinePaint();

/**修改画笔属性*/

paint.setStyle(Paint.Style.FILL);

canvas.drawPath(mPath,getBrokenLinePaint());

}

效果图

5522470760c1

Paste_Image.png

完整代码

package com.app_chart;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Paint;

import android.graphics.Path;

import android.graphics.Point;

import android.support.annotation.Nullable;

import android.util.AttributeSet;

public class CurveChart extends DrawLineChart {

public CurveChart(Context context) {

super(context);

}

public CurveChart(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

}

@Override

public void DrawLineCircle(Canvas canvas) {

}

@Override

public void DrawLine(Canvas canvas) {

Path mPath=new Path();

for(int j = 0; j < mPoints.length; j++) {

Point startp = mPoints[j];

Point endp;

if (j != mPoints.length - 1) {

endp = mPoints[j + 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;

if (j == 0) {

mPath.moveTo(startp.x, startp.y);

}

/**

*添加一个立方贝塞尔曲线的最后一点,接近控制点

*(x1,y1)和(x2,y2),到最后(x3,y3)。如果没有移至()调用

*为这个轮廓,第一点是自动设置为(0,0)。

*

* @param x1的坐标1立方曲线控制点

* @param y1第一控制点的坐标一立方曲线

* @param x2上第二个控制点的坐标一立方曲线

* @param y2第二控制点的坐标一立方曲线

* @param x3的坐标三次曲线的终点

* @param y3终点坐标的三次曲线

*

*/

mPath.cubicTo(p3.x, p3.y, p4.x, p4.y, endp.x, endp.y);

}else {

/**连接到终点x,底部y*/

mPath.lineTo(startp.x,getNeedDrawHeight()+getBrokenLineTop());

/**连接到起点x,底部y*/

mPath.lineTo(mPoints[0].x,getNeedDrawHeight()+getBrokenLineTop());

/**连接到起点x,起点y*/

mPath.lineTo(mPoints[0].x,mPoints[0].y);

}

}

Paint paint=getBrokenLinePaint();

/**修改画笔属性*/

paint.setStyle(Paint.Style.FILL);

canvas.drawPath(mPath,getBrokenLinePaint());

}

}

Activity 调用

5522470760c1

Paste_Image.png

DrawLineChart 代码

package com.app_chart;

import android.content.Context;

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.View;

import java.text.DecimalFormat;

public class DrawLineChart extends View {

private static final String TAG = "DrawLineChart";

/**View宽度*/

private int mViewWidth;

/** View高度*/

private int mViewHeight;

/**边框线画笔*/

private Paint mBorderLinePaint;

/**文本画笔*/

private Paint mTextPaint;

/**要绘制的折线线画笔*/

private Paint mBrokenLinePaint;

/**折线文本画笔*/

private Paint mBrokenLineTextPaint;

/**圆画笔*/

private Paint mCirclePaint;

/**横线画笔*/

private Paint mHorizontalLinePaint;

/**圆的厚度*/

private float mCircleWidth=2;

/**圆的半径*/

private float radius=5;

/**边框的左边距*/

private float mBrokenLineLeft=dip2px(30);

/**边框的上边距*/

private float mBrokenLineTop=dip2px(10);

/**边框的下边距*/

private float mBrokenLineBottom=dip2px(20);

/**边框的右边距*/

private float mBrokenLinerRight=dip2px(10);

/**需要绘制的宽度*/

private float mNeedDrawWidth;

/**需要绘制的高度*/

private float mNeedDrawHeight;

/**数据值*/

private float[] value=new float[]{-5.55f,-6.899f,-4.55f,-0.045f,21.511f,22.221f,22.330f,21.448f,21.955f,23.6612f,22,22.18883f,21.47995f};

/**图表的最大值*/

private float maxVlaue=27.55f;

/**图表的最小值*/

private float minValue=-19.12f;

/**要计算的总值*/

private float calculateValue;

/**框线平均值*/

private float averageValue;

/**横线数量*/

private float numberLine=5;

/**边框线颜色*/

private int mBorderLineColor=Color.BLACK;

/**边框线的宽度*/

private int mBorderWidth=2;

/**边框文本颜色*/

private int mBorderTextColor=Color.GRAY;

/**边框文本大小*/

private float mBorderTextSize=20;

/**边框横线颜色*/

private int mBorderTransverseLineColor=Color.GRAY;

/**边框横线宽度*/

private float mBorderTransverseLineWidth=2;

/**折线颜色*/

private int mBrokenLineColor=Color.BLUE;

/**折线文本颜色*/

private int mBrokenLineTextColor=Color.BLUE;

/**折线宽度*/

private float mBrokenLineWidth=2;

/**折线文本大小*/

private float mBrokenLineTextSize=15;

/**折线圆的颜色*/

private int mCircleColor=Color.BLUE;

/**计算后的x,y坐标*/

public Point[] mPoints;

/**圆的半径*/

public void setRadius(float radius) {

this.radius = dip2px(radius);

}

/**设置宽度*/

public void setCircleWidth(float circleWidth){

this.mCircleWidth=dip2px(circleWidth);

}

/**设置边框左上右下边距*/

public void setBrokenLineLTRB(float l,float t,float r,float b){

mBrokenLineLeft=dip2px(l);

mBrokenLineTop=dip2px(t);

mBrokenLinerRight=dip2px(r);

mBrokenLineBottom=dip2px(b);

}

public int getViewWidth() {

return mViewWidth;

}

public int getViewHeight() {

return mViewHeight;

}

public float getCircleWidth() {

return mCircleWidth;

}

public float getRadius() {

return radius;

}

public float getBrokenLineLeft() {

return mBrokenLineLeft;

}

public float getBrokenLineTop() {

return mBrokenLineTop;

}

public float getBrokenLineBottom() {

return mBrokenLineBottom;

}

public float getBrokenLinerRight() {

return mBrokenLinerRight;

}

public float getNeedDrawWidth() {

return mNeedDrawWidth;

}

public float getNeedDrawHeight() {

return mNeedDrawHeight;

}

public Point[] getPoints() {

return mPoints;

}

/**数据data*/

public void setValue(float[] value) {

this.value = value;

}

/**图表显示最大值*/

public void setMaxVlaue(float maxVlaue) {

this.maxVlaue = maxVlaue;

}

/**图表显示最小值*/

public void setMinValue(float minValue) {

this.minValue = minValue;

}

/**图表横线数量*/

public void setNumberLine(float numberLine) {

this.numberLine = numberLine;

}

/**边框线颜色*/

public void setBorderLineColor(int borderLineColor) {

mBorderLineColor = borderLineColor;

}

/**边框文本颜色*/

public void setBorderTextColor(int borderTextColor) {

mBorderTextColor = borderTextColor;

}

/**边框文本大小*/

public void setBorderTextSize(float borderTextSize) {

mBorderTextSize = dip2px(borderTextSize);

}

/**框线横线 颜色*/

public void setBorderTransverseLineColor(int borderTransverseLineColor) {

mBorderTransverseLineColor = borderTransverseLineColor;

}

/**边框内折线颜色*/

public void setBrokenLineColor(int brokenLineColor) {

mBrokenLineColor = brokenLineColor;

}

/** 折线文本颜色*/

public void setBrokenLineTextColor(int brokenLineTextColor) {

mBrokenLineTextColor = brokenLineTextColor;

}

/**折线文本大小*/

public void setBrokenLineTextSize(float brokenLineTextSize) {

mBrokenLineTextSize = dip2px(brokenLineTextSize);

}

/**折线圆颜色*/

public void setCircleColor(int circleColor) {

mCircleColor = dip2px(circleColor);

}

/**边框线宽度*/

public void setBorderWidth(float borderWidth) {

mBorderWidth = dip2px(borderWidth);

}

/**边框横线宽度*/

public void setBorderTransverseLineWidth(float borderTransverseLineWidth) {

mBorderTransverseLineWidth = dip2px(borderTransverseLineWidth);

}

/**折线宽度*/

public void setBrokenLineWidth(float brokenLineWidth) {

mBrokenLineWidth = dip2px(brokenLineWidth);

}

/**获取框线画笔*/

public Paint getBorderLinePaint() {

return mBorderLinePaint;

}

/**获取边框文本画笔*/

public Paint getTextPaint() {

return mTextPaint;

}

/**获取折线画笔*/

public Paint getBrokenLinePaint() {

return mBrokenLinePaint;

}

/**获取折线文本画笔*/

public Paint getBrokenLineTextPaint() {

return mBrokenLineTextPaint;

}

/**获取圆画笔*/

public Paint getCirclePaint() {

return mCirclePaint;

}

/**获取边框横线画笔*/

public Paint getHorizontalLinePaint() {

return mHorizontalLinePaint;

}

/**

* 将dip或dp值转换为px值,保证尺寸大小不变

*

* @param dipValue

* @return

*/

private int dip2px( float dipValue) {

final float scale = getContext().getResources().getDisplayMetrics().density;

return (int) (dipValue * scale + 0.5f);

}

public DrawLineChart(Context context) {

super(context);

initPaint();

}

public DrawLineChart(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

initPaint();

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

mViewHeight = getMeasuredHeight();

mViewWidth = getMeasuredWidth();

/**计算总值*/

calculateValue=maxVlaue-minValue;

initNeedDrawWidthAndHeight();

/**计算框线横线间隔的数据平均值*/

averageValue = calculateValue/(numberLine-1);

}

/**初始化绘制折线图的宽高*/

private void initNeedDrawWidthAndHeight(){

mNeedDrawWidth = mViewWidth-mBrokenLineLeft-mBrokenLinerRight;

mNeedDrawHeight = mViewHeight-mBrokenLineTop-mBrokenLineBottom;

}

/**初始化画笔*/

private void initPaint() {

/**初始化边框文本画笔*/

if(mTextPaint==null){

mTextPaint=new Paint();

initPaint(mTextPaint);

}

mTextPaint.setTextSize(mBorderTextSize);

mTextPaint.setTextAlign(Paint.Align.RIGHT);

mTextPaint.setColor(Color.GRAY);

/**初始化边框线画笔*/

if(mBorderLinePaint==null){

mBorderLinePaint=new Paint();

initPaint(mBorderLinePaint);

}

mBorderLinePaint.setTextSize(mBorderTextSize);

mBorderLinePaint.setStrokeWidth(mBorderWidth);

mBorderLinePaint.setColor(mBorderLineColor);

/**初始化折线画笔*/

if(mBrokenLinePaint==null){

mBrokenLinePaint=new Paint();

initPaint(mBrokenLinePaint);

}

mBrokenLinePaint.setStrokeWidth(mBrokenLineWidth);

mBrokenLinePaint.setColor(mBrokenLineColor);

if(mCirclePaint==null){

mCirclePaint=new Paint();

initPaint(mCirclePaint);

}

mCirclePaint.setStrokeWidth(mCircleWidth);

mCirclePaint.setColor(mCircleColor);

/**折线文本画笔*/

if (mBrokenLineTextPaint == null){

mBrokenLineTextPaint=new Paint();

initPaint(mBrokenLineTextPaint);

}

mBrokenLineTextPaint.setTextAlign(Paint.Align.CENTER);

mBrokenLineTextPaint.setColor(mBrokenLineTextColor);

mBrokenLineTextPaint.setTextSize(mBrokenLineTextSize);

/**横线画笔*/

if (mHorizontalLinePaint == null){

mHorizontalLinePaint=new Paint();

initPaint(mHorizontalLinePaint);

}

mHorizontalLinePaint.setStrokeWidth(mBorderTransverseLineWidth);

mHorizontalLinePaint.setColor(mBorderTransverseLineColor);

}

/**初始化画笔默认属性*/

private void initPaint(Paint paint){

paint.setAntiAlias(true);

paint.setStyle(Paint.Style.STROKE);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

mPoints = getPoints(value,mNeedDrawHeight,mNeedDrawWidth,calculateValue,minValue,mBrokenLineLeft,mBrokenLineTop);

/**绘制边框线和边框文本*/

DrawBorderLineAndText(canvas);

/**根据数据绘制线*/

DrawLine(canvas);

/**绘制圆*/

DrawLineCircle(canvas);

}

/**绘制线上的圆*/

public void DrawLineCircle(Canvas canvas) {

for (int i = 0; i

Point point=mPoints[i];

mCirclePaint.setColor(Color.WHITE);

mCirclePaint.setStyle(Paint.Style.FILL);

canvas.drawCircle(point.x,point.y,radius,mCirclePaint);

mCirclePaint.setColor(mCircleColor);

mCirclePaint.setStyle(Paint.Style.STROKE);

/**

* drawCircle(float cx, float cy, float radius, Paint paint)

* cx 中间x坐标

* xy 中间y坐标

* radius 圆的半径

* paint 绘制圆的画笔

* */

canvas.drawCircle(point.x,point.y,radius,mCirclePaint);

}

}

/**根据值绘制折线*/

public void DrawLine(Canvas canvas) {

Path mPath=new Path();

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

Point point=mPoints[i];

if(i==0){

mPath.moveTo(point.x,point.y);

}else {

mPath.lineTo(point.x,point.y);

}

canvas.drawText(value[i]+"",point.x,point.y-radius,mBrokenLineTextPaint);

}

canvas.drawPath(mPath,mBrokenLinePaint);

}

/**绘制边框线和边框文本*/

private void DrawBorderLineAndText(Canvas canvas) {

/**对应的属性

* drawLine(float startX, float startY, float stopX, float stopY, Paint paint);

* startX 开始的x坐标

* startY 开始的y坐标

* stopX 结束的x坐标

* stopY 结束的y坐标

* paint 绘制该线的画笔

* */

/**绘制边框竖线*/

canvas.drawLine(mBrokenLineLeft,mBrokenLineTop-dip2px(5),mBrokenLineLeft,mViewHeight-mBrokenLineBottom,mBorderLinePaint);

/**绘制边框横线*/

canvas.drawLine(mBrokenLineLeft,mViewHeight-mBrokenLineBottom,mViewWidth,mViewHeight-mBrokenLineBottom,mBorderLinePaint);

/**绘制边框分段横线与分段文本*/

float averageHeight=mNeedDrawHeight/(numberLine-1);

for (int i = 0; i < numberLine; i++) {

float nowadayHeight= averageHeight*i;

float v=averageValue*(numberLine-1-i)+minValue;

/**最后横线无需绘制,否则会将边框横线覆盖*/

if(i!=numberLine-1) {

canvas.drawLine(mBrokenLineLeft, nowadayHeight + mBrokenLineTop, mViewWidth, nowadayHeight + mBrokenLineTop, mHorizontalLinePaint);

}

canvas.drawText(floatKeepTwoDecimalPlaces(v)+"",mBrokenLineLeft-dip2px(2),nowadayHeight+mBrokenLineTop,mTextPaint);

}

/**竖线*/

for (int i = 1; i < mPoints.length; i++) {

//canvas.drawLine(mPoints[i].x,mBrok enLineTop,mPoints[i].x,mBrokenLineTop+mNeedDrawHeight,mBorderLinePaint);

}

}

/**保留2位小数*/

private String floatKeepTwoDecimalPlaces(float f){

DecimalFormat decimalFormat=new DecimalFormat("0.00");//构造方法的字符格式这里如果小数不足2位,会以0补足.

String p=decimalFormat.format(f);

return p;

}

/**根据值计算在该值的 x,y坐标*/

public Point[] getPoints(float[] values, float height, float width, float max ,float min, float left,float top) {

float leftPading = width / (values.length-1);//绘制边距

Point[] points = new Point[values.length];

for (int i = 0; i < values.length; i++) {

double value = values[i]-min;

//计算每点高度所以对应的值

double mean = (double) max/height;

//获取要绘制的高度

float drawHeight = (float) (value / mean);

int pointY = (int) (height+top - drawHeight);

int pointX = (int) (leftPading * i + left);

Point point = new Point(pointX, pointY);

points[i] = point;

}

return points;

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值