一、控件介绍
SeekBar控件是Android原生组件之一,用于选择连续的数据或者块数据。一般情况下,SeekBar控件都是用来实现调节音量大小、颜色、亮度等操作。但是,在实际开发中,我们会发现自带的SeekBar样式不够美观、不够适用。因此,本文将介绍如何自定义SeekBar控件,实现控件的样式和功能更加多样化。
二、自定义SeekBar的基本步骤
自定义SeekBar的基本步骤如下:
1、继承SeekBar,重写构造函数、绘制方法、触摸事件等方法;
2、定义SeekBar需要用到的属性;
3、在布局文件中引入自定义的SeekBar。
三、自定义SeekBar的具体实现
1. 自定义SeekBar属性的定义
首先,我们需要定义需要用到的SeekBar属性。
<declare-styleable name="MySeekBar">
<attr name="thumb" format="reference" />
<attr name="progressColor" format="color" />
<attr name="progressBackground" format="color" />
<attr name="seekBarHeight" format="dimension" />
</declare-styleable>
其中,thumb
属性用于设置SeekBar的把手图像,progressColor
属性用于设置SeekBar填充区域的颜色,progressBackground
属性用于设置SeekBar未填充区域的颜色,seekBarHeight
属性用于设置SeekBar的高度。
2. 自定义SeekBar的绘制过程
接下来,我们需要重写SeekBar的onMeasure()
、onDraw()
和onTouchEvent()
方法。
2.1 onMeasure方法:测量SeekBar的大小
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = MeasureSpec.getSize(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(width, height);
}
通过重写onMeasure()
方法,我们可以测量SeekBar的大小,并将其设置为期望的大小。
2.2 onDraw方法:绘制SeekBar的基本属性
@Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
float thumbX = getThumbPosX();
// 绘制背景
RectF rectBackground = new RectF(0, height / 2 - seekBarHeight / 2, width, height / 2 + seekBarHeight / 2);
paint.setColor(progressBackground);
canvas.drawRoundRect(rectBackground, height / 2, height / 2, paint);
// 绘制填充区域
float progressEnd = thumbX + getPaddingLeft();
RectF rectProgress = new RectF(0, height / 2 - seekBarHeight / 2, progressEnd, height / 2 + seekBarHeight / 2);
paint.setColor(progressColor);
canvas.drawRoundRect(rectProgress, height / 2, height / 2, paint);
// 绘制把手图像
canvas.drawBitmap(thumbBitmap, thumbX, height / 2 - thumbBitmap.getHeight() / 2, paint);
}
onDraw()
方法是SeekBar的绘制核心方法,我们可以在该方法中实现SeekBar的基本绘制,包括背景、填充区域和把手图像等内容。
2.3 onTouchEvent方法:监听SeekBar的触摸事件
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchDownX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
float touchMoveX = event.getX();
float touchDelta = touchMoveX - touchDownX;
touchDownX = touchMoveX;
int thumbPosX = (int) (getThumbPosX() + touchDelta);
int maxRange = getWidth() - getPaddingLeft() - getPaddingRight();
int seekBarWidth = thumbBitmap.getWidth();
thumbPosX = Math.max(thumbPosX, 0);
thumbPosX = Math.min(thumbPosX, maxRange);
setProgress(thumbPosX * getMax() / (maxRange - seekBarWidth));
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
onTouchEvent()
方法是SeekBar的触摸事件处理方法,通过监听触摸事件的四种状态,我们可以实时更新SeekBar的视图状态。
3. 自定义SeekBar的样式
3.1 基本样式
<com.example.MySeekBar
android:id="@+id/mySeekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
app:thumb="@drawable/ic_thumb"
app:progressColor="@color/colorAccent"
app:progressBackground="@color/colorPrimary"
app:seekBarHeight="20dp" />
通过设置app:thumb
、app:progressColor
和app:progressBackground
等属性,我们可以实现SeekBar的基本样式。
3.2 高级样式
<com.example.MySeekBar
android:id="@+id/mySeekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
app:thumb="@drawable/ic_thumb"
app:progressColor="@color/colorAccent"
app:progressBackground="@color/colorPrimary"
app:seekBarHeight="20dp"
style="?android:attr/progressBarStyleHorizontal" />
除了基本样式外,我们还可以通过设置style="?android:attr/progressBarStyleHorizontal"
属性,使得SeekBar的样式与系统默认的样式保持一致,效果更加美观。
四、完整代码示例
public class MySeekBar extends SeekBar {
private Paint paint;
private Bitmap thumbBitmap;
private int progressColor;
private int progressBackground;
private int seekBarHeight;
private float touchDownX;
public MySeekBar(Context context) {
super(context);
setup();
}
public MySeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
setup(attrs);
}
public MySeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setup(attrs);
}
private void setup() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
thumbBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_thumb);
}
private void setup(AttributeSet attrs) {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
thumbBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_thumb);
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MySeekBar);
progressColor = typedArray.getColor(R.styleable.MySeekBar_progressColor, Color.RED);
progressBackground = typedArray.getColor(R.styleable.MySeekBar_progressBackground, Color.GRAY);
seekBarHeight = (int) typedArray.getDimension(R.styleable.MySeekBar_seekBarHeight, 10);
typedArray.recycle();
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = MeasureSpec.getSize(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(width, height);
}
@Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
float thumbX = getThumbPosX();
// 绘制背景
RectF rectBackground = new RectF(0, height / 2 - seekBarHeight / 2, width, height / 2 + seekBarHeight / 2);
paint.setColor(progressBackground);
canvas.drawRoundRect(rectBackground, height / 2, height / 2, paint);
// 绘制填充区域
float progressEnd = thumbX + getPaddingLeft();
RectF rectProgress = new RectF(0, height / 2 - seekBarHeight / 2, progressEnd, height / 2 + seekBarHeight / 2);
paint.setColor(progressColor);
canvas.drawRoundRect(rectProgress, height / 2, height / 2, paint);
// 绘制把手图像
canvas.drawBitmap(thumbBitmap, thumbX, height / 2 - thumbBitmap.getHeight() / 2, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchDownX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
float touchMoveX = event.getX();
float touchDelta = touchMoveX - touchDownX;
touchDownX = touchMoveX;
int thumbPosX = (int) (getThumbPosX() + touchDelta);
int maxRange = getWidth() - getPaddingLeft() - getPaddingRight();
int seekBarWidth = thumbBitmap.getWidth();
thumbPosX = Math.max(thumbPosX, 0);
thumbPosX = Math.min(thumbPosX, maxRange);
setProgress(thumbPosX * getMax() / (maxRange - seekBarWidth));
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
private float getThumbPosX() {
int width = getWidth() - getPaddingLeft() - getPaddingRight();
int seekBarWidth = thumbBitmap.getWidth();
float ratio = (float) getProgress() / (float) getMax();
return width * ratio;
}
}
<declare-styleable name="MySeekBar">
<attr name="thumb" format="reference" />
<attr name="progressColor" format="color" />
<attr name="progressBackground" format="color" />
<attr name="seekBarHeight" format="dimension" />
</declare-styleable>
<com.example.MySeekBar
android:id="@+id/mySeekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
app:thumb="@drawable/ic_thumb"
app:progressColor="@color/colorAccent"
app:progressBackground="@color/colorPrimary"
app:seekBarHeight="20dp" />