Android - 折线图
16lz
2021-01-23
使用Android的canvas,画折线图:代码为:
package spt.view;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.Log;import android.view.View;/** * 为了扩展不同分辨率手机的兼容性,百分比和一些重要变量设置为final,其他可变变量设置为成员变量. * * @author Administrator * */@SuppressLint("DrawAllocation")public class BrokenLineView extends View {// 计算相对比例时,均使用边距margin为依照(值与比例结果成反比).// 画轴线时,箭头所占比例.private static final int ARROW_PECENT = 10;// x,y轴坐标点文字的比例.private static final int X_TEXT_PECENT = 10;private static final int Y_TEXT_PECENT = 10;// 标题文字的比例.private static final int TITLE_TEXT_PECENT = 5;// 默认坐标轴值.private static final String[] X_LABLE = { "a", "b", "c", "d", "e", "f", "g" };private static final String[] Y_LABLE = { "0", "50", "100", "150", "200","250", "300" };// 数据点圆的半径.private static final int dataRadius = 10;// x,y轴坐标点数字的位置偏离轴线的距离.private int xTextDistanceAxis = 20;private int yTextDistanceAxis = 30;// 数据点数值文字相对于数据点的高度值.private int dataTextAboveCircle = 25;// 边距(也不能为final,因为可能用户可能根据不同条件设置不同间距.private int margin = 100;// X,Y轴的单位长度private int xScale = 20;private int yScale = 20;// 标题的高度.private String title;// 标题距离最顶行线的y距离.private int titleHeight = 20;// 原点坐标private int x0Point;private int y0Point;// X,Y轴上面的显示文字private String[] xLabel = null;private String[] yLabel = null;// 曲线数据private int[] data = null;public BrokenLineView(Context context, String title, String[] xLabel,String[] yLabel, int[] data) {super(context);this.title = title;// 若传递空值,则使用默认的值.this.xLabel = (xLabel == null ? X_LABLE : xLabel);this.yLabel = (yLabel == null ? Y_LABLE : yLabel);if (data == null)throw new RuntimeException("data cannot null:");this.data = data;}public BrokenLineView(Context context) {this(context, null, X_LABLE, Y_LABLE, null);}// 设置坐标原点位置和轴线上的单位长度.public void init() {x0Point = margin; // x0.y0Point = getHeight() - margin; // y0.xScale = (getWidth() - 2 * margin) / (this.xLabel.length - 1);yScale = (getHeight() - 2 * margin) / (this.yLabel.length - 1);}public int getMargin() {return margin;}public void setMargin(int margin) {if (margin < 0)throw new RuntimeException("间距不能为负数:" + margin);this.margin = margin;}@Overrideprotected void onDraw(Canvas canvas) {canvas.drawColor(Color.BLACK); // 背景色.Paint p = new Paint();p.setStyle(Paint.Style.STROKE); // 设置轴线的的外框的样式“空心”(STROKE).p.setAntiAlias(true); // 抗锯齿p.setColor(Color.WHITE);p.setStrokeWidth(2); // 设置轴线的的外框的宽度.init();drawYAxis(canvas, p);drawXAxis(canvas, p);drawHorizontalLine(canvas);drawData(canvas);}// x向线private void drawHorizontalLine(Canvas canvas) {Paint paint = new Paint();paint.setStyle(Paint.Style.STROKE);paint.setColor(Color.GRAY);Path path = new Path();// 先画长度为1的实线,然后长度为10的空白,再画长度为1实线,再画长度为10的空白;最后一个是是偏移量,可不理会.PathEffect effects = new DashPathEffect(new float[] { 1, 10, 1, 10 }, 1);paint.setPathEffect(effects);for (int i = 1; (y0Point - i * yScale) >= margin; i++) {int startX = x0Point;int startY = y0Point - i * yScale;int stopX = x0Point + (xLabel.length - 1) * xScale;path.moveTo(startX, startY);path.lineTo(stopX, startY);paint.setColor(Color.DKGRAY);canvas.drawPath(path, paint);}}/** * 画y轴线 * * @param canvas * @param p */private void drawYAxis(Canvas canvas, Paint p) {// y轴.canvas.drawLine(x0Point, y0Point, margin, margin, p);// y轴箭头的左部分.canvas.drawLine(x0Point, margin, x0Point - x0Point / ARROW_PECENT,margin + margin / ARROW_PECENT, p);// y轴箭头的右部分.canvas.drawLine(x0Point, margin, x0Point + x0Point / ARROW_PECENT,margin + margin / ARROW_PECENT, p);}/** * 画x轴线. * * @param canvas * @param p */private void drawXAxis(Canvas canvas, Paint p) {// x轴.canvas.drawLine(x0Point, y0Point, getWidth() - margin, y0Point, p);// x轴箭头的上部分.canvas.drawLine(getWidth() - margin, y0Point, getWidth() - margin- margin / ARROW_PECENT, y0Point - margin / ARROW_PECENT, p);// x轴箭头的下部分.canvas.drawLine(getWidth() - margin, y0Point, getWidth() - margin- margin / ARROW_PECENT, y0Point + margin / ARROW_PECENT, p);}// 画数据private void drawData(Canvas canvas) {Paint p = new Paint();p.setAntiAlias(true); // 抗锯齿.p.setColor(Color.RED);p.setTextSize(margin / X_TEXT_PECENT);for (int x = 0; x < data.length; x++) {int startX = x0Point + x * xScale;// 轴坐标点文字的显示.canvas.drawText(xLabel[x], startX, y0Point + xTextDistanceAxis, p);// 数据点的圆.canvas.drawCircle(startX, calY(data[x]), dataRadius, p);// 在数据点上标数据值.canvas.drawText(data[x] + "", startX, calY(data[x]) + dataRadius- dataTextAboveCircle, p);// 在数据点圆间画变化线.// 画线时,最后数据点不执行操作.if (x != data.length - 1)canvas.drawLine(startX, calY(data[x]), startX + xScale,calY(data[x + 1]), p);} // for x.// 画y轴坐标点文字.Paint py = new Paint();py.setAntiAlias(true); // 抗锯齿.py.setColor(Color.RED);py.setTextSize(margin / Y_TEXT_PECENT);for (int y = 0; y < yLabel.length; y++) {int startY = y0Point - y * yScale;// y轴坐标点文字的显示.canvas.drawText(yLabel[y], x0Point - yTextDistanceAxis, startY, py);} // for y.// 画标题.Paint pTitle = new Paint();pTitle.setAntiAlias(true); // 抗锯齿.pTitle.setColor(Color.RED);pTitle.setTextSize(margin / TITLE_TEXT_PECENT);// 通过Paint.measureText计算标题长度的像素,进而将标题水平居中.canvas.drawText(title, (getWidth() - pTitle.measureText(title)) / 2,margin - titleHeight, pTitle);}/** * 计算数据值在坐标系中y的位置. * * @param y * @return */private int calY(int y) {int y0 = 0;int y1 = 0;try {y0 = Integer.parseInt(yLabel[0]);y1 = Integer.parseInt(yLabel[1]);return y0Point - ((y - y0) * yScale / (y1 - y0));} catch (NumberFormatException e) {Log.d("sysout", "y轴label必须是数字:" + e.getMessage());return -1;}}}
然后在Activity中调用即可:
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//test:int[] data = { 0, 50, 51, 78, 200, 121, 31 };final String title = "最近一周AQI变化图";setContentView(new BrokenLineView(this, title, null, null, data));initView();}
更多相关文章
- Android右箭头的显示文字的View
- PHP中箭头函数的实例详解
- Google地图信息窗口左箭头,右箭头传递TypeError:无法读取未定义的
- JQuery仿最新淘宝网首页带箭头幻灯片,JQuery轮播图
- 如何通过多次单击在画布上显示多个箭头
- javaES6箭头函数的全新特性
- 在JComboBox箭头JButton上附加动作事件