最近在做股票相关的项目,避免不了折线图,在这里温习一下写法,都是很简单的东西。
先看一下效果图
然后贴代码:
public class FTenBrokenLineView extends View {
public int XPoint = DipUtil.dip2px(40); //原点的X坐标
public int YPoint = DipUtil.dip2px(210); //原点的Y坐标
public int XScale = 55; //X的刻度长度
public int YScale = 40; //Y的刻度长度
public int XLength = 300; //X轴的长度
public int YLength = 210; //Y轴的长度
public String[] XLabel; //X的刻度
public String[] YLabel; //Y的刻度
public String[] Data; //数据
public String Title; //显示的标题
private Context context;
private Paint axisPaint;// 轴线
private Paint dataPaint;// 数据线
private Paint yTextPaint;// 文字的笔
private Paint xTextPaint;
private Paint titlePaint; // Y轴顶部的提示
private Paint annulusPaint; // 数据圆环
private Paint paint;
private Path path;
Paint paint1;
private float radius = DipUtil.dip2px(4);// 数据圆环的半径
public FTenBrokenLineView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init();
}
public FTenBrokenLineView(Context context) {
super(context);
this.context = context;
init();
}
private void init() {
XLength = DataManager.getInstance(context).getDeviceWidth(context) - DipUtil.dip2px(70);// 这个是把屏幕的宽度减去两边的间距的和
YLength = DipUtil.dip2px(190);
}
/**
* 为折线图设置数据
*
* @param XLabels x的刻度
* @param YLabels y的刻度
* @param AllData 传入的数据
*/
public void setInfo(String[] XLabels, String[] YLabels, String[] AllData) {
XLabel = XLabels;
YLabel = YLabels;
Data = AllData;
XScale = XLength / (XLabel.length - 1);
YScale = YLength / YLabel.length;// 因为Y轴方向上第0个点没有数据,所以要特殊处理
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);//重写onDraw方法
initPaint();
canvas.drawLine(XPoint, YPoint, XPoint + XLength, YPoint, axisPaint); //X轴
canvas.drawLine(XPoint, YPoint - YLength, XPoint, YPoint, axisPaint); //画Y轴
for (int i = 1; i <= YLabel.length; i++) {
canvas.drawLine(XPoint, YPoint - i * YScale, XPoint + XLength, YPoint - i * YScale, axisPaint); //画横着方向每一个刻度,长度是X轴的长度
int j = i - 1;// 因为Y轴方向上第0个点没有数据,这里特殊处理
canvas.drawText(YLabel[j], XPoint - DipUtil.dip2px(25), YPoint - i * YScale + DipUtil.dip2px(3), yTextPaint); //文字
}
for (int i = 0; i < XLabel.length; i++) {
canvas.drawLine(XPoint + i * XScale, YPoint, XPoint + i * XScale, YPoint - YLength, axisPaint); //画X轴方向刻度
if (i > 0 && YCoord(Data[i - 1]) != -999 && YCoord(Data[i]) != -999) { //保证有效数据
paint1 = new Paint();
paint1.setAntiAlias(true);
paint1.setStyle(Paint.Style.FILL);
paint1.setColor(0x153ea3ff);// 这个颜色就蓝色不透明8%
path = new Path();// 以每一个间距绘制它的路径,然后填充颜色。
path.moveTo(XPoint + (i - 1) * XScale, YCoord(Data[i - 1]));
path.lineTo(XPoint + i * XScale, YCoord(Data[i]));
path.lineTo(XPoint + i * XScale, YPoint);
path.lineTo(XPoint + (i - 1) * XScale, YPoint);
path.close();
canvas.drawPath(path, paint1);// 这里是绘制阴影效果
canvas.drawLine(XPoint + (i - 1) * XScale, YCoord(Data[i - 1]), XPoint + i * XScale, YCoord(Data[i]), dataPaint);// 绘制数据线
}
}
// 循环X轴上的数据
for (int i = 0; i < XLabel.length; i++) {
// 这里是计算X轴上的标示长度,因为是分为两行,所以分割为2015 和 三季度,然后计算长度
String str1 = XLabel[i].substring(0, 4);
float strwid1 = xTextPaint.measureText(str1);
String str2 = XLabel[i].substring(4);
float strwid2 = xTextPaint.measureText(str2);
canvas.drawText(str1, (XPoint + i * XScale) - strwid1 / 2, YPoint + DipUtil.dip2px(15), xTextPaint);// 绘制X轴上的刻度数据,第一行2015
canvas.drawText(str2, (XPoint + i * XScale) - strwid2 / 2, YPoint + DipUtil.dip2px(30), xTextPaint);// 绘制X轴上的刻度数据,第二行三季度
str1 = null;
str2 = null;
canvas.drawCircle(XPoint + i * XScale, YCoord(Data[i]), radius, annulusPaint);// 绘制数据点,是一个圆环
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(context.getResources().getColor(R.color.background_gray));
canvas.drawCircle(XPoint + i * XScale, YCoord(Data[i]), radius * 2 / 3, paint);// 在圆环内部绘制一个圆,颜色为背景颜色
}
}
private void initPaint() {
axisPaint = new Paint();
axisPaint.setStyle(Paint.Style.STROKE);
axisPaint.setAntiAlias(true);
axisPaint.setStrokeWidth(DipUtil.dip2px(1));
axisPaint.setColor(context.getResources().getColor(R.color.line));
dataPaint = new Paint();
dataPaint.setStyle(Paint.Style.STROKE);
dataPaint.setAntiAlias(true);
dataPaint.setStrokeWidth(radius / 3);
dataPaint.setColor(context.getResources().getColor(R.color.blue));
annulusPaint = new Paint();
annulusPaint.setAntiAlias(true);
annulusPaint.setStrokeWidth(radius / 3);
annulusPaint.setColor(context.getResources().getColor(R.color.blue));
yTextPaint = new Paint();
yTextPaint.setAntiAlias(true);
yTextPaint.setColor(context.getResources().getColor(R.color.title_color));
yTextPaint.setTextSize(context.getResources().getDimension(R.dimen.text_size_12));
xTextPaint = new Paint();
xTextPaint.setColor(context.getResources().getColor(R.color.text_gray));
xTextPaint.setTextSize(context.getResources().getDimension(R.dimen.text_size_12));
xTextPaint.setAntiAlias(true);
titlePaint = new Paint();
titlePaint.setAntiAlias(true);
titlePaint.setColor(context.getResources().getColor(R.color.title_color));
titlePaint.setTextSize(context.getResources().getDimension(R.dimen.text_size));
}
/**
* 计算绘制时的Y坐标,无数据时返回-999
* @param y0
* @return
*/
private float YCoord(String y0) {
double y;
double k;
try {
y = Double.parseDouble(y0);
} catch (Exception e) {
return -999; //出错则返回-999
}
try {
k = (YLength - YScale) / (Double.parseDouble(YLabel[YLabel.length - 1]) - Double.parseDouble(YLabel[0]));
return (float) (((Double.parseDouble(YLabel[YLabel.length - 1]) - y) * k) + (YPoint - YLength));
} catch (Exception e) {
}
return 0;
}
}
<com.....FTenBrokenLineView android:id="@+id/flBrokenLine" android:layout_width="match_parent" android:layout_height="wrap_content" />
上面是xml文件中的
下面是在代码中添加数据:
private FTenBrokenLineView flBrokenLine; public String[] YLabel = {"0.5", "0.6", "0.7", "0.8", "0.9", "1.0", "1.1", "1.2"}; //X的刻度 public String[] XLabel = {"2014三季度", "2014年报", "2015一季度", "2015中报", "2015三季度"}; //Y的刻度 public String[] Data = {"0.9", "0.8", "1.1", "0.65", "0.92"}; //数据
自定义的view代码写的很乱,因为中间写的时候会有覆盖问题,所以为了能显示出效果,就写的很随便了,也不是很通用。flBrokenLine = ViewHolder.init(view, R.id.flBrokenLine);flBrokenLine.setInfo(XLabel, YLabel, Data);
diputil.dip2px方法就是px和dp转换的方法。
DataManager.getInstance(context).getDeviceWidth(context)这个方法就是获取屏幕的宽度。
YCoord() 方法是:根据传入的要显示的参数,把这些值转换为屏幕上Y轴的像素,然后绘制出数据点。转换的跪着就是计算y轴上每一个间距对应的像素的一个比率K,然后用最大值减去传入的参数得到的值乘,这个K,再加上上面空隙的间距,就是返回的值(这写的。。。。我自己都看不懂。。。。。。)反正就是一个数学的比率。
反正代码很简单,复习一下。。。。