一、前言
本文在 上节 的基础上,讲解自定义拖动条的实现思路。
二、思路
先在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>
由上可知,考虑绘制拖动条必要的几个基本属性,如最大值最小值及其默认值,显示拖动效果或进度的左右高度、颜色,以及滑块的半径和颜色。默认自左往右,故进度条左边高右边稍低,而滑块需要比左边更大一些。
对应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; }