前言
折线图在比较数据、天气等方面的时候会用到,网上也不乏大佬将其封装成完整控件。自己也简单写写看一下效果。先上效果图,毕竟无图何以言diao。
嗯,效果就这样,下面将介绍其从无到有的过程!!
重写View的onMeasure方法
在自定义控件的时候,控件的宽高(最终的测量尺寸)由控件本身和其父容器共同决定的,既然是共同决定的我们得知道父容器的“意愿”对吧!
父容器的“意愿”也就三种,在MeasureSpec类中的三个常量表明了其三种意愿:
1、UNSPECIFIED:父容器对控件的大小不在意,你想多大都可以;
2、AT_MOST:父容器给控件的大小设置了一个最大值,表示你大小的取值最好在这个范围内;
3、EXACTLY: 父容器已经给控件计算出了显示的大小,控件就显示这么大得了;
onMeasure方法具体的代码:
int resultWidth;
//获取父容器的意愿
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
resultWidth = sizeWidth;
} else {
if (widthMode == MeasureSpec.AT_MOST) {
resultWidth = Math.min(screenWidth, sizeWidth);
} else {
resultWidth = screenWidth;
}
}
int resultHeight;
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode == MeasureSpec.EXACTLY) {
resultHeight = sizeHeight;
} else {
if (heightMode == MeasureSpec.AT_MOST) {
resultHeight = Math.min(screenHeight, sizeHeight);
} else {
resultHeight = screenHeight;
}
}
//设置测量出的宽高
setMeasuredDimension(resultWidth, resultHeight);
因为折线图是一个显示出来较占位置的控件,所以在父容器对大小无要求的时候,就以屏幕的大小作为宽高。获取屏幕的宽高:
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
screenWidth = metrics.widthPixels;
screenHeight = metrics.heightPixels;
现在展现在你面前的就是一张“白纸”,想怎么画全看意愿了,哈
理一下画图的顺序
drawTextY(Canvas canvas) //绘制y轴上需要显示的文字
drawTextX(Canvas canvas) //绘制x轴上的文字
drawCoordinate(Canvas canvas) //绘制坐标轴
drawBrokenLine(Canvas canvas) //绘制折线、顶点及顶点文字
差不多就是这个从上到下的顺序!
看到这儿估计有人会说:你特么逗我么,绘制折线图不先画坐标轴?
道友别急,请听我狡辩:绘制折线图有一个关键点是确定点的坐标(特么不是废话么),之所以将坐标轴放到文字之后绘制是因为y上绘制文字的宽度和x轴上文字的高度会对坐标轴的定位有一定影响。因为这两个数据是未知的,所以我们得先确定下来才好进行下一步操作!
drawTextY(Canvas canvas)
先上代码
//y轴文字的最大宽度
private int maxTextWidth;
//y轴每一刻度的高度
private int singleHeight;
//文字的高度
private int textHeight;
//y轴文字list
private List<String> textY;
/**
* draw Y轴上的文字
*
* @param canvas
*/
private void drawTextY(Canvas canvas) {
if (textY == null) return;
// 文字的高度:mTextPaint.descent() - mTextPaint.ascent()
textHeight = (int) (mTextPaint.descent() - mTextPaint.ascent());
singleHeight = (getHeight() - paddingBottom - paddingTop - textHeight - spacing) / (textY.size() - 1);
int xPos = paddingLeft;
int yPos;
for (int i = 0; i < textY.size(); i++) {
//找出文字所需的最大宽度
if (mTextPaint.measureText(textY.get(textY.size() - i - 1)) > maxTextWidth) {
maxTextWidth = (int) mTextPaint.measureText(textY.get(textY.size() - i - 1));
}
yPos = paddingTop + singleHeight * i + textHeight / 2;
canvas.drawText(textY.get(textY.size() - i - 1), xPos, yPos, mTextPaint);
}
}
变量注释应该还算详细,抽两个简单介绍一下吧
textHeight:文字的高度获取,mTextPaint.descent() - mTextPaint.ascent()表达式前后的顺序不要反了,不然得到是一个负数(别问我是怎么知道的)。至于为什么是这样获取,问神奇的www.google.cn吧。
singleHeight:每一刻度的高度,通过控件的高度减去上下边距、文字高度、文字与坐标轴的间距(固定值)除以份数得到,聪明的你一定了然于心了!
drawTextX(Canvas canvas)
//x轴每一刻度的宽度
private int singleWidth;
//x轴文字list
private List<String> textX;
/**
* draw X轴上的文字
*
* @param canvas
*/
private