Android 间隔进度拖动-IntervalSeekBar

效果图
在这里插入图片描述
源码-复制即用
FIX:2021-12-03监听拖动问题

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

/**
 * 间距拖动
 */
public class IntervalSeekBar extends View {

    //圆圈颜色
    private int circleColor = Color.WHITE;
    //圆圈半径
    private int radius = 20;
    //背景颜色
    private int backgroundColor = Color.parseColor("#929292");
    //进度颜色
    private int progressColor = Color.parseColor("#003AFD");
    //线条宽度
    private int lineWidth = 8;
    //中心位置
    private int centerX, centerY;
    //宽度、高度
    private int width, height;
    //左边进度
    private int leftProgress = 20;
    //右边进度
    private int rightProgress = 80;
    //水平间距
    private int marginHorizontal = 40;
    //垂直间距
    private int marginVertical = 40;
    //左边坐标
    private int[] leftCoordinate;
    //右边坐标
    private int[] rightCoordinate;

    private Paint paint;

    public IntervalSeekBar(Context context) {
        super(context);
    }

    public IntervalSeekBar(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public IntervalSeekBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        width = getMeasuredWidth();
        height = getMeasuredHeight();
        centerX = getMeasuredWidth() / 2;
        centerY = getMeasuredHeight() / 2;
        int requiredWidth = width, requiredHeight = radius * 2 + marginVertical * 2;
        int measureSpecWidth = MeasureSpec.getSize(widthMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int measureSpecHeight = MeasureSpec.getSize(heightMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int measureWidth = measureSpecWidth;
        int measureHeight = measureSpecHeight;
        if ((widthSpecMode == MeasureSpec.AT_MOST || widthSpecMode == MeasureSpec.UNSPECIFIED) && heightSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.UNSPECIFIED) {
            measureWidth = requiredWidth;
            measureHeight = requiredHeight;
        } else if (widthSpecMode == MeasureSpec.AT_MOST || widthSpecMode == MeasureSpec.UNSPECIFIED) {
            measureWidth = requiredWidth;
            measureHeight = measureSpecHeight;
        } else if (heightSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.UNSPECIFIED) {
            measureWidth = measureSpecWidth;
            measureHeight = requiredHeight;
        }
        setMeasuredDimension(measureWidth, measureHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawBackground(canvas);
        drawProgress(canvas, true);
        drawProgress(canvas, false);
        drawCircle(canvas, true);
        drawCircle(canvas, false);
    }

    /**
     * 绘制背景
     *
     * @param canvas
     */
    private void drawBackground(Canvas canvas) {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(backgroundColor);
        paint.setStrokeWidth(lineWidth);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        int lineWidth = width - 2 * marginHorizontal;
        int lineStartX = marginHorizontal;
        int lineStopX = marginHorizontal + lineWidth;
        paint.setColor(backgroundColor);
        canvas.drawLine(lineStartX, centerY, lineStopX, centerY, paint);
    }

    /**
     * 绘制进度
     *
     * @param canvas
     * @param left
     */
    private void drawProgress(Canvas canvas, boolean left) {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setColor(progressColor);
        paint.setStrokeWidth(lineWidth);
        int lineWidth = width - 2 * marginHorizontal;
        int lineLeftStartX = marginHorizontal;
        int lineLeftStopX = marginHorizontal + lineWidth * leftProgress / 100;
        if (left) {
            paint.setColor(backgroundColor);
            canvas.drawLine(lineLeftStartX, centerY, lineLeftStopX, centerY, paint);
        }
        int lineRightStartX = marginHorizontal + lineWidth * leftProgress / 100;
        int lineSRightStopX = marginHorizontal + lineWidth * rightProgress / 100;
        if (!left) {
            paint.setColor(progressColor);
            canvas.drawLine(lineRightStartX, centerY, lineSRightStopX, centerY, paint);
        }
    }

    /**
     * 绘制圆圈
     *
     * @param canvas
     * @param left
     */
    private void drawCircle(Canvas canvas, boolean left) {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setColor(circleColor);
        paint.setStrokeWidth(lineWidth);
        int lineWidth = width - 2 * marginHorizontal;
        int lineLeftStopX = marginHorizontal + lineWidth * leftProgress / 100;
        if (left) {
            Shader shader = new RadialGradient(lineLeftStopX, centerY, radius, new int[]{Color.WHITE, Color.GRAY}, new float[]{0.9f, 1.0f}, Shader.TileMode.MIRROR);
            paint.setShader(shader);
            canvas.drawCircle(lineLeftStopX, centerY, radius, paint);
            leftCoordinate = new int[]{lineLeftStopX, centerY};
        }
        int lineRightStopX = marginHorizontal + lineWidth * rightProgress / 100;
        if (!left) {
            Shader shader = new RadialGradient(lineRightStopX, centerY, radius, new int[]{Color.WHITE, Color.GRAY}, new float[]{0.9f, 1.0f}, Shader.TileMode.MIRROR);
            paint.setShader(shader);
            canvas.drawCircle(lineRightStopX, centerY, radius, paint);
            rightCoordinate = new int[]{lineRightStopX, centerY};
        }
    }

    /**
     * 是否左边
     *
     * @param x
     * @param y
     * @return
     */
    private boolean isLeft(float x, float y) {
        if (x <= leftCoordinate[0] + radius
                && y >= leftCoordinate[1] - radius * 2
                && y <= leftCoordinate[1] + radius * 2) {
            return true;
        }
        return false;
    }

    /**
     * 是否右边
     *
     * @param x
     * @param y
     * @return
     */
    private boolean isRight(float x, float y) {
        if (x >= rightCoordinate[0] - radius * 2
                && y >= rightCoordinate[1] - radius * 2
                && y <= rightCoordinate[1] + radius * 2) {
            return true;
        }
        return false;
    }

    private boolean isLeft;
    private boolean isRight;
    private float downX, downY;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                isLeft = isLeft(downX, downY);
                isRight = isRight(downX, downY);
                break;
            case MotionEvent.ACTION_MOVE:
                if (isLeft) {
                    int leftMoveX = (int) (event.getX() - leftCoordinate[0]);
                    if (leftMoveX != 0) {
                        leftProgress = (leftCoordinate[0] + leftMoveX) * 100 / width;
                    }
                    if (leftProgress > 100) {
                        leftProgress = 100;
                    }
                    if (leftProgress < 0) {
                        leftProgress = 0;
                    }
                }
                if (isRight) {
                    int rightMoveX = (int) (event.getX() - rightCoordinate[0]);
                    if (rightMoveX != 0) {
                        rightProgress = (rightCoordinate[0] + rightMoveX) * 100 / width;
                    }
                    if (rightProgress > 100) {
                        rightProgress = 100;
                    }
                    if (rightProgress < 0) {
                        rightProgress = 0;
                    }
                }
                int spaceProgress = radius * 3 * 100 / (width - 2 * marginHorizontal);
                if (leftProgress < rightProgress - spaceProgress) {
                    invalidate();
                    if (onSeekBarChangeListener != null) {
                        onSeekBarChangeListener.onProgressChanged(this, leftProgress, rightProgress);
                    }
                }
                break;
        }
        return true;
    }

    private OnSeekBarChangeListener onSeekBarChangeListener;

    /**
     * 设置监听
     *
     * @param onSeekBarChangeListener
     */
    public void setOnSeekBarChangeListener(OnSeekBarChangeListener onSeekBarChangeListener) {
        this.onSeekBarChangeListener = onSeekBarChangeListener;
    }

    public interface OnSeekBarChangeListener {
        void onProgressChanged(IntervalSeekBar seekBar, int leftProgress, int rightProgress);
    }

}