2,完整代码:
/**
* SeekBarBoosterView2
* Created by QiuLong on 2021/3/23.
*/
public class SeekBarBoosterView2 extends View {
private static final int SCALE_LINE_COUNT = 23;// 刻度线的数量
private static final int PROGRESS_RECT_COUNT = 39;// 进度方块的数量
private static final String[] TEXT_VALUES = {"Max", "75", "50", "25", "0"};
private float mTextMaxWidth;
private float mBgRectWidth, mBgRectHeight;// 背景矩形的宽度
private float mLightSpace;// 渐变矩形的外边距
private float mLightRectWidth, mLightRectHeight;// 渐变矩形的宽高
private float mThumbRectWidth, mThumbRectHeight;// 滑块矩形的宽高
private float mProgressBgRectWidth, mProgressBgRectHeight;// 进度背景矩形的宽高
private float mProgressSpaceH, mProgressSpaceV;// 进度矩形左右两边和上下小方块矩形之间的距离
private float mProgressRectWidth, mProgressRectHeight;// 进度整体矩形的宽高
private float mProgressMinWidth, mProgressMinHeight;// 进度小方块矩形的宽高
private float mArrowRectWidth, mArrowRectHeight;// 箭头矩形的宽高
private float mArrowSpace;// 箭头超出左右两边的距离
private Drawable mBgDrawable;
private Drawable mLightDrawable;
private Drawable mArrowDrawable;
private Drawable mThumbDrawable;
private int[] mLinearGradient;
private Rect mBgRect;
private Rect mLightRect;
private Rect mArrowRect;
private Rect mThumbRect;
private RectF mProgressBgRect;
private RectF mProgressRect;
private RectF mLightBgRect;
private Paint mPaint;
private Paint mPaintText;
public int mMaxProgress = 100;
public int mProgress = 0;
private PointF mLastPoint;
public boolean isPressed;
private OnSeekBarBoosterChangeListener mOnSeekBarChangeListener;
public SeekBarBoosterView2(Context context) {
this(context, null);
}
public SeekBarBoosterView2(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SeekBarBoosterView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
mBgRect = new Rect();
mLightRect = new Rect();
mArrowRect = new Rect();
mThumbRect = new Rect();
mProgressBgRect = new RectF();
mProgressRect = new RectF();
mLightBgRect = new RectF();
mLastPoint = new PointF();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL);
mPaintText = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintText.setTextSize(DensityUtils.sp2px(context, 12));
mPaintText.setColor(Color.WHITE);
mBgDrawable = AppCompatResources.getDrawable(context, R.drawable.booster_bg_02);
mLightDrawable = AppCompatResources.getDrawable(context, R.drawable.booster_light_bg);
mArrowDrawable = AppCompatResources.getDrawable(context, R.drawable.thumb_arrow);
mThumbDrawable = AppCompatResources.getDrawable(context, R.drawable.thumb_on_02);
mLinearGradient = new int[]{0xFFA6FF56, 0xFFFFFF4F, 0xFFFF9536, 0xFFFF1B14};
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBg(canvas);
drawLightBg(canvas);
drawProgressBg(canvas);
drawProgress(canvas);
drawScaleLine(canvas);
drawArrow(canvas);
drawLight(canvas);
drawThumb(canvas);
drawText(canvas);
}
private void drawBg(Canvas canvas) {
if (mBgDrawable != null) {
mBgRect.set(0, 0, (int) mBgRectWidth, (int) mBgRectHeight);
mBgRect.offsetTo((int) (getPaddingLeft() + mTextMaxWidth + mArrowSpace), getPaddingTop());
mBgDrawable.setBounds(mBgRect);
mBgDrawable.draw(canvas);
}
}
private void drawLight(Canvas canvas) {
if (mLightDrawable != null) {
mLightRect.set(0, 0, (int) mLightRectWidth, (int) mLightRectHeight);
mLightRect.offsetTo((int) (getPaddingLeft() + mTextMaxWidth + mArrowSpace + mLightSpace),
(int) (getPaddingTop() + mLightSpace));
mLightDrawable.setBounds(mLightRect);
mLightDrawable.draw(canvas);
}
}
private void drawLightBg(Canvas canvas) {
mPaint.setShader(null);
mPaint.setColor(0xFF262623);
mLightBgRect.set(0, 0, (int) mLightRectWidth, (int) mLightRectHeight);
mLightBgRect.offsetTo((int) (getPaddingLeft() + mTextMaxWidth + mArrowSpace + mLightSpace),
(int) (getPaddingTop() + mLightSpace));
float radius = mLightRectWidth / 10f;
canvas.drawRoundRect(mLightBgRect, radius, radius, mPaint);
}
private void drawArrow(Canvas canvas) {
if (mArrowDrawable != null) {
float percent = 1 - mProgress / (float) mMaxProgress;
int top = (int) (mProgressBgRect.top + (mProgressSpaceV * 2) + (percent * mProgressRectHeight) - mArrowRectHeight / 2f);
mArrowRect.set(0, 0, (int) mArrowRectWidth, (int) mArrowRectHeight);
mArrowRect.offsetTo((int) (getPaddingLeft() + mTextMaxWidth), top);
mArrowDrawable.setBounds(mArrowRect);
mArrowDrawable.draw(canvas);
}
}
private void drawScaleLine(Canvas canvas) {
try {
mPaint.setShader(null);
mPaint.setColor(0xFF8D9193);
float lineSpacingH = mProgressRectWidth / 6f;
float lineSpacingV = mProgressRectHeight / (float) (SCALE_LINE_COUNT - 1);
float lineStartY = mProgressBgRect.top + mProgressSpaceV * 2;
float lineWidthBig = mProgressRectWidth - lineSpacingH;
float lineWidthSmall = lineWidthBig / 2f;
float left_stopX = mProgressBgRect.left - lineSpacingH;
float left_startX_big = left_stopX - lineWidthBig;
float left_startX_small = left_stopX - lineWidthSmall;
float right_startX = mProgressBgRect.right + lineSpacingH;
float right_stopX_big = right_startX + lineWidthBig;
float right_stopX_small = right_startX + lineWidthSmall;
for (int i = 0; i < SCALE_LINE_COUNT; i++) {
float startY = lineStartY + i * lineSpacingV;
float[] pts = {left_startX_big, startY, left_stopX, startY, right_startX, startY, right_stopX_big, startY};
if (i % 2 != 0) {
pts[0] = left_startX_small;
pts[6] = right_stopX_small;
}
canvas.drawLines(pts, mPaint);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void drawText(Canvas canvas) {
float startY = mProgressRectHeight / (float) (TEXT_VALUES.length - 1);
for (int i = 0; i < TEXT_VALUES.length; i++) {
String text = TEXT_VALUES[i];
float textWidth = mPaintText.measureText(text);
float x = getPaddingLeft() + mTextMaxWidth - textWidth;
float y = DensityUtils.getTextBaseLine(mPaintText, (mProgressBgRect.top + mProgressSpaceV * 2) + i * startY);
canvas.drawText(text, x, y, mPaintText);
}
}
private void drawProgressBg(Canvas canvas) {
mPaint.setShader(null);
mPaint.setColor(0xFF000000);
float newLeft = mBgRect.left + mLightSpace + (mLightRectWidth - mProgressBgRectWidth) / 2f;
mProgressBgRect.set(0, 0, mProgressBgRectWidth, mProgressBgRectHeight);
mProgressBgRect.offsetTo(newLeft, getPaddingTop() + mLightSpace + mThumbRectHeight / 2f);
float radius = mProgressBgRect.width() / 6f;
canvas.drawRoundRect(mProgressBgRect, radius, radius, mPaint);
}
private void drawProgress(Canvas canvas) {
//1.绘制默认背景
mPaint.setColor(0xFF262626);
mPaint.setShader(null);
drawSpectrum(canvas, 0, PROGRESS_RECT_COUNT, 1.0f);
//2.绘制彩色的进度
setPaintShader(mPaint);
float sampleValue = Math.max(0, Math.min(1.0f, mProgress / (float) mMaxProgress));
int pos = (int) (sampleValue * PROGRESS_RECT_COUNT);
//2.1 绘制完整方块的彩色进度
drawSpectrum(canvas, 0, pos - 1, 1.0f);
//2.1 绘制不完整方块的彩色进度
float factor = sampleValue * PROGRESS_RECT_COUNT - pos;
if (factor > 0) {
drawSpectrum(canvas, pos, pos, factor);
}
}
private void drawSpectrum(Canvas canvas, int startPos, int endPos, float factor) {
for (int i = PROGRESS_RECT_COUNT - 1 - startPos; i >= PROGRESS_RECT_COUNT - 1 - endPos; i--) {
if (i >= 0) {
drawRect(canvas, i, factor);
}
}
}
private void drawRect(Canvas canvas, int position, float factor) {
// 计算矩形的绘制点
float bottom = mProgressRectHeight - (PROGRESS_RECT_COUNT - position - 1) * (mProgressMinHeight + mProgressSpaceV);
float top = bottom - mProgressMinHeight * factor;// 进度阀值,也就是绘制进度矩形时的top
// 开始绘制矩形
mProgressRect.set(0, top, mProgressRectWidth, bottom);
mProgressRect.offset(mProgressBgRect.left + mProgressSpaceH, mProgressBgRect.top + mProgressSpaceV * 2);
canvas.drawRect(mProgressRect, mPaint);
}
private void setPaintShader(Paint paint) {
if (paint != null) {
paint.setShader(new LinearGradient(mProgressBgRect.left + mProgressSpaceH,
mProgressBgRect.bottom - mProgressSpaceV * 2,
mProgressBgRect.left + mProgressSpaceH,
mProgressBgRect.top + mProgressSpaceV * 2,
mLinearGradient, null, Shader.TileMode.CLAMP));
}
}
private void drawThumb(Canvas canvas) {
if (mThumbDrawable != null) {
float percent = 1 - mProgress / (float) mMaxProgress;
int top = (int) (mProgressBgRect.top + (mProgressSpaceV * 2) + (percent * mProgressRectHeight) - mThumbRectHeight / 2f);
mThumbRect.set(0, 0, (int) mThumbRectWidth, (int) mThumbRectHeight);
mThumbRect.offsetTo((int) (mProgressBgRect.left - (mThumbRectWidth - mProgressBgRect.width()) / 2f), top);
mThumbDrawable.setBounds(mThumbRect);
mThumbDrawable.setState(isEnabled() ? ViewState.DRAWABLE_STATE_ENABLED : ViewState.DRAWABLE_STATE_DISABLED);
mThumbDrawable.draw(canvas);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isPressed = isPressed(event);
if (isPressed) {
mLastPoint.set(event.getX(), event.getY());
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStartTrackingTouch(this);
}
}
break;
case MotionEvent.ACTION_MOVE:
if (isPressed) {
if (calculateProgress(event.getX() - mLastPoint.x, event.getY() - mLastPoint.y)) {
mLastPoint.set(event.getX(), event.getY());
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
isPressed = false;
invalidate();
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStopTrackingTouch(this);
}
break;
}
return true;
}
private boolean isPressed(MotionEvent event) {
return (event.getX() >= mThumbRect.left && event.getX() <= mThumbRect.right
&& event.getY() >= mThumbRect.top && event.getY() <= mThumbRect.bottom);
}
/**
* 根据手指移动距离计算进度
*/
private boolean calculateProgress(float dx, float dy) {
int dProgress = -(int) (dy * mMaxProgress / mProgressRectHeight);
if (dProgress != 0) {
setProgress(mProgress + dProgress, true);
return true;
}
return false;
}
public void setProgress(int progress) {
if (progress < 0) {
progress = 0;
}
if (progress > mMaxProgress) {
progress = mMaxProgress;
}
if (mProgress != progress) {
setProgress(progress, false);
}
}
public void setProgress(int progress, boolean fromUser) {
if (progress < 0) {
progress = 0;
}
if (progress > mMaxProgress) {
progress = mMaxProgress;
}
if (mProgress != progress) {
mProgress = progress;
onSizeChanged(getWidth(), getHeight(), 0, 0);
invalidate();
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgressChanged(this, progress, fromUser);
}
}
}
public void setOnSeekBarChangeListener(OnSeekBarBoosterChangeListener listener) {
this.mOnSeekBarChangeListener = listener;
}
public interface OnSeekBarBoosterChangeListener {
void onProgressChanged(SeekBarBoosterView2 seekBar, int progress, boolean fromUser);
void onStartTrackingTouch(SeekBarBoosterView2 seekBar);
void onStopTrackingTouch(SeekBarBoosterView2 seekBar);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec));
// 1.计算最长宽度的字符串
if (mPaintText != null) {
String maxText = "";
for (String text : TEXT_VALUES) {
if (text.length() > maxText.length()) {
maxText = text;
}
}
mTextMaxWidth = mPaintText.measureText(maxText);
}
// 2.计算背景矩形的的宽高
mBgRectWidth = getMeasuredHeight() / 2.4f;
mBgRectHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
// 3.计算渐变矩形的宽高
mLightSpace = mBgRectWidth / 14f;
mLightRectWidth = mBgRectWidth - mLightSpace * 2;
mLightRectHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - mLightSpace * 2;
// 4.计算箭头矩形的宽高
mArrowSpace = mBgRectWidth / 10f;
mArrowRectWidth = mBgRectWidth + mArrowSpace * 2;
mArrowRectHeight = mArrowRectWidth * 10 / 360f;// 按照比例计算高度
// 5.计算滑块矩形的宽高
mThumbRectWidth = mLightRectWidth / 2.4f;
if (mThumbDrawable != null) {
mThumbRectHeight = (int) (mThumbDrawable.getIntrinsicHeight() / (float) mThumbDrawable.getIntrinsicWidth() * mThumbRectWidth);
}
// 6.计算进度背景矩形的宽高
mProgressBgRectWidth = mLightRectWidth / 4f;
mProgressBgRectHeight = mLightRectHeight - mThumbRectHeight;
// 7.计算进度矩形的宽高
mProgressSpaceH = mProgressBgRectWidth / 6f;// 进度矩形左右两边的距离
mProgressSpaceV = mProgressSpaceH / 2f;// 单个进度矩形上下之间的距离
mProgressRectWidth = mProgressBgRectWidth - mProgressSpaceH * 2;// 进度的宽度
mProgressRectHeight = mProgressBgRectHeight - mProgressSpaceV * 4;// 进度的总体高度
// 8.计算进度小方块矩形的宽高
mProgressMinWidth = mProgressRectWidth;
mProgressMinHeight = (mProgressRectHeight - mProgressSpaceV * (PROGRESS_RECT_COUNT - 1)) / (float) PROGRESS_RECT_COUNT;
// 根据高度按比例测量控件宽度
int measureWidth = getPaddingLeft() + getPaddingRight() + (int) (mTextMaxWidth + mBgRectWidth + mArrowSpace * 2);
super.onMeasure(MeasureSpec.makeMeasureSpec(measureWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
}
}