The first reaction was to use SeekBar. After debugging, I found that SeekBar could not meet the company’s business needs and UI design effect. Therefore, I browsed the custom draggable progress bar and hand-wrote a custom View.

Function:

  • 1. Dragging progress bar can be controlled or prohibited;
  • 2. Customize the height of the progress bar and the dot radius of the progress indicator;
  • 3. Customize the background color of progress bar, progress color of progress bar and dot color of progress indicator;
  • 4. Customize the color of the progress bar and the dot color of the progress indicator in the dragging state.

Example in the draggable state:

Example in the forbidden drag state:

Without further ado, go to the code.

1, custom view part code:

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

/**
 * 自定义可拖动的进度条
 */
public class DragProgressView extends View {

    // 灰色背景线段的画笔
    private Paint bgPaint;
    // 实际进度绿色线段的画笔
    private Paint progressPaint;
    // 圆点指示器的画笔
    private Paint circlePaint;
    // 进度条的最大宽度
    private float maxProgress;
    // 进度条当前的宽度
    private float currentProgress;
    // 当前View的宽度
    private int width;
    // 当前View的高度
    private int height;
    // 距离左边的内边距
    private int paddingLeft;
    // 距离右边的内边距
    private int paddingRight;
    // 是否可拖动
    private boolean isDrag = true;
    // 圆点指示器的半径 默认10dp
    private int mCircleRadius;
    // 进度条高度 默认10dp
    private int progressHeight;
    // 进度条背景颜色
    private int progressBackgroundColor;
    // 进度条颜色
    private int progressColor;
    // 圆形指示器颜色
    private int circleColor;
    // 圆形指示器阴影颜色
    private int circleShadowColor;
    // 进度条不可拖动时颜色
    private int progressColorN;
    // 圆形指示器不可拖动时颜色
    private int circleColorN;
    // 圆形指示器阴影不可拖动时颜色
    private int circleShadowColorN;

    private Context mContext;

    private String TAG = getClass().getSimpleName();

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

    public DragProgressView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    /**
     * 初始化画笔
     */
    private void initPaint() {
        // 进度条背景画笔
        bgPaint = new Paint();
        bgPaint.setColor(progressBackgroundColor);  // 进度条背景颜色
        bgPaint.setStyle(Paint.Style.FILL_AND_STROKE);      // 填充且描边
        bgPaint.setAntiAlias(true);                 // 抗锯齿
        bgPaint.setStrokeCap(Paint.Cap.ROUND);      // 线冒的头是圆的
        bgPaint.setStrokeWidth(progressHeight);     // 进度条背景高度  dp转px

        // 设置进度画笔
        progressPaint = new Paint();
        progressPaint.setColor(progressColor);          // 进度条进度颜色
        progressPaint.setStyle(Paint.Style.FILL_AND_STROKE);    // 填充且描边
        progressPaint.setAntiAlias(true);               // 抗锯齿
        progressPaint.setStrokeCap(Paint.Cap.ROUND);    // 线冒的头圆的
        progressPaint.setStrokeWidth(progressHeight);   // 进度条进度高度 3dp转px

        // 圆点指示器
        circlePaint = new Paint();
        circlePaint.setAntiAlias(true);         // 设置抗锯齿
        circlePaint.setColor(circleColor);      // 圆点指示器颜色
        circlePaint.setShadowLayer(dip2px(mContext, 2), 0, 0, circleShadowColor); // 圆点指示器阴影颜色
        circlePaint.setStyle(Paint.Style.FILL); // 填充
    }

