打造一个简单易用的刻度尺控件
废话不多说先上效果图:
可以看出通过刻度线和刻度值的位置配置共实现了四种显示模式。
- 刻度线在上刻度在下模式
- 刻度线在上刻度在下靠拢模式
- 刻度在上刻度线在下模式
- 刻度在上刻度线下靠拢模式
接下来讲学习如何实现刻度在上刻度线在下方并靠拢的显示模式。
自定义View
测量
首先我们要配置画布大小修改onMeasure方法。先上代码
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int heightModel = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = 0;
if (heightModel == MeasureSpec.AT_MOST) {
heightSize = (int) (textHeight + (showHeightScaleLine ? heightScaleHeight : showMiddleScaleLine ? middleScaleHeight : lowScaleHeight) + valueScaleSpace + getPaddingTop() + getPaddingBottom());
} else if (heightModel == MeasureSpec.EXACTLY) {
heightSize = MeasureSpec.getSize(heightMeasureSpec) + getPaddingTop() + getPaddingBottom();
}
setMeasuredDimension(widthSize, heightSize);
}
这里主要对高度做处理,使用者配置为wrap_content 我们需要明确设置一个高度便于后面绘制的Y轴坐标计算。 当用户设置为wrap_content 画布高度为 文字高度+文字和刻度间隔高度+最大刻度高度+padding 如果不是wrap_content模式则测量多少就是多少高度。
获取最终的宽高
获取最终的宽高 可以在onMeasure 方法setMessureDimension 后获取。当时笔者更愿意在 onSizeChanged方法中获取。
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
}
开始绘制
绘制刻度
先上代码
//绘制刻度线从中间开始绘制,向两边依次绘制
private void drawScale(Canvas canvas) {
int currentValue;
canvas.save();
canvas.translate(mWidth / 2, mHeight / 2);
// 向绘制左边 <----
float currentValueXAxis = 0;
currentValue = initValue;
//最左边的坐标 计算边界使用。
while (currentValueXAxis >= -(mWidth / 2) && currentValue >= startValue) {
drawScaleLineItem(currentValueXAxis, currentValue, canvas);
currentValueXAxis = (int) (currentValueXAxis - spacing);
currentValue = currentValue - 1;
}
Log.d(TAG, "drawScale: " + leftXAxis);
//向左绘制 ----->
currentValueXAxis = 0;
currentValue = initValue;
while (currentValueXAxis <= mWidth && currentValue <= endValue) {
drawScaleLineItem(currentValueXAxis, currentValue, canvas);
currentValueXAxis = (int) (currentValueXAxis + spacing);
currentValue = currentValue + 1;
}
Log.d(TAG, "drawScale: " + rightXAxis);
canvas.restore();
}
首先将画布坐标移到View的中心位置:canvas.translate(mWidth / 2, mHeight / 2) 刻度的绘制由中间(0,0)位置开始向X轴负方向(左边)和X轴正方向(右边)开始绘制。
先绘制左边
设置当X点坐标为0. 开始刻度为我们设置的开始刻度。
// 向绘制左边 <----
float currentValueXAxis = 0 + offsetX;
currentValue = initValue;
根据开始刻度向最小刻度开始循环绘制,当刻度最小刻度切坐标超出View 的显示范围时停止绘制。绘制完一个刻度线后重置X点坐标坐标为当前X坐标减去相邻的刻度间距值,重置当前的刻度值:当前刻度-1
//最左边的坐标 计算边界使用。
while (currentValueXAxis >= -(mWidth / 2) && currentValue >= startValue) {
drawScaleLineItem(currentValueXAxis, currentValue, canvas);
currentValueXAxis = (int) (currentValueXAxis - spacing);
currentValue = currentValue - 1;
}
绘制刻度线
//绘制刻度线
private void drawScaleLineItem(float currentValueXAxis, int currentValue, Canvas canvas) {
float startCenterX = 0,endCenterX = 0,startCenterY =0,endCenterY=0;
float startLowX=0, endLowX=0, startLowY=0, endLowY=0;
float startMiddleX=0, endMiddleX=0, startMiddleY=0, endMiddleY=0;
float startHighX=0, endHighX=0, startHighY=0, endHighY=0;
startCenterX = -mWidth;
startCenterY = valueScaleSpace / 2;
endCenterX = mWidth;
endCenterY = valueScaleSpace / 2;
startLowX = currentValueXAxis;
startLowY = 0 + valueScaleSpace / 2;
endLowX = currentValueXAxis;
endLowY = lowScaleHeight + valueScaleSpace / 2;
startMiddleX = currentValueXAxis;
startMiddleY = 0 + valueScaleSpace / 2;
endMiddleX = currentValueXAxis;
endMiddleY = middleScaleHeight + valueScaleSpace / 2;
startHighX = currentValueXAxis;
startHighY = 0 + valueScaleSpace / 2;
endHighX = currentValueXAxis;
endHighY = heightScaleHeight + valueScaleSpace / 2;
canvas.drawLine(startCenterX, startCenterY, endCenterX, endCenterY, centerLinePaint);
}
canvas.drawLine(startLowX, startLowY, endLowX, endLowY, lowScalePaint);
if (currentValue % middleSpaceValueSpace == 0) {
canvas.drawLine(startMiddleX, startMiddleY, endMiddleX, endMiddleY, middleScalePaint);
}
if (currentValue % highSpaceValeSpace == 0) {
canvas.drawLine(startHighX, startHighY, endHighX, endHighY, highScalePaint);
}
}
X坐标不会变化,主要考虑Y轴的最低点坐标,应为刻度分为高中低。这里加了一个valueScaleSpace,valueScaleSpace 值是数值和刻度之间的间隔距离。 只是向左边绘制,当然向右绘制也是一样的只不过刻度值是+1,X轴坐标点每次循环都是加上间距。
currentValueXAxis = (int) (currentValueXAxis + spacing);
currentValue = currentValue + 1;
接下来绘制刻度值
//绘制刻度值从中间开始绘制,向两边依次绘制
private void drawScaleNumber(Canvas canvas) {
int currentValue;
canvas.save();
canvas.translate(mWidth / 2, mHeight / 2);
// 向绘制左边 <----
float currentValueXAxis = 0;
currentValue = initValue;
while (currentValueXAxis >= -(mWidth / 2) && currentValue >= startValue) {
drawScaleNumberItem(canvas, currentValue, (int) currentValueXAxis);
currentValueXAxis = (int) (currentValueXAxis - spacing);
currentValue = currentValue - 1;
}
//向左绘制 ----->
currentValueXAxis = 0;
currentValue = initValue;
while (currentValueXAxis <= mWidth && currentValue <= endValue) {
drawScaleNumberItem(canvas, currentValue, (int) currentValueXAxis);
currentValueXAxis = (int) (currentValueXAxis + spacing);
currentValue = currentValue + 1;
}
canvas.restore();
}
这里逻辑和绘制刻度是一样的,直接上代码看吧。
//绘制刻度值
private void drawScaleNumberItem(Canvas canvas, int currentValue, int currentValueXAxis) {
drawText(canvas, String.valueOf(currentValue), currentValueXAxis, 0);
}
private void drawText(Canvas canvas, String text, int x, int y) {
Rect rect = new Rect();
Log.d(TAG, "drawText: x :" + x);
Log.d(TAG, "drawText: text :" + text);
textPaint.getTextBounds(text, 0, text.length(), rect);
canvas.drawText(text, 0, text.length(), (float) (x + ((rect.left * 1.0 - rect.right * 1.0) / 2)), y - valueScaleSpace / 2, textPaint);
}
绘制完了怎么让画面动起来呢
直接上代码
float onTouchLocationX = -1;
float offsetX = 0;
float lastMoveX = 0;
@SuppressLint("ClickableViewAccessibility")
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
onTouchLocationX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
offsetX = event.getX() - onTouchLocationX + lastMoveX;
invalidate();
break;
case MotionEvent.ACTION_UP:
lastMoveX = offsetX;
invalidate();
startAnimation();
break;
}
return true;
}
这里通过监听手指的滑动时间计算出来偏移量,这个偏移量叠加到绘制的x坐标上不就可以滑动了吗。
// 向绘制左边 <----
float currentValueXAxis = 0 + offsetX;
绘制的时候将偏移量加到X轴坐标上。
好了最基础的刻度尺算是完成了。
当然了你也可以访问gitHub查看完整功能的实现。 Andihu/ruler (github.com)
当然也可以留言:1641926972@qq.com