1.废话少说,看效果
2.只需要一个class,即实现ChartView.java
public class ChartView extends View { private float xLength; private float yLength; private float startPointX; private float startPointY; private float xScale; private float yScale; private float coordTextSize; private String[] xLabel; private String[] yLabel; private ArrayList<String[]> data; private String[] title; //private String[] mDataLineColors; private int[] drawColors; private ArrayList<float[][]> mDataCoords = new ArrayList<float[][]>(); private Paint mScaleLinePaint; private Paint mDataLinePaint; private Paint mScaleValuePaint; private Paint mBackColorPaint; Rect bounds = new Rect(); private boolean isClick; // 是否点击了数据点 private int[] clickIndex; //private PopupWindow mPopWin; public ChartView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { this.setBackgroundColor(Color.WHITE); // x轴刻度值 if (xLabel == null) { //获取当前日期 xLabel = new String[7]; long current = System.currentTimeMillis() / 1000; for (int i = 6; i >= 0; i--) { long tmp = current - 24 * 60 * 60 * (i + 1); String dateStr = Utils.longtimeToDayDate(tmp); xLabel[i] = dateStr.substring(5, dateStr.length()); } } // 数据点 if (data == null) { data = new ArrayList<String[]>(); data.add(new String[]{"0", "0", "0", "0", "0", "0", "0"}); } clickIndex = new int[data.size()]; for (int i = 0; i < clickIndex.length; i++) { clickIndex[i] = -1; } // 标题 if (title == null) { title = new String[]{"网点"}; } // 根据设置的数据值生成Y坐标刻度值 yLabel = createYLabel(); //设置折线颜色 if (drawColors == null) { drawColors = new int[1]; drawColors[0] = Color.parseColor("#ff5400"); } // 新建画笔 mDataLinePaint = new Paint(); // 数据(点和连线)画笔 mScaleLinePaint = new Paint(); // 坐标(刻度线条)值画笔 mScaleValuePaint = new Paint(); // 图表(刻度值)画笔 mBackColorPaint = new Paint(); // 背景(色块)画笔 // 画笔抗锯齿 mDataLinePaint.setAntiAlias(true); mScaleLinePaint.setAntiAlias(true); mScaleValuePaint.setAntiAlias(true); mBackColorPaint.setAntiAlias(true); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); initParams(); drawBackColor(canvas); // 绘制背景色块 drawYAxisAndXScaleValue(canvas); // 绘制y轴和x刻度值 drawXAxisAndYScaleValue(canvas); // 绘制x轴和y刻度值 drawDataLines(canvas); // 绘制数据连线 drawDataPoints(canvas); // 绘制数据点 drawTitle(canvas); // 绘制标题 } /** * 绘制背景色块 * * @param canvas */ private void drawBackColor(Canvas canvas) { for (int i = 0; i < 7; i++) { if (i == 2 || i == 4 || i == 6) { canvas.drawRect(startPointX + (i - 1) * xScale, startPointY, startPointX + i * xScale, yLength + startPointY, mBackColorPaint); } } } private void drawYAxisAndXScaleValue(Canvas canvas) { for (int i = 0; i < 7; i++) { canvas.drawLine(startPointX + i * xScale, startPointY, startPointX + i * xScale, startPointY + yLength, mScaleLinePaint); mScaleValuePaint.getTextBounds(xLabel[i], 0, xLabel[i].length(), bounds); canvas.drawText(xLabel[i], startPointX + i * xScale - bounds.width() / 2, startPointY + yLength + bounds.height() + yScale / 15, mScaleValuePaint); } } /** * 绘制x轴和y刻度值 * * @param canvas */ private void drawXAxisAndYScaleValue(Canvas canvas) { for (int i = 0; i < 6; i++) { if (i < 5) { mScaleValuePaint.getTextBounds(yLabel[4 - i], 0, yLabel[4 - i].length(), bounds); canvas.drawText(yLabel[4 - i], startPointX - bounds.width() - 2 * xScale / 15, startPointY + yScale * (i + 0.5f) + bounds.height() / 2, mScaleValuePaint); canvas.drawLine(startPointX, startPointY + (i + 0.5f) * yScale, startPointX + xLength, startPointY + (i + 0.5f) * yScale, mScaleLinePaint); } else { canvas.drawLine(startPointX, startPointY + (i + 0.5f) * yScale, startPointX + xLength, startPointY + (i + 0.5f) * yScale, mScaleLinePaint); } } } /** * 绘制数据线条 * * @param canvas */ private void drawDataLines(Canvas canvas) { getDataRoords(); for (int n = 0; n < data.size(); n++) { for (int i = 0; i < 6; i++) { mDataLinePaint.setColor(drawColors[n]); canvas.drawLine(mDataCoords.get(n)[i][0], mDataCoords.get(n)[i][1], mDataCoords.get(n)[i + 1][0], mDataCoords.get(n)[i + 1][1], mDataLinePaint); canvas.drawCircle(mDataCoords.get(n)[i][0], mDataCoords.get(n)[i][1], xScale / 15, mDataLinePaint); } canvas.drawCircle(mDataCoords.get(n)[6][0], mDataCoords.get(n)[6][1], xScale / 15, mDataLinePaint); } } /** * 绘制数据点 * * @param canvas */ private void drawDataPoints(Canvas canvas) { // 点击后,绘制数据点 for (int n = 0; n < data.size(); n++) { if (isClick && clickIndex[n] > -1) { mDataLinePaint.setColor(drawColors[n]); canvas.drawCircle(mDataCoords.get(n)[clickIndex[n]][0], mDataCoords.get(n)[clickIndex[n]][1], xScale / 10, mDataLinePaint); mDataLinePaint.setColor(Color.WHITE); canvas.drawCircle(mDataCoords.get(n)[clickIndex[n]][0], mDataCoords.get(n)[clickIndex[n]][1], xScale / 20, mDataLinePaint); mDataLinePaint.getTextBounds(data.get(n)[clickIndex[n]], 0, data.get(n)[clickIndex[n]].length(), bounds); //画圆角矩形 mDataLinePaint.setStyle(Paint.Style.FILL);//充满 mDataLinePaint.setColor(drawColors[n]);//设置颜色 // mDataLinePaint.setAntiAlias(true);// 设置画笔的锯齿效果 float width = bounds.height() > bounds.width() ? bounds.height() :bounds.width(); RectF rectF = new RectF( mDataCoords.get(n)[clickIndex[n]][0] - 15 - width / 2, mDataCoords.get(n)[clickIndex[n]][1] - 2 * bounds.height() - 17, mDataCoords.get(n)[clickIndex[n]][0] + 15 + width / 2, mDataCoords.get(n)[clickIndex[n]][1] - 2 * bounds.height() + bounds.height() + 13);// 设置个新的长方形 canvas.drawRoundRect(rectF, rectF.height() / 2, rectF.height() / 2, mDataLinePaint);//第二个参数是x半径,第三个参数是y半径 //绘制数据 mDataLinePaint.setColor(Color.WHITE); canvas.drawText(data.get(n)[clickIndex[n]], mDataCoords.get(n)[clickIndex[n]][0] - bounds.width() / 2 - 2, mDataCoords.get(n)[clickIndex[n]][1] - 1 * bounds.height() - 2, mDataLinePaint); } } } /** * 绘制标题 * * @param canvas */ private void drawTitle(Canvas canvas) { // 绘制标题文本和线条 for (int i = 0; i < data.size(); i++) { //设置绘制标题时的颜色 mDataLinePaint.setColor(drawColors[i]); mDataLinePaint.getTextBounds(title[i], 0, title[i].length(), bounds); canvas.drawText(title[i], startPointX + i * xLength / data.size() + xScale / 2 + xLength / (2 * data.size()) - xScale / 2 - bounds.width() / 2, startPointY + yLength + yScale / 2 + (yScale / 15 + coordTextSize) / 2 + bounds.height() / 2, mDataLinePaint); canvas.drawLine(startPointX + i * xLength / data.size() + xLength / (2 * data.size()) - xScale / 2 - bounds.width() / 2, startPointY + yLength + yScale / 2 + (yScale / 15 + coordTextSize) / 2, startPointX + i * xLength / data.size() + xScale / 2 - xScale / 15 + xLength / (2 * data.size()) - xScale / 2 - bounds.width() / 2, startPointY + yLength + yScale / 2 + (yScale / 15 + coordTextSize) / 2, mDataLinePaint); } } @Override public boolean onTouchEvent(MotionEvent event) { float touchX = event.getX(); float touchY = event.getY(); for (int i = 0; i < data.size(); i++) { clickIndex[i] = -1; } for (int n = 0; n < data.size(); n++) { for (int i = 0; i < 7; i++) { float dataX = mDataCoords.get(n)[i][0]; float dataY = mDataCoords.get(n)[i][1]; // 控制触摸/点击的范围,在有效范围内才触发 if (Math.abs(touchX - dataX) < xScale / 4 && Math.abs(touchY - dataY) < yScale / 4) { isClick = true; clickIndex[n] = i; invalidate(); return true; } } } invalidate(); return super.onTouchEvent(event); } private void initParams() { int width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); int height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); yScale = height / 7f; // y轴刻度 xScale = width / 7.5f; // x轴刻度 startPointX = xScale / 2; // 开始绘图的x坐标 startPointY = yScale / 2; // 开始UI图的y坐标 xLength = 6.5f * xScale; // x轴长度 yLength = 5.5f * yScale; // y轴长度 float chartLineStrokeWidth = xScale / 50; // 图表线条的线宽 coordTextSize = xScale / 5; // 坐标刻度文字的大小 float dataLineStrodeWidth = xScale / 15; // 数据线条的线宽 // 设置画笔相关属性 mBackColorPaint.setColor(0x11DEDE68); mScaleLinePaint.setStrokeWidth(chartLineStrokeWidth); mScaleLinePaint.setColor(0xFFDEDCD8); mScaleValuePaint.setColor(0xFF999999); mScaleValuePaint.setTextSize(coordTextSize); mDataLinePaint.setStrokeWidth(dataLineStrodeWidth); mDataLinePaint.setStrokeCap(Paint.Cap.ROUND); mDataLinePaint.setTextSize(1.5f * coordTextSize); } /** * 根据数据值data生成合适的Y坐标刻度值 * * @return y轴坐标刻度值数组 */ private String[] createYLabel() { if (data.isEmpty()) throw new NullPointerException("数据点为空"); int num = data.size() * data.get(0).length;//所有的数据长度 int[] dataFloats = new int[num]; for (int i = 0; i < data.size(); i++) { String[] strs = data.get(i); for (int j = 0; j < strs.length; j++) { dataFloats[i * strs.length + j] = Integer.parseInt(strs[j]); } } // 将数据值从小到大排序 Arrays.sort(dataFloats); // 中间值 int middle = (dataFloats[0] + dataFloats[num - 1]) / 2; // Y刻度值 int scale = (dataFloats[num - 1] - dataFloats[0] + 4) / 4; String[] yText = new String[5]; yText[0] = (middle - 2 * scale) + ""; yText[1] = (middle - scale) + ""; yText[2] = middle + ""; yText[3] = (middle + scale) + ""; yText[4] = (middle + 2 * scale) + ""; for (int i = 0; i < yText.length; i++) { yText[i] = yText[i] + ""; } return yText; } /** * 获取数据值的坐标点 * * @return 数据点的坐标 */ private void getDataRoords() { float originalPointX = startPointX; float originalPointY = startPointY + yLength - yScale; for (int n = 0; n < data.size(); n++) { float[][] tmp = new float[7][2]; for (int i = 0; i < data.get(n).length; i++) { tmp[i][0] = originalPointX + i * xScale; float dataY = Float.parseFloat(data.get(n)[i]); float oriY = Float.parseFloat(yLabel[0]); tmp[i][1] = originalPointY - (yScale * (dataY - oriY) / (Float.parseFloat(yLabel[1]) - Float.parseFloat(yLabel[0]))); } mDataCoords.add(tmp); } } /** * 格式化数字 ###.000 * * @return ###.000 */ /* private float format3Bit(int number) { DecimalFormat decimalFormat = new DecimalFormat("###.000"); String target = decimalFormat.format(number); if (target.startsWith(".")) { target = "0" + target; } return Float.parseFloat(target); }*/ /** * 格式化数据 ###.000 * * @param numberStr 格式化后的字符串形式 * @return ###.000 */ /*private String format3Bit(String numberStr) { if (TextUtils.isEmpty(numberStr)) { return "0.000"; } float numberFloat = Float.parseFloat(numberStr); DecimalFormat decimalFormat = new DecimalFormat("###.000"); String target = decimalFormat.format(numberFloat); if (target.startsWith(".")) { target = "0" + target; } return target; }*/ /** * 设置x轴刻度值 * * @param xLabel x刻度值 */ public void setxLabel(String[] xLabel) { this.xLabel = xLabel; } /** * 设置数据 * * @param data 数据值 */ public void setData(ArrayList<String[]> data) { this.data = data; } /** * 设置标题 * * @param title 标题 */ public void setTitle(String[] title) { this.title = title; } /** * 重新设置x轴刻度、数据、标题后必须刷新重绘 */ public void fresh() { init(); postInvalidate(); } public void setDrawColors(int[] drawColors) { this.drawColors = drawColors; } public int[] getDrawColors() { return drawColors; } }
3.用法
a.多条折线
/**
* 填充数据到line_chart
*/
private void setData() {
String[] title = {"网点", "专线", "分拨中心"};//标题
ArrayList<String[]> data = new ArrayList<String[]>();//数据
data.add(new String[]{"6", "1", "9", "3", "0", "8", "5"});//第一条折线数据
data.add(new String[]{"5", "1", "2", "6", "1", "7", "9"});//第二条折线数据
String[] tmp = new String[data.get(0).length];//第三条折线数据
for (int i = 0; i < data.get(0).length; i++) {
tmp[i] = Integer.parseInt(data.get(0)[i]) + Integer.parseInt(data.get(1)[i]) + "";
}
data.add(tmp);
//画折线的颜色
int[] colors = new int[3];
colors[0] = getResources().getColor(R.color.theme_bg);
colors[1] = getResources().getColor(R.color.orange_f92);
colors[2] = getResources().getColor(R.color.theme_red);
mChartView1.setTitle(title);//设置标题
mChartView1.setDrawColors(colors);//设置颜色
mChartView1.setData(data);//设置数据
mChartView1.fresh();//刷新折线图
}
b.单条折线
/** * 填充数据到line_chart */ private void setData(ChartView c) { String[] title=new String[]{"宁波中通路桥店"}; ArrayList<String[]> data = new ArrayList<String[]>();//数据 int[] colors; colors=new int[]{mActivity.getResources().getColor(R.color.theme_bg)}; data.add(new String[]{"6", "1", "9", "3", "0", "8", "5"}); c.setTitle(title);//设置标题 c.setDrawColors(colors);//设置颜色 c.setData(data);//设置数据 c.fresh();//刷新折线图 }