SeekBar的几种常见用法


前言

SeekBar是Android的常用控件之一,简单总结下几种用法。

一、SeekBar基础用法

SeekBar是ProgressBar的扩展,是一个可以拖动的进度条,这样用户就可以通过拖动控制条来改变进度。系统默认只有水平样式。

1、水平样式

在这里插入图片描述

2、常用属性

  1. 设置进度条范围最大值
    android:max=“”
  2. 设置进度条范围最小值
    android:min=“”
  3. 设置当前进度值
    android:progress=“”
  4. 设置第二进度值(类似缓冲进度)
    android:secondaryProgress =“”
  5. 设置进度条的图片
    android:progressDrawable = “”
  6. 设置进度条的滑块图片
    android:thumb = “”

2、布局写法

  <SeekBar
                android:id="@+id/sb_number"
                android:layout_marginStart="@dimen/x38"
                android:layout_marginEnd="@dimen/x42"
                android:background="@null"
                android:splitTrack="false"
                android:layout_width="@dimen/x540"
                android:layout_height="wrap_content"
                android:maxHeight="@dimen/x10"
                android:progressDrawable="@drawable/seekbar_volume"
                android:thumb="@drawable/icon_seekbar_thumb"/>

3、代码调用

 SeekBar seekBar = contentView.findViewById(R.id.sb_number);
        seekBar.setProgress(100);
        seekBar.setMin(0);
        seekBar.setMax(finalMaxTypeVolume);
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                // 进度变化时的回调
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
 				// 开始拖动时的回调
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
				// 停止拖动时的回调
            }
        });

二、垂直seekBar

默认的SeekBar没有设置方向的功能,垂直方向需要重新SeekBar.

1.关键代码,重新onDraw方法,将控件旋转90度

     
  protected void onDraw(Canvas c) {
            c.rotate(-90);
            c.translate(-getHeight(), 0);

            super.onDraw(c);
        }

三、圆弧seekBar

圆弧seekBar可以用来在复杂的界面使用,相对代码更加复杂,本文方法自定义view绘制。

1. 自定义圆弧SeekBar


/**
 * 圆弧 SeekBar
 */
public class ArcSeekBarPro extends View {

    /**
     * 画笔
     */
    private Paint mPaint;

    /**
     * 笔画描边的宽度
     */
    private float mStrokeWidth;

    private Paint.Cap mStrokeCap = Paint.Cap.ROUND;

    /**
     * 默认的控件宽度,也是圆弧直径
     */
    private int defaultArcDiameter;

    /**
     * 开始角度
     */
    private int mStartAngle = 0;
    /**
     * 路径角度
     */
    private int mSweepAngle = 30;

    /**
     * 圆心坐标x
     */
    private float mCircleCenterX;
    /**
     * 圆心坐标y
     */
    private float mCircleCenterY;

    /**
     * 弧形 正常颜色
     */
    private int mNormalColor = 0x80D0D4D9;
    /**
     * 进度颜色
     */
    private int mProgressColor = 0x99FFFFFF;

    /**
     * 半径
     */
    private float mRadius;

    /**
     * 最大进度
     */
    private int mMax = 10;

    /**
     * 当前进度
     */
    private int mProgress = 0;

    /**
     * 进度百分比
     */
    private int mProgressPercent;

    private Bitmap mThumbBitmap;
    private Matrix mMatrix;

    /**
     * 拖动按钮的画笔宽度
     */
    private float mThumbStrokeWidth;
    /**
     * 拖动按钮的颜色
     */
    private int mThumbColor = 0xFFD5BDAD;
    /**
     * 拖动按钮的半径
     */
    private float mThumbRadius;
    /**
     * 拖动按钮的中心点X坐标
     */
    private float mThumbCenterX;
    /**
     * 拖动按钮的中心点Y坐标
     */
    private float mThumbCenterY;
    /**
     * 触摸时可偏移距离
     */
    private float mAllowableOffsets;
    /**
     * 触摸时按钮半径放大量
     */
    private float mThumbRadiusEnlarges;

    /**
     * 是否显示拖动按钮
     */
    private boolean isShowThumb = true;

    /**
     * 手势,用来处理点击事件
     */
    private GestureDetector mDetector;

    /**
     * 是否可以拖拽
     */
    private boolean isCanDrag = false;

    /**
     * 是否启用拖拽改变进度
     */
    private boolean isEnabledDrag = true;

