思路

先在res/values文件夹下,自定义控件属性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MySeekBar">

        <attr name="progress_max" format="float|reference"/> <!-- 进度条最大值 -->
        <attr name="progress_min" format="float|reference"/> <!-- 进度条最小值 -->
        <attr name="progress_default" format="float|reference"/> <!-- 进度条默认值 -->

        <attr name="track_left_height" format="dimension|reference"/> <!-- 进度条左边高度 -->
        <attr name="track_right_height" format="dimension|reference"/> <!-- 进度条右边高度 -->

        <attr name="track_left_color" format="color|reference"/> <!-- 进度条左边颜色 -->
        <attr name="track_right_color" format="color|reference"/> <!-- 进度条右边颜色 -->

        <attr name="thumb_color_default" format="color|reference"/> <!-- 拖动滑块默认颜色 -->
        <attr name="thumb_radius_default" format="dimension|reference"/> <!-- 拖动滑块半径 -->

        <attr name="thumb_color_on_dragging" format="color|reference"/> <!-- 拖动滑块拖动中颜色 -->
        <attr name="thumb_radius_on_dragging" format="dimension|reference"/> <!-- 拖动滑块拖动中半径 -->

    </declare-styleable>
</resources>

 

由上可知,考虑绘制拖动条必要的几个基本属性,如最大值最小值及其默认值,显示拖动效果或进度的左右高度、颜色,以及滑块的半径和颜色。默认自左往右,故进度条左边高右边稍低,而滑块需要比左边更大一些。

 

android 自定义ViewGroup Android 自定义seekbar_控件

 

对应SeekBar的构造函数:

 

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

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MySeekBar, defStyleAttr, 0);

        this.progress_max = typedArray.getFloat(R.styleable.MySeekBar_progress_max, 100);
        this.progress_min = typedArray.getFloat(R.styleable.MySeekBar_progress_min, 0);
        this.progress_default = typedArray.getFloat(R.styleable.MySeekBar_progress_default, progress_min);

        this.track_left_height = typedArray.getDimensionPixelSize(R.styleable.MySeekBar_track_left_height, DimensionUtil.dp2px(8));
        this.track_right_height = typedArray.getDimensionPixelSize(R.styleable.MySeekBar_track_right_height, track_left_height - DimensionUtil.dp2px(2));

        this.track_left_color = typedArray.getColor(R.styleable.MySeekBar_track_left_color, Color.BLUE);
        this.track_right_color = typedArray.getColor(R.styleable.MySeekBar_track_right_color, Color.LTGRAY);

        this.thumb_color_default = typedArray.getColor(R.styleable.MySeekBar_thumb_color_default, track_left_color);
        this.thumb_radius_default = typedArray.getDimensionPixelSize(R.styleable.MySeekBar_thumb_radius_default, track_left_height + DimensionUtil.dp2px(2));

        this.thumb_color_on_dragging = typedArray.getColor(R.styleable.MySeekBar_thumb_color_on_dragging, thumb_color_default);
        this.thumb_radius_on_dragging = typedArray.getDimensionPixelSize(R.styleable.MySeekBar_thumb_radius_on_dragging, thumb_radius_default + DimensionUtil.dp2px(2));

        typedArray.recycle();

        thumb_radius = thumb_radius_default;

        initPaint(); // 初始化画笔
    }

 

由于其他基础知识点已于前面 Android 自定义View 部分讲解,此处不再赘述。着重讲解 onMeasure(测量) 部分。先上代码

 

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 仅当 android_layout_width = wrap_content 或未指定时生效,若测出来的size大于你所指定的size (譬如这里是180dp),则使用所指定的size
        int width = resolveSize(DimensionUtil.dp2px(180), widthMeasureSpec);
        int height = thumb_radius_on_dragging * 2; // 控件高度按拖动时的滑块直径
        setMeasuredDimension(width, height); // 强制指定控件大小

        xLeft = getPaddingLeft() + thumb_radius_on_dragging; // 实际的绘图区域按距离父布局左边 padding 算起
        xRight = getMeasuredWidth() - getPaddingRight() - thumb_radius_on_dragging; // 到距离父布局右边的的 padding 结束
        yCenter = getPaddingTop() + thumb_radius_on_dragging; // 确定绘制进度条Y轴意义上的中点
    }

 

此处有个resolveSize() 函数,这个函数继承自view类,效果是当用户没有给控件指定大小的时候,可以给它一个默认的大小。我们可以点进去看下该方法的具体实现:

 

/**
     * Version of {@link #resolveSizeAndState(int, int, int)}
     * returning only the {@link #MEASURED_SIZE_MASK} bits of the result.
     */
    public static int resolveSize(int size, int measureSpec) {
        return resolveSizeAndState(size, measureSpec, 0) & MEASURED_SIZE_MASK;
    }