转发请注明出处:http://blog.csdn.net/qq_30552993/article/details/51546919
这里也给出后续的两篇文章
最近公司想做一个表格统计图,而且要符合可以多条折线的那种,自己也是翻阅了好多资料也没有找到很合适的一种,后来果断决定还是自己定义一个咯,下面先附上效果图.
这里有三条折线,其实里面是有两个列表:一个是管颜色的,另一个是管折线上的数据.同时这里的控件外面也包了一个HorizontalScrollView,当超过屏幕时就可以进行水平滚动.而且这里的控件LineView也提供了很过设置属性的方法.
转发请注明出处: http://blog.csdn.net/qq_30552993/article/details/51546919
其实在这里之前也有很多好有的图表框架,这里我列举几个在github上面比较好的:
MPAndroidChart: https://github.com/PhilJay/MPAndroidChart
hellocharts-android: https://github.com/lecho/hellocharts-android
XCL-Charts: https://github.com/xcltapestry/XCL-Charts
Android-Charts: https://github.com/limccn/Android-Charts
那这里或许有些人就奇怪了,那你都说这里的框架好用,那你为什么不用.其实东西好用可是也有不合适自己的对吧!所以自己就自定义一个咯,就是这么任性.
LineView.java
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.util.AttributeSet;
import android.view.View;
import java.util.ArrayList;
@SuppressLint("DrawAllocation")
public class LineView extends View {
private static final String TAG = "LineView";
// 默认边距
private int Margin = 40;
// 原点坐标
private int Xpoint;
private int Ypoint;
// X,Y轴的单位长度,即表格中的正方形的宽高
private int Yscale = 32;
private int Xscale = Yscale * 2;
//是否显示表格
private boolean isShowGrid = false;
//是否展示表格为虚线
private boolean isDottedLine = false;
// X,Y轴 线的颜色
private int XYColor = Color.WHITE;
// X轴字体的颜色
private int XTextColor = Color.WHITE;
// Y轴字体的颜色
private int YTextColor = Color.WHITE;
// 表格的颜色
private int GridColor = Color.WHITE;
// 默认折线数据的颜色
private int DataColor = Color.WHITE;
private int XTextSize = 16;
private int YTextSize = 16;
// X轴上面的显示文字
private ArrayList<String> XLabel;
// Y轴上面的显示文字
private ArrayList<String> YLabel;
private int left = 0; // Y轴与Y轴上的文字的距离
private int bottom = 0; //X轴与X轴上的文字的距离
// 折线数据列表
private ArrayList<ArrayList<Integer>> dataLists;
//折线颜色列表
private ArrayList<Integer> dataColorList;
// 单前数据
private ArrayList<Integer> dataList;
private int dataColor;
public LineView(Context context) {
super(context);
}
public LineView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setXYColor(int XYColor) {
this.XYColor = XYColor;
}
public void setXTextColor(int XTextColor) {
this.XTextColor = XTextColor;
}
public void setYTextColor(int YTextColor) {
this.YTextColor = YTextColor;
}
public void setGridColor(int gridColor) {
this.GridColor = gridColor;
}
public void setDataColor(int dataColor) {
this.DataColor = dataColor;
}
public void setDataColorList(ArrayList<Integer> dataColorList) {
this.dataColorList = dataColorList;
}
public void setXTextSize(int XTextSize) {
this.XTextSize = XTextSize;
}
public void setYTextSize(int YTextSize) {
this.YTextSize = YTextSize;
}
public void setShowGrid(boolean isShowGrid) {
this.isShowGrid = isShowGrid;
}
public void setDottedLine(boolean isDottedLine) {
this.isDottedLine = isDottedLine;
}
public void setXYLabel(ArrayList<String> xlabel, ArrayList<String> ylabel) {
this.XLabel = xlabel;
this.YLabel = ylabel;
}
public void setScale(int scale) {
this.Yscale = scale;
this.Xscale = this.Yscale * 2;
}
public void setYToYTextSpace(int left) {
this.left = left;
}
public void setXToXTextSpace(int bottom) {
this.bottom = bottom;
}
public void setDataList(ArrayList<ArrayList<Integer>> dataLists) {
this.dataLists = dataLists;
postInvalidate();
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (Yscale <= 24) {
setMeasuredDimension(Xscale * (XLabel.size() + 2) + left,
Yscale * (YLabel.size() + 2) + bottom);
} else {
setMeasuredDimension(Xscale * (XLabel.size() + 1) + left,
Yscale * (YLabel.size() + 1) + bottom);
}
}
// 初始化数据值
public void init() {
Xpoint = this.Margin + left;
Ypoint = this.getHeight() - this.Margin - bottom;
/*Xscale = (this.getWidth() - 2 * this.Margin) / (this.XLabel.size());
Yscale = (this.getHeight() - 2 * this.Margin) / (this.YLabel.size() - 1);*/
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
Paint p1 = new Paint();
p1.setStyle(Paint.Style.STROKE);
p1.setAntiAlias(true);
p1.setColor(XYColor);
p1.setStrokeWidth(2);
init();
if (isShowGrid) {
this.drawTable(canvas);
} else {
this.drawXLine(canvas, p1);
this.drawYLine(canvas, p1);
}
if (dataLists != null) {
for (int i = 0; i < dataLists.size(); i++) {
this.drawData(canvas, i);
}
}
}
// 画表格
private void drawTable(Canvas canvas) {
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(GridColor);
Path path = new Path();
if (isDottedLine) {
PathEffect effects = new DashPathEffect(new float[]{5, 5, 5, 5}, 1);//画虚线
paint.setPathEffect(effects);
}
int startX = 0;
int startY = 0;
int stopX = 0;
int stopY = 0;
// 纵向线
for (int i = 0; i <= XLabel.size(); i++) {
startX = Xpoint + i * Xscale;
startY = Ypoint;
stopY = Ypoint - (this.YLabel.size() - 1) * Yscale;
if (i != 0) {
path.moveTo(startX - Xscale / 2, startY);
path.lineTo(startX - Xscale / 2, stopY);
canvas.drawPath(path, paint);
}
path.moveTo(startX, startY);
path.lineTo(startX, stopY);
canvas.drawPath(path, paint);
}
// 横向线
for (int i = 0; i < YLabel.size(); i++) {
startX = Xpoint;
startY = Ypoint - i * Yscale;
stopX = Xpoint + (this.XLabel.size()) * Xscale;
path.moveTo(startX, startY);
path.lineTo(stopX, startY);
canvas.drawPath(path, paint);
}
}
//画纵轴
private void drawXLine(Canvas canvas, Paint p) {
p.setColor(XYColor);
float stopX = Xpoint;
float stopY = Ypoint - Yscale * (YLabel.size() - 1);
canvas.drawLine(Xpoint, Ypoint, stopX, stopY, p);
// Y轴最后是否有箭头
canvas.drawLine(stopX, stopY, stopX - Xscale / 6, stopY + Yscale / 3, p);
canvas.drawLine(stopX, stopY, stopX + Xscale / 6, stopY + Yscale / 3, p);
}
// 画横轴
private void drawYLine(Canvas canvas, Paint p) {
p.setColor(XYColor);
float stopX = Xpoint + Xscale * XLabel.size();
float stopY = Ypoint;
canvas.drawLine(Xpoint, Ypoint, stopX, stopY, p);
// X轴最后是否有箭头
canvas.drawLine(stopX, stopY, stopX - Xscale / 6, stopY - Yscale / 3, p);
canvas.drawLine(stopX, stopY, stopX - Xscale / 6, stopY + Yscale / 3, p);
}
// 画数据
private void drawData(Canvas canvas, int pos) {
dataList = dataLists.get(pos);
dataColor = pos < dataColorList.size() ? dataColorList.get(pos) : DataColor;
Paint p = new Paint();
p.setAntiAlias(true);
// 纵轴数据
for (int i = 0; i < YLabel.size(); i++) {
int startY = Ypoint - i * Yscale;
p.setColor(YTextColor);
p.setTextSize(XTextSize);
canvas.drawText(this.YLabel.get(i), this.Margin / 4,
startY + this.Margin / 4, p);
}
//横轴数据
for (int i = 0; i < XLabel.size(); i++) {
int startX = Xpoint + i * Xscale;
p.setColor(XTextColor);
p.setTextSize(YTextSize);
canvas.drawText(this.XLabel.get(i), startX - this.Margin / 4 + Xscale / 2,
this.getHeight() - this.Margin / 4, p);
p.setColor(dataColorList.size() > 0 ? dataColor : DataColor);
canvas.drawCircle(startX + Xscale / 2, calY(dataList.get(i)), 4, p);
if (i < XLabel.size() - 1) {
canvas.drawLine(startX + Xscale / 2, calY(dataList.get(i)),
Xpoint + (i + 1) * Xscale + Xscale / 2,
calY(dataList.get(i + 1)), p);
}
}
}
/**
* @param y
* @return
*/
private int calY(int y) {
int y0 = 0;
int y1 = 0;
// Log.i("zzzz", "y:"+y);
try {
y0 = Integer.parseInt(YLabel.get(0));
// Log.i("zzzz", "y0"+y0);
y1 = Integer.parseInt(YLabel.get(1));
// Log.i("zzzz","y1"+y1);
} catch (Exception e) {
// Log.i("zzzz", "string changed is err");
return 0;
}
try {
// Log.i("zzzz", "返回数据"+(Ypoint-(y-y0)*Yscale/(y1-y0)) );
return Ypoint - ((y - y0) * Yscale / (y1 - y0));
} catch (Exception e) {
// Log.i("zzzz", "return is err");
return 0;
}
}
}
这里先来一个列子,说明怎么使用LineView
MainActivity.java
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import java.util.ArrayList;
public class MainActivity extends Activity {
private LineView mSkinLineView;
private ArrayList<Integer> colorList = new ArrayList<>();//折线的颜色列表
private ArrayList<String> XLabel = new ArrayList<>(); //X轴上的标签数据列表
private ArrayList<String> YLabel = new ArrayList<>(); //Y轴上的标签数据列表
private ArrayList<ArrayList<Integer>> dataLists;//折线上的数据列表
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
}
private void initViews() {
colorList.add(getResources().getColor(R.color.using_before));
colorList.add(getResources().getColor(R.color.using_after));
for (int i = 0; i < 10; i++) {
XLabel.add(String.valueOf(i));
}
for (int i = 0; i < 11; i++) {
YLabel.add(String.valueOf(i * 10));
}
mSkinLineView = (LineView) findViewById(R.id.skinLineView);
mSkinLineView.setDataColorList(colorList);
mSkinLineView.setXYLabel(XLabel, YLabel); //设置设置X,Y轴的数据
mSkinLineView.setShowGrid(true);//true是展示表格,false是显示带箭头的X,Y轴
// mSkinLineView.setDottedLine(true); // 表格展示微虚线
mSkinLineView.setXYColor(Color.LTGRAY); // X,Y轴线与数据的颜色
mSkinLineView.setGridColor(Color.LTGRAY);// 表格的颜色
/* mSkinLineView.setScale(32);//表格中的正方形的单位,默认是32
mSkinLineView.setXTextSize(30); // X轴字体的颜色
mSkinLineView.setYTextSize(30); // Y轴字体的颜色
mSkinLineView.setXToXTextSpace(20);
mSkinLineView.setYToYTextSpace(40);
mSkinLineView.setXTextColor(Color.RED);
mSkinLineView.setYTextColor(Color.RED);
mSkinLineView.setDataColor(Color.BLUE);*/
randSet();
findViewById(R.id.resetBtn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
randSet();
}
});
}
private void randSet() {
dataLists = new ArrayList<>();
ArrayList<Integer> dataList1 = new ArrayList<Integer>();
int random1 = (int) (Math.random() * 99 + 1);
for (int i = 0; i < XLabel.size(); i++) {
dataList1.add((int) (Math.random() * random1));
}
ArrayList<Integer> dataList2 = new ArrayList<Integer>();
int random2 = (int) (Math.random() * 99 + 1);
for (int i = 0; i < XLabel.size(); i++) {
dataList2.add((int) (Math.random() * random2));
}
ArrayList<Integer> dataList3 = new ArrayList<Integer>();
int random3 = (int) (Math.random() * 99 + 1);
for (int i = 0; i < XLabel.size(); i++) {
dataList3.add((int) (Math.random() * random3));
}
dataLists.add(dataList1);
dataLists.add(dataList2);
dataLists.add(dataList3);
mSkinLineView.setDataList(dataLists);
}
}
布局文件activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0099FF"
android:orientation="vertical">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<sl.myapplication.LineView
android:id="@+id/skinLineView"
android:layout_width="match_parent"
android:layout_height="200dip" />
</LinearLayout>
</HorizontalScrollView>
<Button
android:id="@+id/resetBtn"
android:text="刷新"
android:textColor="@color/white"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
上面设计到的颜色XML,在res-->values的目录下添加colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="white">#ffffff</color>
<color name="using_before">#f6e749</color>
<color name="using_after">#7ae6fe</color>
</resources>