前言
最近项目遇到了这种分段式的进度条,支持单点、滑动更改进度;最初想着从网上找一些类似的 库直接拿过来用,为了个效果引入一个库感觉不太好,于是直接用Canvas画了一个。
整体思路是绘制三层叠加来实现:底层背景、渐变层、分割线。底层绘制一个灰色的圆角矩形; 上层覆盖可变大小的渐变层,根据当前的步阶动态计算出渐变层的大小;分割线通过设置的总段 数和步阶计算出每个分割线的位置并绘制,具体细节请看下方代码。
效果图:
代码如下:
public class DragStepBar extends View {
//背景颜色
private int bgColor = 0xFFDDDDDD;
//渐变颜色
private int gradientStart = 0xFF91C1FE;
private int gradientEnd = 0xFF495FE9;
//遮罩颜色
private int dividerColor = 0x80FFFFFF;
//画笔
private Paint paint;
//高度
private int barHeight;
//宽度
private int barWidth;
//左右两头圆角
private float corner;
//步阶
private int step = 6;
//当前步阶
private int curStep = 6;
//一段的宽度
private float stepWidth;
//分割线的宽度
private float dividerWidth;
//是否可以滑动
private boolean touchEnable = true;
private OnDragStepListener onDragStepListener;
public DragStepBar(Context context) {
this(context, null);
}
public DragStepBar(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public DragStepBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
DisplayMetrics displayMetrics = getDisplayMetrics();
barWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 322, displayMetrics);
barHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 28, displayMetrics);
dividerWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, displayMetrics);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DragStepBar);
//获取进度条背景色
bgColor = a.getColor(R.styleable.DragStepBar_bar_bg, bgColor);
//获取渐变色开始颜色
gradientStart = a.getColor(R.styleable.DragStepBar_bar_color_start, gradientStart);
//获取渐变色结束颜色
gradientEnd = a.getColor(R.styleable.DragStepBar_bar_color_end, gradientEnd);
//分割线颜色
dividerColor = a.getColor(R.styleable.DragStepBar_bar_divider_color, dividerColor);
//分割线宽度
dividerWidth = a.getDimension(R.styleable.DragStepBar_bar_divider_width, dividerWidth);
//总步数
step = a.getInt(R.styleable.DragStepBar_bar_step_count, step);
//当前步数
curStep = a.getInt(R.styleable.DragStepBar_bar_cur_step, curStep);
if (curStep>step)
curStep=step;
a.recycle();
paint = new Paint();
}
private DisplayMetrics getDisplayMetrics() {
return getResources().getDisplayMetrics();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制底部背景
drawBackGround(canvas);
//绘制步阶进度
drawGradient(canvas);
//绘制分割线
drawShade(canvas);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {//宽高都是wrap_content,使用默认值
setBarMeasure(barWidth, barHeight);
} else if (widthMode == MeasureSpec.AT_MOST) {
setBarMeasure(barWidth, heightSize);
} else if (heightMode == MeasureSpec.AT_MOST) {
setBarMeasure(widthSize, barHeight);
} else {
setBarMeasure(widthSize, heightSize);
}
}
/**
* 根据测量的宽高计算出单个进度的宽度和圆角的大小
*
*/
private void setBarMeasure(int width, int height) {
barWidth = width;
barHeight = height;
stepWidth = (barWidth - (step - 1) * dividerWidth) / step;
corner = (float) barHeight / 2;
setMeasuredDimension(width, height);
}
//绘制底色
private void drawBackGround(Canvas canvas) {
paint.reset();
paint.setAntiAlias(true);//抗锯齿
paint.setDither(true);//图形抖动
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(5);
paint.setColor(bgColor);
RectF rectF = new RectF(0, 0, barWidth, barHeight);
canvas.drawRoundRect(rectF, corner, corner, paint);
}
//绘制渐变色
private void drawGradient(Canvas canvas) {
if (curStep == 0)
return;
LinearGradient linearGradient = new LinearGradient(0, 0, barWidth, barHeight, gradientStart, gradientEnd, Shader.TileMode.MIRROR);
if (curStep == step) {
paint.reset();
paint.setAntiAlias(true);//抗锯齿
paint.setDither(true);//图形抖动
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(1);
paint.setShader(linearGradient);
RectF rectF = new RectF(0, 0, barWidth, barHeight);
canvas.drawRoundRect(rectF, corner, corner, paint);
return;
}
//绘制一半圆
paint.reset();
paint.setAntiAlias(true);//抗锯齿
paint.setDither(true);//图形抖动
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(1);
paint.setShader(linearGradient);
RectF ovalRecF = new RectF(0, 0, corner * 2, barHeight);
canvas.drawArc(ovalRecF, 90, 180, true, paint);
paint.reset();
paint.setAntiAlias(true);//抗锯齿
paint.setDither(true);//图形抖动
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(1);
paint.setShader(linearGradient);
RectF rectF = new RectF(corner, 0, curStep * stepWidth + (curStep - 1) * dividerWidth, barHeight);
canvas.drawRect(rectF, paint);
}
//绘制分割线
private void drawShade(Canvas canvas) {
paint.reset();
paint.setColor(dividerColor);
paint.setAntiAlias(true);//抗锯齿
paint.setDither(true);//图形抖动
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(1);
for (int i = 1; i < step; i++) {
float left = stepWidth * i + dividerWidth * (i - 1);
RectF rectF = new RectF(left, 0, left + dividerWidth, barHeight);
canvas.drawRect(rectF, paint);
}
}
//设置是否可点击
public void setTouchEnable(boolean touchEnable) {
this.touchEnable = touchEnable;
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!touchEnable) {
return false;
}
//获取触摸坐标
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
updateStep(event.getX());
if (onDragStepListener != null)
onDragStepListener.onDragStep(curStep, false);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
updateStep(event.getX());
if (onDragStepListener != null)
onDragStepListener.onDragStep(curStep, true);
break;
}
getParent().requestDisallowInterceptTouchEvent(true);
return true;
}
private void updateStep(float x) {
for (int i=0;i<step;i++){
float startX = i*stepWidth+i*dividerWidth;
float endX = (i==step-1)?barWidth:((i+1)*stepWidth+(i+1)*dividerWidth);
if (x> startX && x<=endX){
curStep = i+1;
invalidate();
break;
}
}
}
//获取当前步阶
public int getCurStep() {
return curStep;
}
public void setOnDragStepListener(OnDragStepListener onDragStepListener) {
this.onDragStepListener = onDragStepListener;
}
///设置默认步阶
public void setDefaultStep(int step) {
curStep = step;
invalidate();
}
public interface OnDragStepListener {
void onDragStep(int step, boolean moveCancel);
}
}
<declare-styleable name="DragStepBar">
<!--渐变色开始色-->
<attr name="bar_color_start" format="color"/>
<!--渐变色结束色-->
<attr name="bar_color_end" format="color"/>
<!--进度条背景色-->
<attr name="bar_bg" format="color"/>
<!--分割线颜色-->
<attr name="bar_divider_color" format="color"/>
<!--分割线宽度-->
<attr name="bar_divider_width" format="dimension"/>
<!--总步数-->
<attr name="bar_step_count" format="integer"/>
<!--当前所在步数-->
<attr name="bar_cur_step" format="integer"/>
</declare-styleable>
布局中使用:
<com.study.android.diyviewdemo.DragStepBar
android:layout_width="wrap_content"
android:layout_height="28dp"
android:layout_marginTop="20dp"
android:layout_marginStart="10dp"
app:bar_step_count="5"
app:bar_cur_step="3"
app:bar_divider_width="1dp"
android:paddingStart="16dp" />