近期,需要做一个折线统计图,来显示相关数据,下面是代码,分享给大家:
xml视图文件:在哪里使用就加进去即可
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
// LineChartView为自己本地路径
<view.linechart.LineChartView
android:id="@+id/lineChart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
linchart:lineColor="@color/color_d4"
linchart:dividerCount="5"
linchart:xInterval="70dp"
linchart:leftInterval="20dp"
linchart:bottomInterval="20dp"
linchart:topInterval="40dp"
linchart:yAxisFontSize="14sp"/>
</HorizontalScrollView>
因为要做的数据格式,X轴的数据较多,所以使用了一个横向滚动(有兴趣的朋友可以研究研究负数及竖向滚动)
LineChartView类:
public class LineChartView extends View{
private static final String TAG = "LineChartView";
//Y轴 每个刻度的间距间距
private int myInterval;
//X轴 每个刻度的间距间距
private int mxInterval;
//Y轴距离view长度
private int mLeftInterval;
//X轴距离view长度
private int mBottomInterval;
//X轴距离view顶部长度
private int mTopInterval;
//Y轴字体的大小
private float mYAxisFontSize;
//View 的宽和高
private int mWidth, mHeight;
//线的颜色
private int mLineColor;
//线条的宽度
private float mStrokeWidth = 4.0f;
//X轴的文字
private ArrayList<String> mXAxis;
//点 (温度)
private ArrayList<Integer> mYAxis;
//纵轴最大值
private int maxYValue;
//纵轴分割数量
private int dividerCount;
//画坐标线的轴
private Paint axisPaint;
//画X轴文字
private Paint axisTextPaint;
//连接线条
private Paint linePaint;
//小圆点内环
private Paint innerCirclePaint;
//小圆点中间环
private Paint middleCiclePaint;
//小圆点外环
private Paint outterCiclePaint;
//折线路径
private Path mpolylinePath;
//小圆点内环半径
private int innerCircleRadius;
//小圆点中间环半径
private int middleRadius;
//小圆点外环半径
private int outerRadius;
public LineChartView(Context context) {
this(context, null);
init(context);
}
public LineChartView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
init(context);
}
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.parseColor("#fbaa56"));
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();
}
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(Color.parseColor("#fbaa56"));
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.parseColor("#fbaa56"));
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.parseColor("#fbaa56"));
outterCiclePaint.setStrokeWidth(UtilApp.dip2px(context,2));
// 折线路径
mpolylinePath = new Path();
//小圆点内环半径
innerCircleRadius = UtilApp.dip2px(context,3);
//小圆点中间环半径
middleRadius = UtilApp.dip2px(context,4);
//小圆点外环半径
outerRadius = UtilApp.dip2px(context,6);
}
@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;
}
@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);
}
@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(Color.parseColor("#fbaa56")); // 设置坐标值的颜色
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);
}
}
}
/**
* 设置Y轴文字
* @param yItem
*/
public void setYItem(ArrayList<Integer> yItem){
mYAxis = yItem;
}
/**
* 设置X轴文字
* @param xItem
*/
public void setXItem(ArrayList xItem){
mXAxis = xItem;
}
public void setMaxYValue(int maxYValue) {
this.maxYValue = maxYValue;
}
}
类中所需的Util :
public class UtilApp {
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
* 获取屏幕的宽度
*/
public static int getScreenWidth(Context context) {
WindowManager manager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
return display.getWidth();
}
/**
* 获取屏幕的高度
*/
public static int getScreenHeight(Context context) {
WindowManager manager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
return display.getHeight();
}
}
然后在需要使用折线图的页面中进行赋值:
protected void initData(Bundle savedInstanceState) {
private ArrayList<String> xdata; // X轴数据
private ArrayList<Integer> ydatas; // Y轴数据
// xdata和ydatas是和Y轴数据,这里现在是由上一个页面出入,也可以自定义
xdata = getIntent().getStringArrayListExtra("xdata");
ydatas = getIntent().getIntegerArrayListExtra("ydatas");
lineChart = (LineChartView) findViewById(R.id.lineChart);
// X轴
ArrayList xItemArray = new ArrayList();
for (int i = 0; i < xdata.size(); i++) {
xItemArray.add(xdata.get(i));
}
// Y轴
ArrayList<Integer> yItemArray = new ArrayList<>();
for (int i = 0; i < ydatas.size(); i++) {
yItemArray.add(ydatas.get(i));
}
int yMax = Collections.max(ydatas);
lineChart.setXItem(xItemArray);
lineChart.setYItem(yItemArray);
lineChart.setMaxYValue(yMax);
}
以上,就可以实现折线图啦!!!
但是,在使用过程中,也发现了一个问题:当我们在对X轴及Y轴赋值的时候,如果数据不是已经给定好的,会报错!
例如,我当时给X轴及Y轴赋值的时候,最初采用的是网络请求获取数据然后赋值到X轴及Y轴中,这就可能会出现折线图绘制完成的时候X轴及Y轴的数据还没有返回,所以会报错!!!
所以,我再给X轴及Y轴赋值的时候,先在上一个页面中获取所需的值,然后在加载折线图的时候可以直接拿到X轴及Y轴的数据。
以上,如有不同见解,请评论区留言!!!