效果图:
image image设计图标注:
image1.自定义折线图LineChartView的属性,可以参照上面给出的标注图:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="LineChartView">
<attr name="lineColor" format="color"></attr><!--折线的颜色-->
<attr name="dividerCount" format="integer"></attr><!--纵轴分割数量-->
<attr name="xInterval" format="dimension"></attr><!--X轴每个刻度的间距间距-->
<attr name="leftInterval" format="dimension"></attr><!-- Y轴距离view长度-->
<attr name="bottomInterval" format="dimension"></attr><!--X轴距离view底部的高度-->
<attr name="topInterval" format="dimension"></attr><!--X轴距离view顶部长度-->
<attr name="yAxisFontSize" format="dimension"></attr><!--Y轴字体的大小-->
</declare-styleable>
</resources>
2.在布局文件使用自定义的属性值画图,自定义一个LineChartView嵌套在HorizontalScrollView下面,让LineChartView可以左右滑动;
<?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的代码,在构造函数里获取自定义的属性值:
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轴每个刻度的间距间距
case R.styleable.LineChartView_dividerCount:
dividerCount = a.getInt(index, 5);
break;
// X轴每个刻度的间距间距
case R.styleable.LineChartView_xInterval:
mxInterval = a.getDimensionPixelSize(index, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()));
break;
// Y轴距离view长度
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顶部长度
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:
// 默认设置为16sp,TypeValue也可以把sp转化为px
mYAxisFontSize = a.getDimensionPixelSize(index, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;
}
}
a.recycle();
}
4.然后在构造函数调用init()方法,初始化各种画笔,注意一点的是,折线与折线之间的交点,为了达到线与圆之间有间隙,圆心有空隙,我是用三个画笔来画这圆的.
private void init(Context context){
// 画坐标线的轴
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);
// 小圆点内环
innerCirclePaint = new Paint();
innerCirclePaint.setStyle(Paint.Style.FILL);
innerCirclePaint.setAntiAlias(true);
innerCirclePaint.setColor(Color.WHITE);
innerCirclePaint.setStrokeWidth(UtilApp.dip2px(context,2));
// 小圆点中间环
middleCiclePaint = new Paint();
middleCiclePaint.setStyle(Paint.Style.STROKE);
middleCiclePaint.setAntiAlias(true);
middleCiclePaint.setColor(mLineColor);
middleCiclePaint.setStrokeWidth(UtilApp.dip2px(context,2));
// 小圆点外环
outterCiclePaint = new Paint();
outterCiclePaint.setStyle(Paint.Style.STROKE);
outterCiclePaint.setAntiAlias(true);
outterCiclePaint.setColor(Color.WHITE);
outterCiclePaint.setStrokeWidth(UtilApp.dip2px(context,2));
// 折线路径
mpolylinePath = new Path();
//小圆点内环半径
innerCircleRadius = UtilApp.dip2px(context,3);
//小圆点中间环半径
middleRadius = UtilApp.dip2px(context,4);
//小圆点外环半径
outerRadius = UtilApp.dip2px(context,6);
}
5.在onMeasure()方法根据传入X轴值的数量算出折线图的宽( mWidth = mxInterval(mXAxis.size()-1) + mLeftInterval2;),折线图的高根据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;
}
//宽度通过数组长度计算
mWidth = mxInterval*(mXAxis.size()-1) + mLeftInterval*2;
setMeasuredDimension(mWidth, mHeight);
}
6.在onLayout()方法根据要划分Y轴的数量,算出Y轴每个刻度之间的数量值:
@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坐标轴,折线和交点.
@Override
protected void onDraw(Canvas canvas) {
if(mXAxis.size() ==0 || mYAxis.size()==0){
Log.e(TAG,"数据异常");
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); // 设置坐标值的颜色
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设置好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);
}
代码地址:https://github.com/michael19900101/LineChart.git
转载自:
作者:mcaotuman
链接:https://www.jianshu.com/p/8679ba7f0f0f
网友评论