前言:当你想测量身边小物件的长度,却发现没有刻度尺,这个时候,下面这个自定义View就能帮你解决这个问题。
styleable文件配置
valuse文件夹下新建attrs.xml文件,内容如下:
//刻度尺文本字体大小
//刻度尺宽度
//刻度尺基本长度
///刻度尺颜色
//竖线宽度
//竖线颜色
//提示文字字体大小
//默认文本
//文本颜色
//背景色
效果图:
实现代码:
/**
* 自定义控件 直尺
*/
public class RulerView extends View {
private Unit unit;
private DisplayMetrics dm;
private SparseArray activePointers;
private Paint scalePaint;
private Paint labelPaint;
private Paint backgroundPaint;
private Paint pointerPaint;
private float guideScaleTextSize;
private float graduatedScaleWidth;
private float graduatedScaleBaseLength;
private int scaleColor;
private float labelTextSize;
private String defaultLabelText;
private int labelColor;
private int backgroundColor;
private float pointerStrokeWidth;
private int pointerColor;
private float zreoY;
public RulerView(Context context) {
this(context, null);
}
public RulerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RulerView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public RulerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.RulerView, defStyleAttr, defStyleRes);
guideScaleTextSize = a.getDimension(R.styleable.RulerView_rulerViewTextSize, 40);//文字大小
graduatedScaleWidth = a.getDimension(R.styleable.RulerView_graduatedScaleWidth, 2);//刻度尺宽度
graduatedScaleBaseLength =
a.getDimension(R.styleable.RulerView_graduatedScaleBaseLength, 100);
scaleColor = a.getColor(R.styleable.RulerView_scaleColor, 0xFF03070A);
labelTextSize = a.getDimension(R.styleable.RulerView_labelTextSize, 60);
defaultLabelText = a.getString(R.styleable.RulerView_defaultLabelText);
if (defaultLabelText == null) {
defaultLabelText = "Measure with two fingers";
}
labelColor = a.getColor(R.styleable.RulerView_labelColor, 0xFF03070A);
backgroundColor = a.getColor(R.styleable.RulerView_backgroundColor, 0xFFFACC31);
pointerColor = a.getColor(R.styleable.RulerView_pointerColor, 0xFF03070A);
pointerStrokeWidth = a.getDimension(R.styleable.RulerView_pointerStrokeWidth, 8);//指针笔划宽度
dm = getResources().getDisplayMetrics();
unit = new Unit(dm.ydpi);
unit.setType(1);
a.recycle();
initRulerView();
}
public void setUnitType(int type) {
unit.type = type;
invalidate();
}
public int getUnitType() {
return unit.type;
}
private void initRulerView() {
activePointers = new SparseArray<>();
//刻度尺
scalePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
scalePaint.setStrokeWidth(graduatedScaleWidth);
scalePaint.setTextSize(guideScaleTextSize);
scalePaint.setColor(scaleColor);
//文本
labelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
labelPaint.setTextSize(labelTextSize);
labelPaint.setColor(labelColor);
//背景
backgroundPaint = new Paint();
backgroundPaint.setColor(backgroundColor);
//竖线
pointerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
pointerPaint.setColor(pointerColor);
pointerPaint.setStrokeWidth(pointerStrokeWidth);
pointerPaint.setStyle(Paint.Style.STROKE);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int pointerIndex = event.getActionIndex();
int pointerId = event.getPointerId(pointerIndex);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// case MotionEvent.ACTION_POINTER_DOWN://按下
PointF position = new PointF(event.getX(pointerIndex), event.getY(pointerIndex));
activePointers.put(pointerId, position);
break;
case MotionEvent.ACTION_MOVE: //移动
int numberOfPointers = event.getPointerCount();
for (int i = 0; i < numberOfPointers; i++) {
PointF point = activePointers.get(event.getPointerId(i));
if (point == null) {
continue;
}
point.x = event.getX(i);
point.y = event.getY(i);
}
break;
case MotionEvent.ACTION_CANCEL:
activePointers.remove(pointerId);
break;
}
invalidate();
return true;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
int paddingTop = getPaddingTop();
int paddingLeft = getPaddingLeft();
// 画背景
canvas.drawPaint(backgroundPaint);
// 画刻度
Iterator pixelsIterator = unit.getPixelIterator(height - paddingTop);
while (pixelsIterator.hasNext()) {
Unit.Graduation graduation = pixelsIterator.next();
float startX = width - graduation.relativeLength * graduatedScaleBaseLength;
float startY = paddingTop + graduation.pixelOffset;
float endX = width;
float endY = startY;
zreoY = paddingTop;
canvas.drawLine(startX, startY, endX, endY, scalePaint);
if (graduation.value % 1 == 0) {
String text = (int) graduation.value + "";
canvas.save();
canvas.translate(
startX - guideScaleTextSize, startY - scalePaint.measureText(text) / 2);
canvas.rotate(90);
canvas.drawText(text, 0, 0, scalePaint);
canvas.restore();
}
}
//画竖线
PointF bottomPointer = null;
for (int i = 0, numberOfPointers = activePointers.size(); i < numberOfPointers; i++) {
PointF pointer = activePointers.valueAt(i);
if (bottomPointer == null || bottomPointer.y > pointer.y) {
bottomPointer = pointer;
}
}
if (bottomPointer != null) {
canvas.drawLine(0,
bottomPointer.y,
width,
bottomPointer.y,
pointerPaint);
}
// 画提示文本
String labelText = defaultLabelText;
if (bottomPointer != null) {
float distanceInPixels = 0;
if (zreoY <= bottomPointer.y) {
distanceInPixels = Math.abs(zreoY - bottomPointer.y);
}
labelText = unit.getStringRepresentation(distanceInPixels / unit.getPixelsPerUnit());
}
canvas.drawText(labelText, paddingLeft, paddingTop + labelTextSize, labelPaint);
}
@Override
protected int getSuggestedMinimumWidth() {
return 200;
}
@Override
protected int getSuggestedMinimumHeight() {
return 200;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int minWidth = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
int width = Math.max(minWidth, MeasureSpec.getSize(widthMeasureSpec));
int minHeight = getPaddingBottom() + getPaddingTop() + getSuggestedMinimumHeight();
int height = Math.max(minHeight, MeasureSpec.getSize(heightMeasureSpec));
setMeasuredDimension(width, height);
}
public class Unit {
class Graduation {
float value;
int pixelOffset;
float relativeLength;
}
public static final int INCH = 0;
public static final int CM = 1;
private int type = INCH;
private float dpi;
Unit(float dpi) {
this.dpi = dpi;
}
public void setType(int type) {
if (type == INCH || type == CM) {
this.type = type;
}
}
public String getStringRepresentation(float value) {
String suffix = "";
if (type == INCH) {
suffix = value > 1 ? "Inches" : "Inch";
} else if (type == CM) {
suffix = "CM";
}
return String.format("%.3f %s", value, suffix);
}
public Iterator getPixelIterator(final int numberOfPixels) {
return new Iterator() {
int graduationIndex = 0;
Graduation graduation = new Graduation();
private float getValue() {
return graduationIndex * getPrecision();
}
private int getPixels() {
return (int) (getValue() * getPixelsPerUnit());
}
@Override
public boolean hasNext() {
return getPixels() <= numberOfPixels;
}
@Override
public Graduation next() {
graduation.value = getValue();
graduation.pixelOffset = getPixels();
graduation.relativeLength = getGraduatedScaleRelativeLength(graduationIndex);
graduationIndex++;
return graduation;
}
@Override
public void remove() {
}
};
}
public float getPixelsPerUnit() {
if (type == INCH) {
return dpi;
} else if (type == CM) {
return dpi / 2.54f;
}
return 0;
}
private float getPrecision() {
if (type == INCH) {
return 1 / 4f;
} else if (type == CM) {
return 1 / 10f;
}
return 0;
}
private float getGraduatedScaleRelativeLength(int graduationIndex) {//画竖线
if (type == INCH) {
if (graduationIndex % 4 == 0) {
return 1f;
} else if (graduationIndex % 2 == 0) {
return 3 / 4f;
} else {
return 1 / 2f;
}
} else if (type == CM) {
if (graduationIndex % 10 == 0) {
return 1;
} else if (graduationIndex % 5 == 0) {
return 3 / 4f;
} else {
return 1 / 2f;
}
}
return 0;
}
}
}