效果圖:

2017-04-01_11_36_38~1.gif

device-2017-04-01-101715.png
設(shè)計(jì)圖標(biāo)注:

biaozhu.png
1.自定義折線圖LineChartView的屬性,可以參照上面給出的標(biāo)注圖:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="LineChartView">
<attr name="lineColor" format="color"></attr><!--折線的顏色-->
<attr name="dividerCount" format="integer"></attr><!--縱軸分割數(shù)量-->
<attr name="xInterval" format="dimension"></attr><!--X軸每個(gè)刻度的間距間距-->
<attr name="leftInterval" format="dimension"></attr><!-- Y軸距離view長(zhǎng)度-->
<attr name="bottomInterval" format="dimension"></attr><!--X軸距離view底部的高度-->
<attr name="topInterval" format="dimension"></attr><!--X軸距離view頂部長(zhǎng)度-->
<attr name="yAxisFontSize" format="dimension"></attr><!--Y軸字體的大小-->
</declare-styleable>
</resources>
2.在布局文件使用自定義的屬性值畫圖,自定義一個(gè)LineChartView嵌套在HorizontalScrollView下面,讓LineChartView可以左右滑動(dòng);
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:linchart="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
android:scrollbars="none"
android:layout_marginTop="50dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.aotuman.linechart.LineChartView
android:id="@+id/lineChart"
android:layout_width="match_parent"
android:layout_height="200dp"
linchart:lineColor="@color/colorLine"
linchart:dividerCount="5"
linchart:xInterval="70dp"
linchart:leftInterval="20dp"
linchart:bottomInterval="20dp"
linchart:topInterval="40dp"
linchart:yAxisFontSize="14sp"/>
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
3.看一下LineChartView的代碼,在構(gòu)造函數(shù)里獲取自定義的屬性值:
public LineChartView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
/**
* 獲得我們所定義的自定義樣式屬性
*/
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.LineChartView, defStyleAttr, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++){
int index = a.getIndex(i);
switch (index)
{
// 折線顏色
case R.styleable.LineChartView_lineColor:
mLineColor = a.getColor(index, Color.BLACK);
break;
// X軸每個(gè)刻度的間距間距
case R.styleable.LineChartView_dividerCount:
dividerCount = a.getInt(index, 5);
break;
// X軸每個(gè)刻度的間距間距
case R.styleable.LineChartView_xInterval:
mxInterval = a.getDimensionPixelSize(index, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()));
break;
// Y軸距離view長(zhǎng)度
case R.styleable.LineChartView_leftInterval:
mLeftInterval = a.getDimensionPixelSize(index, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()));
break;
// X軸距離view底部的高度
case R.styleable.LineChartView_bottomInterval:
mBottomInterval = a.getDimensionPixelSize(index, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()));
break;
// X軸距離view頂部長(zhǎng)度
case R.styleable.LineChartView_topInterval:
mTopInterval = a.getDimensionPixelSize(index, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()));
break;
// Y軸字體的大小
case R.styleable.LineChartView_yAxisFontSize:
// 默認(rèn)設(shè)置為16sp,TypeValue也可以把sp轉(zhuǎn)化為px
mYAxisFontSize = a.getDimensionPixelSize(index, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;
}
}
a.recycle();
}
4.然后在構(gòu)造函數(shù)調(diào)用init()方法,初始化各種畫筆,注意一點(diǎn)的是,折線與折線之間的交點(diǎn),為了達(dá)到線與圓之間有間隙,圓心有空隙,我是用三個(gè)畫筆來(lái)畫這圓的.
private void init(Context context){
// 畫坐標(biāo)線的軸
axisPaint = new Paint();
axisPaint.setTextSize(mYAxisFontSize);
axisPaint.setColor(Color.parseColor("#D9D9D9"));
// 畫X軸文字
axisTextPaint = new Paint();
axisTextPaint.setTextSize(mYAxisFontSize);
axisTextPaint.setColor(Color.parseColor("#878787"));
// 連接線條
linePaint = new Paint();
linePaint.setColor(mLineColor);
linePaint.setAntiAlias(true);
linePaint.setStyle(Paint.Style.STROKE);
linePaint.setStrokeWidth(mStrokeWidth);
// 小圓點(diǎn)內(nèi)環(huán)
innerCirclePaint = new Paint();
innerCirclePaint.setStyle(Paint.Style.FILL);
innerCirclePaint.setAntiAlias(true);
innerCirclePaint.setColor(Color.WHITE);
innerCirclePaint.setStrokeWidth(UtilApp.dip2px(context,2));
// 小圓點(diǎn)中間環(huán)
middleCiclePaint = new Paint();
middleCiclePaint.setStyle(Paint.Style.STROKE);
middleCiclePaint.setAntiAlias(true);
middleCiclePaint.setColor(mLineColor);
middleCiclePaint.setStrokeWidth(UtilApp.dip2px(context,2));
// 小圓點(diǎn)外環(huán)
outterCiclePaint = new Paint();
outterCiclePaint.setStyle(Paint.Style.STROKE);
outterCiclePaint.setAntiAlias(true);
outterCiclePaint.setColor(Color.WHITE);
outterCiclePaint.setStrokeWidth(UtilApp.dip2px(context,2));
// 折線路徑
mpolylinePath = new Path();
//小圓點(diǎn)內(nèi)環(huán)半徑
innerCircleRadius = UtilApp.dip2px(context,3);
//小圓點(diǎn)中間環(huán)半徑
middleRadius = UtilApp.dip2px(context,4);
//小圓點(diǎn)外環(huán)半徑
outerRadius = UtilApp.dip2px(context,6);
}
5.在onMeasure()方法根據(jù)傳入X軸值的數(shù)量算出折線圖的寬( mWidth = mxInterval(mXAxis.size()-1) + mLeftInterval2;),折線圖的高根據(jù)XML的定義好的高度獲取.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
Log.d(TAG,"widthSize:"+widthSize+",heightSize:"+heightSize );
mHeight =heightSize;
if(mXAxis == null){
Log.d(TAG,"mWidth:"+mWidth+",mHeight:"+mHeight +"mXAxis:"+mXAxis);
return;
}
//寬度通過(guò)數(shù)組長(zhǎng)度計(jì)算
mWidth = mxInterval*(mXAxis.size()-1) + mLeftInterval*2;
setMeasuredDimension(mWidth, mHeight);
}
6.在onLayout()方法根據(jù)要?jiǎng)澐諽軸的數(shù)量,算出Y軸每個(gè)刻度之間的數(shù)量值:
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
myInterval = (getHeight()-mBottomInterval-mTopInterval)/dividerCount;
}
7.在onDraw()方法分別畫X,Y坐標(biāo)軸,折線和交點(diǎn).
@Override
protected void onDraw(Canvas canvas) {
if(mXAxis.size() ==0 || mYAxis.size()==0){
Log.e(TAG,"數(shù)據(jù)異常");
return;
}
// 畫橫線
for(int i = 0;i <= dividerCount;i++){
canvas.drawLine(mLeftInterval, mHeight - mBottomInterval - i * myInterval, (mXAxis.size()-1)*mxInterval+ mLeftInterval,
mHeight - mBottomInterval - myInterval * i, axisPaint);
}
// x軸的刻度集合
int[] xPoints = new int[mXAxis.size()];
for (int i = 0; i < mXAxis.size(); i++) {
float xTextWidth = axisPaint.measureText(mXAxis.get(i))/2 ; //文字寬度一半
float xfloat = i * mxInterval + mLeftInterval - xTextWidth;
// 畫X軸的文字
canvas.drawText(mXAxis.get(i), xfloat, mHeight - mBottomInterval + mYAxisFontSize, axisTextPaint);
xPoints[i] = (int) (xfloat+xTextWidth);
// 畫豎線
float xvfloat = i * mxInterval + mLeftInterval;
canvas.drawLine(xvfloat,mHeight - mBottomInterval, xvfloat,
mHeight - mBottomInterval - myInterval*dividerCount, axisPaint);
}
/**
* 畫軌跡
*/
int y = myInterval * (dividerCount - 1); // 只拿縱軸的dividerCount-1/dividerCount畫圖
axisPaint.setColor(mLineColor); // 設(shè)置坐標(biāo)值的顏色
for (int i = 0;i<mYAxis.size();i++){
int h = mHeight - (mBottomInterval + y * mYAxis.get(i)/ maxYValue);
float textWidth = axisPaint.measureText(String.valueOf(mYAxis.get(i)))/2 ; //文字寬度一半
if (i==0){
mpolylinePath.moveTo(mLeftInterval,h);
canvas.drawText(mYAxis.get(i) + "", mLeftInterval - textWidth, h - mYAxisFontSize, axisPaint);
}else{
mpolylinePath.lineTo(mLeftInterval + i*mxInterval,h);
canvas.drawText(mYAxis.get(i) + "", mLeftInterval+i*mxInterval- textWidth, h - mYAxisFontSize, axisPaint);
}
}
canvas.drawPath(mpolylinePath,linePaint);
/**
* 畫小圓圈
*/
for (int i = 0;i<mYAxis.size();i++){
int h = mHeight - (mBottomInterval + y * mYAxis.get(i)/ maxYValue);
if (i==0){
canvas.drawCircle(mLeftInterval,h,innerCircleRadius,innerCirclePaint);
canvas.drawCircle(mLeftInterval,h,middleRadius,middleCiclePaint);
canvas.drawCircle(mLeftInterval,h,outerRadius,outterCiclePaint);
}else{
canvas.drawCircle(mLeftInterval + i*mxInterval,h,innerCircleRadius,innerCirclePaint);
canvas.drawCircle(mLeftInterval + i*mxInterval,h,middleRadius,middleCiclePaint);
canvas.drawCircle(mLeftInterval + i*mxInterval,h,outerRadius,outterCiclePaint);
}
}
}
8.在MainActivity設(shè)置好X軸和Y軸的值,并求出Y軸的最大值.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLineChartView = (LineChartView) findViewById(R.id.lineChart);
//X軸
String[] xItem = {"4/1","4/2","4/3","4/4","4/5","4/6","4/7","4/8","4/9","4/10","4/11","4/12",
"4/13","4/14","4/15","4/16","4/17","4/18","4/19","4/20","4/21","4/22","4/23","4/24"
,"4/25","4/26","4/27","4/28","4/29","4/30"};
ArrayList xItemArray = new ArrayList();
for (int i = 0; i < xItem.length; i++) {
xItemArray.add(xItem[i]);
}
//Y軸
int[] yItem = {3,7,19,7,20,19,27,8,18,19,21,20,19,20,8,18,19,21,20,22,21,24,26,24,20,22,21,24,26,24};
ArrayList<Integer> yItemArray = new ArrayList<>();
for (int i = 0; i < yItem.length; i++) {
yItemArray.add(yItem[i]);
}
int yMax = findMax(yItem);
mLineChartView.setXItem(xItemArray);
mLineChartView.setYItem(yItemArray);
mLineChartView.setMaxYValue(yMax);
}