搜索自定义seekbar全都是基于原生seekbar都是拖动滑块的,并且点击seekbar某个位置就会跳变到那里,我想要的是跟miui10的那个亮度条那样滑动来调节的,既然没有那就自己动手吧。
首先自己要清楚要画出来的是什么样的,这里先看下效果图:
实现思路:
主要的就是要draw两个原件矩形,一个背景,一个进度的方块,然后通过onTouchEvent来触发滑动改变进度。
首先先声明两支笔,一只画背景,一只话进度,然后给笔设置颜色:
private Paint bgPaint;
private Paint barPaint;
public void init(){
bgPaint = new Paint();
barPaint = new Paint();
bgPaint.setAntiAlias(true);
barPaint.setAntiAlias(true);
bgPaint.setColor(Color.GRAY);
barPaint.setColor(Color.WHITE);
}
绘制方法里面画一下康康效果,可以看到已经成功一半了:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
canvas.drawRoundRect(0,0,width,height,10,10,bgPaint);
canvas.drawRoundRect(0,0,width/2f,height,10,10,barPaint);
}
接下来就是设置成动态的,接受进度值改变来修改进度,也就是上面第二个圆角矩形的宽度:
private int maxProgress = 100;
private int currentProgress = 50;
private float radius = 10;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
canvas.drawRoundRect(0f,0f,width,height,radius,radius,bgPaint);
float barWidth = width*1.0f/ maxProgress * currentProgress;
canvas.drawRoundRect(0,0,barWidth,height,radius,radius,barPaint);
}
当前进度、最大进度、圆角半径后面可以动态修改。
然后,我们来处理touch事件。
思路就是按下的时候记录当前进度以及当前按下的点的x轴的值,然后在滑动的时候去计算滑动的距离,用滑动距离 ÷ 控件宽度 = 滑动进度 ÷ 最大进度来得到滑动进度,再用记录的当前进度 + 滑动进度赋值给当前进度,再重新绘制一遍就ok了:
private float downX;
private int currentCountTemp;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getX();
currentCountTemp = currentProgress;
return true;
case MotionEvent.ACTION_MOVE:
float downXTemp = event.getX();
float x = downXTemp - downX;
currentProgress = currentCountTemp + (int) (x/getWidth() * maxProgress);
if(currentProgress < 0){
currentProgress = 0;
}else if(currentProgress > maxProgress){
currentProgress = maxProgress;
}
invalidate();
return true;
default:
break;
}
return super.onTouchEvent(event);
}
最后再放出全部代码,xml声明属性就懒得搞了,可以自己去搞一下:
public class MySeekBar extends View {
private String TAG = "MySeekBar";
private int maxProgress = 100;
private int currentProgress = 50;
private float radius = 10;
private Paint bgPaint;
private Paint barPaint;
public void setBgColor(int bgColor) {
bgPaint.setColor(bgColor);
}
public void setBarColor(int barColor) {
barPaint.setColor(barColor);
}
public int getMaxProgress() {
return maxProgress;
}
public void setMaxProgress(int maxProgress) {
this.maxProgress = maxProgress;
}
public int getCurrentProgress() {
return currentProgress;
}
public void setCurrentProgress(int currentProgress) {
this.currentProgress = currentProgress;
}
public void setRadius(float radius) {
this.radius = radius;
}
public MySeekBar(Context context) {
super(context);
init();
}
public MySeekBar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public MySeekBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void init(){
bgPaint = new Paint();
barPaint = new Paint();
bgPaint.setAntiAlias(true);
barPaint.setAntiAlias(true);
bgPaint.setColor(Color.GRAY);
barPaint.setColor(Color.WHITE);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
canvas.drawRoundRect(0f,0f,width,height,radius,radius,bgPaint);
float barWidth = width*1.0f/ maxProgress * currentProgress;
canvas.drawRoundRect(0,0,barWidth,height,radius,radius,barPaint);
}
private float downX;
private int currentCountTemp;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getX();
currentCountTemp = currentProgress;
Log.i(TAG, "onTouchEvent: downX:"+downX);
return true;
case MotionEvent.ACTION_MOVE:
float downXTemp = event.getX();
float x = downXTemp - downX;
currentProgress = currentCountTemp + (int) (x/getWidth() * maxProgress);
if(currentProgress < 0){
currentProgress = 0;
}else if(currentProgress > maxProgress){
currentProgress = maxProgress;
}
invalidate();
if(onDragListener != null){
onDragListener.onProgressChange(currentProgress);
}
return true;
default:
break;
}
return super.onTouchEvent(event);
}
public interface OnDragListener{
void onProgressChange(int progress);
}
private OnDragListener onDragListener;
public void setOnDragListener(OnDragListener onDragListener) {
this.onDragListener = onDragListener;
}
}
2024-01-08更新:
如果需要进度条区域一边圆角矩形另一边直角矩形,可以使用Paint.setXfermode或者canvas.clipPath的方式。因为Paint.setXfermode方式设置的时候底部背景颜色值如果带了透明度则进度区域颜色也会被透明化了,所以这里使用canvas.clipPath的方式:
我们在初始化的时候把背景层的路径添加进来:
Path bgPath = new Path();
float[] rii = {rectRadius,rectRadius,rectRadius,rectRadius,
rectRadius,rectRadius,rectRadius,rectRadius};
bgPath.addRoundRect(bgRect, rii, Path.Direction.CW);
addRoundRect第一个参数就是背景的矩形RectF;第二个参数是圆角半径,可以看到是一个包含了8个元素的数组,分别表示左上、右上、左下、右下的x轴上的圆角半径,y轴上的圆角半径,所以是4*2=8个元素;第三个参数CW表示顺时针,CCW表示逆时针。
在onDraw的时候就可以裁剪这个path区域了:
//绘制背景
canvas.drawRoundRect(bgRect, rectRadius, rectRadius, bgPaint);
//裁剪背景区域,之后的绘制都会在这个区域范围以内
canvas.clipPath(bgPath);
//绘制进度块
canvas.drawRect(progressRect, progressPaint);