    /**
     * 初始化高度、颜色等可自定义参数
     */
    private void getAttrs(Context context, AttributeSet attrs) {
        mContext = context;
        Resources resources = context.getResources();
        progressBackgroundColor = resources.getColor(R.color.text_colorc);
        progressColor = resources.getColor(R.color.toolbar_background);
        circleColor = resources.getColor(R.color.toolbar_background);
        circleShadowColor = resources.getColor(R.color.white);
        if (attrs != null) {
            TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.dragProgressView);
            isDrag = mTypedArray.getBoolean(R.styleable.dragProgressView_isDrag, true); // 默认可拖动
            mCircleRadius = mTypedArray.getLayoutDimension(R.styleable.dragProgressView_circleRadius, dip2px(context, 10));
            progressHeight = mTypedArray.getLayoutDimension(R.styleable.dragProgressView_progressHeight, dip2px(context, 10));
            progressBackgroundColor = mTypedArray.getColor(R.styleable.dragProgressView_progressBackgroundColor, progressBackgroundColor);
            progressColor = mTypedArray.getColor(R.styleable.dragProgressView_progressColor, progressColor);
            circleColor = mTypedArray.getColor(R.styleable.dragProgressView_circleColor, circleColor);
            circleShadowColor = mTypedArray.getColor(R.styleable.dragProgressView_circleShadowColor, circleShadowColor);
            progressColorN = mTypedArray.getColor(R.styleable.dragProgressView_progressColorN, progressColor);
            circleColorN = mTypedArray.getColor(R.styleable.dragProgressView_circleColorN, circleColor);
            circleShadowColorN = mTypedArray.getColor(R.styleable.dragProgressView_circleShadowColorN, circleShadowColor);
            mTypedArray.recycle();
        } else {
            isDrag = true;
            mCircleRadius = dip2px(context, 10);
            progressHeight = dip2px(context, 10);
            progressColorN = progressColor;
            circleColorN = circleColor;
            circleShadowColorN = circleShadowColor;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int minHeight = mCircleRadius * 2 + (dip2px(mContext, 2) * 2);
        int height = resolveSize(minHeight, heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        // 初始化几个距离参数
        width = getWidth();     // view的宽度
        height = getHeight();   // view的高度

        // 让左边距至少为半个圆点指示器的距离
        paddingLeft = getPaddingLeft(); // 距离左边的距离
        if (getPaddingLeft() < mCircleRadius) {
            paddingLeft = mCircleRadius;
        }
        // 让右边距至少为半个圆点指示器的距离
        paddingRight = getPaddingRight();   // 距离右边的距离
        if (getPaddingRight() < mCircleRadius) {
            paddingRight = mCircleRadius;
        }

        // 如果当前进度小于左边距
        setCurrentProgress();
        // 最大进度长度等于View的宽度-(左边的内边距+右边的内边距)
        maxProgress = width - paddingLeft - paddingRight;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (isDrag) {
            progressPaint.setColor(progressColor);  // 进度条颜色
            circlePaint.setColor(circleColor);      // 指示器颜色
        } else {
            progressPaint.setColor(progressColorN); // 进度条颜色
            circlePaint.setColor(circleColorN);     // 指示器颜色
        }
        // 绘制进度条背景
        // 从(左边距,View高度的一半)开始,到(View宽度-右边距,View高度的一半)还将绘制灰色背景线段
        canvas.drawLine(paddingLeft, height / 2, width - paddingRight, height / 2, bgPaint);
        // 绘制进度条进度
        // 从(左边距,View高度的一半)开始,到(现在的触摸到的进度宽度,View高度的一半)还将绘制灰色背景线段
        canvas.drawLine(paddingLeft, height / 2, currentProgress, height / 2, progressPaint);
        // 要支持阴影下过必须关闭硬件加速
        setLayerType(LAYER_TYPE_SOFTWARE, null);// 发光效果不支持硬件加速
        // 绘制圆点指示器
        canvas.drawCircle(currentProgress, getHeight() / 2, mCircleRadius, circlePaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:   // 按下
                // 设置进度值
                setMotionProgress(event);
                return true;
            case MotionEvent.ACTION_MOVE:   // 移动
                // 获取当前触摸点,赋值给当前进度
                setMotionProgress(event);
                return true;
        }
        return super.onTouchEvent(event);
    }

    /**
     * 根据用户手势计算进度值
     * @param event 用户手势操作事件
     */
    private void setMotionProgress(MotionEvent event) {
        if (!isDrag) {
            return;
        }
        // 获取当前触摸点,赋值给当前进度
        currentProgress = (int) event.getX();
        // 如果当前进度小于左边距
        setCurrentProgress();
        // 实际百分比进度数值
        float result = ((currentProgress - paddingLeft) * 100) / maxProgress;
        // 进行空值判断
        if (onProgressListener != null) {
            onProgressListener.onProgressChanged((int) result);
        }
        invalidate();
    }

    // 设置当前进度条进度,从1到100
    public void setProgress(int progress) {
        if (progress > 100 || progress < 0) {
            return;
        }
        setCurrentProgress();
        //设置当前进度的宽度
        currentProgress = ((progress * maxProgress) / 100) + paddingLeft;
        if (onProgressListener != null) {
            onProgressListener.onProgressChanged(progress);
        }
        invalidate();
    }

    // 如果当前进度超出边界,将当前进度赋值为边界极值
    private void setCurrentProgress() {
        if (currentProgress < paddingLeft) {    // 如果当前进度小于左边距
            currentProgress = paddingLeft;
        } else if (currentProgress > width - paddingRight) {  // 如果当前进度大于 宽度 - 右边距
            currentProgress = width - paddingRight;
        }
    }

    /**
     * 获取当前是否可拖动状态
     * @return isDrag true 可拖动; false 不可拖动。
     */
    public boolean isDrag() {
        return isDrag;
    }

    /**
     * 设置当前是否可拖动,立即重绘
     * @param drag true 可拖动; false 不可拖动。
     */
    public void setDrag(boolean drag) {
        isDrag = drag;
        invalidate();
    }

    private OnProgressListener onProgressListener;

    public interface OnProgressListener {
        void onProgressChanged(int progress);
    }

    // 设置拖动进度监听
    public void setOnProgressListener(OnProgressListener onProgressListener) {
        this.onProgressListener = onProgressListener;
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        float scale = 1;
        scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
     */
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 sp
     */
    public static int px2sp(Context context, float pxValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }
}
Copy the code

Add the following code to the attrs file under values:

<! <declare-styleable name=" DragProgressView "> <attr name="isDrag" format=" Boolean "/> <attr name="circleRadius" format="dimension" /> <attr name="progressHeight" format="dimension" /> <attr name="progressBackgroundColor" format="color" /> <attr name="progressColor" format="color" /> <attr name="circleColor" format="color" /> <attr name="circleShadowColor" format="color" /> <attr name="progressColorN" format="color" /> <attr name="circleColorN" format="color" /> <attr name="circleShadowColorN" format="color" /> </declare-styleable>Copy the code

3. Use in layout XML

<com.example.view.DragProgressView
    android:id="@+id/db_progress"
    android:layout_marginTop="40dp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="15dp"
    android:layout_marginRight="15dp"
    apps:isDrag="true"
    apps:circleRadius="10dp"
    apps:progressHeight="10dp"
    apps:progressBackgroundColor="@color/text_colorc"
    apps:progressColor="@color/toolbar_background"
    apps:circleColor="@color/toolbar_background"
    apps:circleShadowColor="@color/settings_chart_progress_circle_shadow"
    apps:progressColorN="@color/text_color8"
    apps:circleColorN="@color/text_color8"
    apps:circleShadowColorN="@color/white" />
Copy the code

4. Use in an Activity or Fragment

DragProgressView dbProgress = findViewById(R.id.db_progress); dbProgress.setOnProgressListener(new DragProgressView.OnProgressListener() { @Override public void onProgressChanged(int  progress) { Log.v("progress", "... DbProgress :" + progress); }}); Dbprogress.setprogress (chartProgress); // Get the default value from the server or custom display. boolean isDrag = true; Dbprogress.setdrag (isDrag); dbprogress.setdrag (isDrag);Copy the code

Above is a custom view can drag horizontal progress bar all the code, the code has a detailed explanation, if do not meet the business needs, can continue to modify on this basis. Welcome to the relevant business needs of the reference study, reproduced please indicate the address.