效果图:
直接上代码:
package com.my.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
/**
* 弧形可滑动刻度控件
*/
public class ArcRulerView extends View {
//弧形开始的角度
private float startAngle = 180f;
//弧面所跨的弧度
private final float sweepAngle = 180f;
//outer总刻度
private final int outerTotalDial = 80;
//inner总刻度
private final int innerTotalDial = (int) (outerTotalDial * 2.5f);
//每次滑动角度
private final float moveAnglePre = sweepAngle / outerTotalDial * 0.5f;
//inner半圆的半径
private int innerRadius;
//outer半圆的半径
private int outerRadius;
private String TAG = ArcRulerView.class.getSimpleName();
private int dp2px(int i) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, i, getResources().getDisplayMetrics());
}
//inner刻度线的宽度
private int innerLineWidth = 1;
//inner刻度线的高度
private int innerLineHeight = dp2px(18);
//刻度线的宽度
private int outerLineWidth = dp2px(2);
//outer刻度线的高度
private int outerLineHeight = dp2px(36);
private Bitmap mRulerPoint;
private int sp2px(int i) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, i, getResources().getDisplayMetrics());
}
private Paint outerLinePaint, innerLinePaint, innerBgPaint, outerBgPaint;
public ArcRulerView(Context context) {
this(context, null);
}
public ArcRulerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ArcRulerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public ArcRulerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
outerLinePaint = new Paint();
outerLinePaint.setColor(Color.WHITE);
outerLinePaint.setAntiAlias(true);
innerLinePaint = new Paint();
innerLinePaint.setColor(Color.parseColor("#D6D6D6"));
innerLinePaint.setAntiAlias(true);
outerBgPaint = new Paint();
outerBgPaint.setColor(Color.parseColor("#1E1E1E"));
outerBgPaint.setAlpha(30);
outerBgPaint.setAntiAlias(true);
innerBgPaint = new Paint();
innerBgPaint.setColor(Color.parseColor("#D6D6D6"));
innerBgPaint.setAlpha(40);
innerBgPaint.setAntiAlias(true);
mRulerPoint = BitmapFactory.decodeResource(getResources(), R.drawable.ic_ruler_point);
}
private float dx = 0, dy = 0, mx = 0, my = 0;
private boolean mIsDrawDial = false;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
dx = event.getX();
dy = event.getY();
break;
case MotionEvent.ACTION_MOVE:
if (!mIsDrawDial) {
mx = event.getX();
my = event.getY();
float md = mx - dx;//x轴滑动距离
if (md > 0 && startAngle < sweepAngle + 90) {
if (md > 5) {
startAngle += moveAnglePre;
invalidate();
dx = mx;
dy = my;
}
} else if (md < 0 && startAngle > sweepAngle - 90) {
if (Math.abs(md) > 5) {
startAngle -= moveAnglePre;
invalidate();
dx = mx;
dy = my;
}
}
}
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
outerRadius = (int) (getWidth() * 0.68f);//outer半径
innerRadius = (int) (outerRadius - outerLineHeight - innerLineHeight * 0.8f);//inner半径
if (!mIsDrawDial) {
mIsDrawDial = true;
//outer背景
float outerBgRadius = Math.max(getHeight(), outerRadius);
float outerWidthDiff = outerBgRadius - getWidth() * 0.5f;
canvas.drawArc(-outerWidthDiff, 0, getWidth() + outerWidthDiff, outerBgRadius * 2, startAngle, sweepAngle, true, outerBgPaint);
//inner背景
float innerWidthDiff = innerRadius - getWidth() * 0.5f;
canvas.drawArc(-innerWidthDiff, getHeight() - innerRadius, getWidth() + innerWidthDiff, innerRadius * 2f + getHeight() - innerRadius, startAngle, sweepAngle, true, innerBgPaint);
drawDial(startAngle, sweepAngle, outerTotalDial, outerLineWidth, outerLineHeight, outerRadius, outerLinePaint, canvas);//outer刻度
drawDial(startAngle, sweepAngle, innerTotalDial, innerLineWidth, innerLineHeight, innerRadius, innerLinePaint, canvas);//inner刻度
mIsDrawDial = false;
}
canvas.drawBitmap(mRulerPoint, getWidth() * 0.5f - mRulerPoint.getWidth() * 0.5f, 0, null);//指针
}
/**
* 画刻度盘
*/
private void drawDial(float startAngle, float sweepAngle, int dialCount, int lineWidth, int lineHeight, int radius, Paint paint, Canvas canvas) {
paint.setStrokeWidth(lineWidth);
float length;
float angle;
//根据需要显示的刻度总个数遍历
for (int i = 0; i <= dialCount; i++) {
//每一个刻度对应的起始角度为180度+(总度数/个数)*对应刻度的位置
angle = ((sweepAngle) / (dialCount * 1f) * i) + startAngle;
//线条的起始点位置
float[] startP;
//线条的end点的位置
float[] endP;
//短刻度条的长度为长刻度条的一半
length = lineHeight;
startP = getPointFromAngleAndRadius(angle, radius);
endP = getPointFromAngleAndRadius(angle, radius - length);
//高亮计算
int alpha = 255;
int centerAngle = 270;
if (angle != centerAngle) {
int angleDiff = (int) Math.abs(angle - centerAngle);
int anglePer = 255 / 45;
alpha = 255 - angleDiff * anglePer;
}
LibLogUtil.i(TAG, "alpha=" + alpha);
alpha = Math.max(alpha, 0);
paint.setAlpha(alpha);
//画出对应的刻度条
canvas.drawLine(startP[0], startP[1], endP[0], endP[1], paint);
}
}
/**
* 根据刻度条相应的角度算出点位置
*
* @param angle
* @param radius
* @return
*/
private float[] getPointFromAngleAndRadius(float angle, float radius) {
//根据三角函数公式可以知道,横坐标值为(刻度条+innnerradius)也就是刻度条对应圆的半径
//乘以一个cos(angle),因为我们是以(getWidth() / 2,控件的高度)位置建的坐标系
//而真正的坐标系的位置为控件左上角,所以算出的值后需要+getWidth() / 2或者getHeight()
double x = radius * Math.cos(angle * Math.PI / 180) + getWidth() / 2;
double y = radius * Math.sin(angle * Math.PI / 180) + getHeight();
return new float[]{(float) x, (float) y};
}
}