    /**
     * 是否启用点击改变进度
     */
    private boolean isEnabledSingle = true;

    private OnChangeListener mOnChangeListener;


    public ArcSeekBarPro(Context context) {
        this(context, null);
    }

    public ArcSeekBarPro(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ArcSeekBarPro(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ArcSeekBarPro);
        mThumbBitmap = Utils.loadBitmap(R.drawable.icon_seekbar_pro_thumb, context);
        mMatrix = new Matrix();
        DisplayMetrics displayMetrics = getDisplayMetrics();
        defaultArcDiameter = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.arc_diameter), getDisplayMetrics());
        mStrokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.eac_control_thumb), displayMetrics);
        mThumbRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.thumb_drawable_radius), displayMetrics);

        mThumbStrokeWidth = mThumbRadius;

        mAllowableOffsets = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, displayMetrics);

        mThumbRadiusEnlarges = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, displayMetrics);

        int size = a.getIndexCount();
        for (int i = 0; i < size; i++) {
            int attr = a.getIndex(i);
            if (attr == R.styleable.ArcSeekBarPro_arcStrokeWidth) {
                mStrokeWidth = a.getDimension(attr, mStrokeWidth);
            } else if (attr == R.styleable.ArcSeekBarPro_arcStrokeCap) {
                mStrokeCap = getStrokeCap(a.getInt(attr, 3));
            } else if (attr == R.styleable.ArcSeekBarPro_arcNormalColor) {
                mNormalColor = a.getColor(attr, mNormalColor);
            } else if (attr == R.styleable.ArcSeekBarPro_arcProgressColor) {
                mProgressColor = a.getColor(attr, mProgressColor);
            } else if (attr == R.styleable.ArcSeekBarPro_arcStartAngle) {
                mStartAngle = a.getInt(attr, mStartAngle);
            } else if (attr == R.styleable.ArcSeekBarPro_arcSweepAngle) {
                mSweepAngle = a.getInt(attr, mSweepAngle);
            } else if (attr == R.styleable.ArcSeekBarPro_arcMax) {
                int max = a.getInt(attr, mMax);
                if (max > 0) {
                    mMax = max;
                }
            } else if (attr == R.styleable.ArcSeekBarPro_arcProgress) {
                mProgress = a.getInt(attr, mProgress);
            } else if (attr == R.styleable.ArcSeekBarPro_arcThumbStrokeWidth) {
                mThumbStrokeWidth = a.getDimension(attr, mThumbStrokeWidth);
            } else if (attr == R.styleable.ArcSeekBarPro_arcThumbColor) {
                mThumbColor = a.getColor(attr, mThumbColor);
            } else if (attr == R.styleable.ArcSeekBarPro_arcThumbRadius) {
                mThumbRadius = a.getDimension(attr, mThumbRadius);
            } else if (attr == R.styleable.ArcSeekBarPro_arcThumbRadiusEnlarges) {
                mThumbRadiusEnlarges = a.getDimension(attr, mThumbRadiusEnlarges);
            } else if (attr == R.styleable.ArcSeekBarPro_arcShowThumb) {
                isShowThumb = a.getBoolean(attr, isShowThumb);
            } else if (attr == R.styleable.ArcSeekBarPro_arcAllowableOffsets) {
                mAllowableOffsets = a.getDimension(attr, mAllowableOffsets);
            } else if (attr == R.styleable.ArcSeekBarPro_arcEnabledDrag) {
                isEnabledDrag = a.getBoolean(attr, true);
            } else if (attr == R.styleable.ArcSeekBarPro_arcEnabledSingle) {
                isEnabledSingle = a.getBoolean(attr, true);
            }
        }

        a.recycle();
        mProgressPercent = (int) (mProgress * 100.0f / mMax);
        mPaint = new Paint();

        mDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapUp(MotionEvent event) {

                if (isInArc(event.getX(), event.getY())) {
                    updateDragThumb(event.getX(), event.getY(), true);
                    if (mOnChangeListener != null) {
                        mOnChangeListener.onSingleTapUp();
                    }
                    return true;
                }

                return super.onSingleTapUp(event);
            }
        });

    }

    private Paint.Cap getStrokeCap(int value) {
        switch (value) {
            case 1:
                return Paint.Cap.BUTT;
            case 2:
                return Paint.Cap.SQUARE;
            default:
                return Paint.Cap.ROUND;
        }
    }


    private DisplayMetrics getDisplayMetrics() {
        return getResources().getDisplayMetrics();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int width = measureHandler(widthMeasureSpec, defaultArcDiameter);
        int height = measureHandler(heightMeasureSpec, defaultArcDiameter);

        //圆心坐标
        mCircleCenterX = (width + getPaddingLeft() - getPaddingRight()) / 2.0f;
        mCircleCenterY = (height + getPaddingTop() - getPaddingBottom()) / 2.0f;
        //计算间距
        int padding = Math.max(getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom());
        //半径=视图宽度-横向或纵向内间距值 - 画笔宽度
        mRadius = (width - padding - Math.max(mStrokeWidth, mThumbStrokeWidth)) / 2.0f - mThumbRadius;

        setMeasuredDimension(width, height);

    }

    /**
     * 测量
     *
     * @param measureSpec
     * @param defaultSize
     * @return
     */
    private int measureHandler(int measureSpec, int defaultSize) {

        int result = defaultSize;
        int measureMode = MeasureSpec.getMode(measureSpec);
        int measureSize = MeasureSpec.getSize(measureSpec);
        if (measureMode == MeasureSpec.EXACTLY) {
            result = measureSize;
        } else if (measureMode == MeasureSpec.AT_MOST) {
            result = Math.min(defaultSize, measureSize);
        }
        return result;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawArc(canvas);
        drawThumb(canvas);
    }


    /**
     * 绘制弧形
     *
     * @param canvas
     */
    private void drawArc(Canvas canvas) {
        mPaint.reset();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);

        mPaint.setStrokeWidth(mStrokeWidth);
        mPaint.setShader(null);
        mPaint.setStrokeCap(mStrokeCap);

        //进度圆半径
        float diameter = mRadius * 2;
        float startX = mCircleCenterX - mRadius;
        float startY = mCircleCenterY - mRadius;
        RectF rectF1 = new RectF(startX, startY, startX + diameter, startY + diameter);

        if (mNormalColor != 0) {
            mPaint.setColor(mNormalColor);
            //绘制底层弧形
            canvas.drawArc(rectF1, mStartAngle, mSweepAngle, false, mPaint);
        }

        mPaint.setColor(mProgressColor);

        float ratio = getRatio();
        if (ratio != 0) {
            //绘制当前进度弧形
            canvas.drawArc(rectF1, mStartAngle, mSweepAngle * ratio, false, mPaint);
        }

    }

    private void drawThumb(Canvas canvas) {
        if (isShowThumb) {
            mPaint.reset();
//            mPaint.setAntiAlias(true);
//            mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
//            mPaint.setStrokeWidth(mThumbStrokeWidth);
//            mPaint.setColor(mThumbColor);
            float thumbAngle = mStartAngle + mSweepAngle * getRatio();
            //已知圆心,半径,角度,求圆上的点坐标
            mThumbCenterX = (float) (mCircleCenterX + mRadius * Math.cos(Math.toRadians(thumbAngle)));
            mThumbCenterY = (float) (mCircleCenterY + mRadius * Math.sin(Math.toRadians(thumbAngle)));
//            if (isCanDrag) {
//                canvas.drawCircle(mThumbCenterX, mThumbCenterY, mThumbRadius + mThumbRadiusEnlarges, mPaint);
//            } else {
//                canvas.drawCircle(mThumbCenterX, mThumbCenterY, mThumbRadius, mPaint);
//            }

            if (null != mThumbBitmap) {
                mMatrix.setTranslate(mThumbCenterX - mThumbRadius, mThumbCenterY - mThumbRadius);
                mMatrix.preScale(1, 1, 0, 0);
                mPaint.setAlpha(255);
                canvas.drawBitmap(mThumbBitmap, mMatrix, mPaint);
            }
        }

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if (isEnabledDrag) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    checkCanDrag(event.getX(), event.getY());
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (isCanDrag) {
                        updateDragThumb(event.getX(), event.getY(), false);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    getParent().requestDisallowInterceptTouchEvent(false);
                    if (mOnChangeListener != null) {
                        mOnChangeListener.onStopTrackingTouch(isCanDrag);
                    }
                    isCanDrag = false;
                    invalidate();
                    break;

            }
        }

        if (isEnabledSingle) {
            mDetector.onTouchEvent(event);
        }

        return isEnabledSingle || isEnabledDrag || super.onTouchEvent(event);
    }

    /**
     * 判断坐标点是否在弧形上
     *
     * @param x
     * @param y
     * @return
     */
    private boolean isInArc(float x, float y) {
        float distance = getDistance(mCircleCenterX, mCircleCenterY, x, y);
        if (Math.abs(distance - mRadius) <= mStrokeWidth / 2f + mAllowableOffsets) {
            if (mSweepAngle < 360) {
                float angle = (getTouchDegrees(x, y) + mStartAngle) % 360;
                if (mSweepAngle < 0) {
                    if (mStartAngle + mSweepAngle + 360 <= 360) {
                        return angle <= mStartAngle || angle >= (mStartAngle + mSweepAngle + 360) % 360;

                    } else {
                        return angle <= mStartAngle && angle >= mStartAngle + mSweepAngle + 360;
                    }
                }
                if (mStartAngle + mSweepAngle <= 360) {
                    return angle >= mStartAngle && angle <= mStartAngle + mSweepAngle;
                } else {
                    return angle >= mStartAngle || angle <= (mStartAngle + mSweepAngle) % 360;
                }
            }

            return true;

        }
        return false;
    }

    /**
     * 获取两点间距离
     *
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @return
     */
    private float getDistance(float x1, float y1, float x2, float y2) {
        return (float) Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
    }

    /**
     * 更新多拽进度
     *
     * @param x
     * @param y
     * @param isSingle
     */
    private void updateDragThumb(float x, float y, boolean isSingle) {
        int progress = getProgressForAngle(getTouchDegrees(x, y));
        if (!isSingle) {
            int tempProgressPercent = (int) (progress * 100.0f / mMax);
            //当滑动至至边界值时,增加进度校准机制
            if (mProgressPercent < 10 && tempProgressPercent > 90) {
                progress = 0;
            } else if (mProgressPercent > 90 && tempProgressPercent < 10) {
                progress = mMax;
            }
            int progressPercent = (int) (progress * 100.0f / mMax);
            //拖动进度突变不允许超过30%
            if (Math.abs(progressPercent - mProgressPercent) > 30) {
                return;
            }
        }

        setProgress(progress, true);
    }

    /**
     * 通过弧度换算得到当前精度
     *
     * @param angle
     * @return
     */
    private int getProgressForAngle(float angle) {
        int touchProgress = 0;
        if (mSweepAngle < 0) {
            touchProgress = Math.round(1.0f * mMax / mSweepAngle * (angle - 360));
        } else {
            touchProgress = Math.round(1.0f * mMax / mSweepAngle * angle);
        }
        return touchProgress;
    }

    /**
     * 获取触摸坐标的夹角度数
     *
     * @param x
     * @param y
     * @return
     */
    private float getTouchDegrees(float x, float y) {
        float x1 = x - mCircleCenterX;
        float y1 = y - mCircleCenterY;
        //求触摸点弧形的夹角度数
        float angle = (float) (Math.atan2(y1, x1) * 180 / Math.PI);
        angle -= mStartAngle;
        while (angle < 0) {
            angle += 360;
        }
        return angle;
    }

    /**
     * 检测是否可拖拽
     *
     * @param x
     * @param y
     */
    private void checkCanDrag(float x, float y) {
        float distance = getDistance(mThumbCenterX, mThumbCenterY, x, y);
        isCanDrag = distance <= mThumbRadius + mAllowableOffsets;
        if (mOnChangeListener != null) {
            mOnChangeListener.onStartTrackingTouch(isCanDrag);
        }
        invalidate();
    }

    /**
     * 进度比例
     *
     * @return
     */
    private float getRatio() {
        return mProgress * 1.0f / mMax;
    }

    /**
     * 设置最大进度
     *
     * @param max
     */
    public void setMax(int max) {

        if (max > 0) {
            this.mMax = max;
            invalidate();
        }
    }

    /**
     * 设置当前进度
     *
     * @param progress
     */
    public void setProgress(int progress) {
        setProgress(progress, false);
    }

    private void setProgress(int progress, boolean fromUser) {
        if (progress < 0) {
            progress = 0;
        } else if (progress > mMax) {
            progress = mMax;
        }
        this.mProgress = progress;
        mProgressPercent = (int) (mProgress * 100.0f / mMax);
        invalidate();

        if (mOnChangeListener != null) {
            mOnChangeListener.onProgressChanged(mProgress, mMax, fromUser);
        }
    }

    /**
     * 设置正常颜色
     *
     * @param color
     */
    public void setNormalColor(int color) {
        this.mNormalColor = color;
        invalidate();
    }

    /**
     * 设置进度颜色(纯色)
     *
     * @param color
     */
    public void setProgressColor(int color) {
        this.mProgressColor = color;
        invalidate();
    }

    /**
     * 设置进度颜色
     *
     * @param resId
     */
    public void setProgressColorResource(int resId) {
        int color = getResources().getColor(resId);
        setProgressColor(color);
    }

    public int getStartAngle() {
        return mStartAngle;
    }

    public int getSweepAngle() {
        return mSweepAngle;
    }

    public float getCircleCenterX() {
        return mCircleCenterX;
    }

    public float getCircleCenterY() {
        return mCircleCenterY;
    }

    public float getRadius() {
        return mRadius;
    }

    public int getMax() {
        return mMax;
    }

    public int getProgress() {
        return mProgress;
    }

    public float getThumbRadius() {
        return mThumbRadius;
    }

    public float getThumbCenterX() {
        return mThumbCenterX;
    }

    public float getThumbCenterY() {
        return mThumbCenterY;
    }

    public float getAllowableOffsets() {
        return mAllowableOffsets;
    }

    public boolean isEnabledDrag() {
        return isEnabledDrag;
    }

    public boolean isEnabledSingle() {
        return isEnabledSingle;
    }

    public boolean isShowThumb() {
        return isShowThumb;
    }


    public float getThumbRadiusEnlarges() {
        return mThumbRadiusEnlarges;
    }

    /**
     * 触摸时按钮半径放大量
     *
     * @param thumbRadiusEnlarges
     */
    public void setThumbRadiusEnlarges(float thumbRadiusEnlarges) {
        this.mThumbRadiusEnlarges = thumbRadiusEnlarges;
    }

    /**
     * 是否显示拖动按钮
     *
     * @param showThumb
     */
    public void setShowThumb(boolean showThumb) {
        isShowThumb = showThumb;
        invalidate();
    }

    /**
     * 触摸时可偏移距离:偏移量越大,触摸精度越小
     *
     * @param allowableOffsets
     */
    public void setAllowableOffsets(float allowableOffsets) {
        this.mAllowableOffsets = allowableOffsets;
    }

    /**
     * 是否启用拖拽
     *
     * @param enabledDrag 默认为 true,为 false 时 相当于{@link android.widget.ProgressBar}
     */
    public void setEnabledDrag(boolean enabledDrag) {
        isEnabledDrag = enabledDrag;
    }


    /**
     * 设置是否启用点击改变进度
     *
     * @param enabledSingle
     */
    public void setEnabledSingle(boolean enabledSingle) {
        isEnabledSingle = enabledSingle;
    }

    /**
     * 进度百分比
     *
     * @return
     */
    public int getProgressPercent() {
        return mProgressPercent;
    }

    /**
     * 设置进度改变监听
     *
     * @param onChangeListener
     */
    public void setOnChangeListener(OnChangeListener onChangeListener) {
        this.mOnChangeListener = onChangeListener;
    }


    public interface OnChangeListener {
        /**
         * 跟踪触摸事件开始时回调此方法 {@link MotionEvent#ACTION_DOWN}
         *
         * @param isCanDrag
         */
        void onStartTrackingTouch(boolean isCanDrag);

        /**
         * 进度改变时回调此方法
         *
         * @param progress
         * @param max
         * @param fromUser
         */
        void onProgressChanged(int progress, int max, boolean fromUser);

        /**
         * 跟踪触摸事件停止时回调此方法 {@link MotionEvent#ACTION_UP}
         */
        void onStopTrackingTouch(boolean isCanDrag);

        /**
         * 通过点击事件改变进度后回调此方法 {@link GestureDetector#GestureDetector#onSingleTapUp()}
         */
        void onSingleTapUp();
    }

}

三种SeekBar效果如图所示

在这里插入图片描述

三、常见问题

  1. 自定义图标滑到最小或者最大时,图标异常
	android:thumbOffset="0px"
  1. 滑块间隔是否覆盖滑轨,系统默认是间隔
	android:splitTrack=false//默认为true
  1. 解决 seekBar 宽度不撑满布局
		android:paddingStart="0dp"
        android:paddingEnd="0dp"

四、参考链接

  • https://github.com/jenly1314/ArcSeekBar
  • https://blog.csdn.net/liosen/article/details/124304685
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值