Android 曲線圖
Paste_Image.png
這里我引用了上回介紹的 折線圖
首先我們看看onDrwa 里面做了什么 從圖里面我們可看到 繪制的框線方法懦窘,繪制data線方法 繪制線上的圓
從最上面的圖效果來看我們不需要圓 也不需要折線尸执,但是框線要保留 因此我們需要 把
這個幾個方法 改成public 蠕蚜,到時候繼承之后重寫他 不調(diào)用父方法即可
Paste_Image.png
Paste_Image.png
接下來我們創(chuàng)建一個 類繼承 DrawLineChart 重寫 DrawLineCircle矫户,DrawLine 方法
這樣我們就保留 了框線爽锥, 取消了圓和折線的繪制
接下來在DrawLine 里面繪制曲線
<code>
@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)绪颖。如果沒有移至()調(diào)用
*為這個輪廓,第一點是自動設(shè)置為(0,0)锚贱。
*
* @param x1的坐標(biāo)1立方曲線控制點
* @param y1第一控制點的坐標(biāo)一立方曲線
* @param x2上第二個控制點的坐標(biāo)一立方曲線
* @param y2第二控制點的坐標(biāo)一立方曲線
* @param x3的坐標(biāo)三次曲線的終點
* @param y3終點坐標(biāo)的三次曲線
*
*/
mPath.cubicTo(p3.x, p3.y, p4.x, p4.y, endp.x, endp.y);
}
}
/**這里直接使用了折線的畫筆,你也可以在此類在定義一個畫筆*/
canvas.drawPath(mPath,getBrokenLinePaint());
}
</code>
什么吆寨, 你不光要曲線赏淌,。 還要填充式的怎么辦啄清,這個時候你就需要將你的曲線連接起來 起點到終點連接起來 將畫筆Style 改為FILL 六水,怎么連? 看圖辣卒,從圖上 我們可以看到
- 終點x,終點y 連接到 終點x,底部y
- 終點x,底部y 連接到 起點x,底部y
- 起點x 底部y 連接到 起點x,起點y
Paste_Image.png
- 根據(jù)上面的分析 我們代碼 就是這樣的了
<code>
@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)掷贾。如果沒有移至()調(diào)用
*為這個輪廓,第一點是自動設(shè)置為(0,0)。
*
* @param x1的坐標(biāo)1立方曲線控制點
* @param y1第一控制點的坐標(biāo)一立方曲線
* @param x2上第二個控制點的坐標(biāo)一立方曲線
* @param y2第二控制點的坐標(biāo)一立方曲線
* @param x3的坐標(biāo)三次曲線的終點
* @param y3終點坐標(biāo)的三次曲線
*
*/
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());
}
</code>
- 效果圖
Paste_Image.png
完整代碼
<code>
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)荣茫。如果沒有移至()調(diào)用
*為這個輪廓,第一點是自動設(shè)置為(0,0)想帅。
*
* @param x1的坐標(biāo)1立方曲線控制點
* @param y1第一控制點的坐標(biāo)一立方曲線
* @param x2上第二個控制點的坐標(biāo)一立方曲線
* @param y2第二控制點的坐標(biāo)一立方曲線
* @param x3的坐標(biāo)三次曲線的終點
* @param y3終點坐標(biāo)的三次曲線
*
*/
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());
}
}
</code>
Activity 調(diào)用
Paste_Image.png
DrawLineChart 代碼
<code>
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;
/**數(shù)據(jù)值*/
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;
/**橫線數(shù)量*/
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坐標(biāo)*/
public Point[] mPoints;
/**圓的半徑*/
public void setRadius(float radius) {
this.radius = dip2px(radius);
}
/**設(shè)置寬度*/
public void setCircleWidth(float circleWidth){
this.mCircleWidth=dip2px(circleWidth);
}
/**設(shè)置邊框左上右下邊距*/
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;
}
/**數(shù)據(jù)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;
}
/**圖表橫線數(shù)量*/
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;
}
/**邊框內(nèi)折線顏色*/
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值轉(zhuǎn)換為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();
/**計算框線橫線間隔的數(shù)據(jù)平均值*/
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);
}
/**初始化畫筆默認(rèn)屬性*/
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);
/**根據(jù)數(shù)據(jù)繪制線*/
DrawLine(canvas);
/**繪制圓*/
DrawLineCircle(canvas);
}
/**繪制線上的圓*/
public void DrawLineCircle(Canvas canvas) {
for (int i = 0; i <mPoints.length ; 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坐標(biāo)
* xy 中間y坐標(biāo)
* radius 圓的半徑
* paint 繪制圓的畫筆
* */
canvas.drawCircle(point.x,point.y,radius,mCirclePaint);
}
}
/**根據(jù)值繪制折線*/
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) {
/**對應(yīng)的屬性
* drawLine(float startX, float startY, float stopX, float stopY, Paint paint);
* startX 開始的x坐標(biāo)
* startY 開始的y坐標(biāo)
* stopX 結(jié)束的x坐標(biāo)
* stopY 結(jié)束的y坐標(biāo)
* 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位小數(shù)*/
private String floatKeepTwoDecimalPlaces(float f){
DecimalFormat decimalFormat=new DecimalFormat("0.00");//構(gòu)造方法的字符格式這里如果小數(shù)不足2位,會以0補足.
String p=decimalFormat.format(f);
return p;
}
/**根據(jù)值計算在該值的 x旨剥,y坐標(biāo)*/
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;
//計算每點高度所以對應(yīng)的值
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;
}
}
</code>