前言
本篇针对第一篇自定义分段进度条存在的问题进行优化,主要进行两点优化:
一、一半圆角矩形的绘制
上一篇中我们是通过Canvas绘制一个半圆组合一个矩形的方式绘制,现在我们替换成Path的方式绘制更简单。
//绘制渐变色
private void drawGradient(Canvas canvas) {
if (curStep == 0)
return;
Log.d("lz_test", "curStep=" + curStep);
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;
}
//Path绘制
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, barHeight, barHeight);
float rectWidth = curStep * stepWidth + (curStep - 1) * dividerWidth - corner;
path.reset();
path.addArc(ovalRecF, 90, 180);//绘制半圆
path.rLineTo(rectWidth, 0);
path.rLineTo(0, barHeight);
path.close();
canvas.drawPath(path, paint);
}
二、触摸过度绘制问题
我们在监听到ACTION_MOVE和ACTION_CANCE时先计算出触摸区域是第几个段,比对curStep的值如果不相同时我们才重新绘制。
计算触点位于第几段:
private int getTouchStep(float x) {
//先计算出触点范围属于第几段
int targetStep = curStep;
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) {
targetStep = i + 1;
break;
}
}
return targetStep;
}
比对当前所在的段数curStep:
private void dealViewTouch(float x, boolean complete) {
//判断步阶是否改变了
int targetStep = getTouchStep(x);
boolean stepIsChange = targetStep != curStep;
if (stepIsChange) {//步阶改变了才去重绘
curStep = targetStep;
invalidate();
if (onDragStepListener != null)
onDragStepListener.onDragStep(curStep, complete);
}
}
完整代码:
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 Path path;
//高度
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();
path = new Path();
}
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;
Log.d("lz_test", "curStep=" + curStep);
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;
}
//Path绘制
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, barHeight, barHeight);
float rectWidth = curStep * stepWidth + (curStep - 1) * dividerWidth - corner;
path.reset();
path.addArc(ovalRecF, 90, 180);
path.rLineTo(rectWidth, 0);
path.rLineTo(0, barHeight);
path.close();
canvas.drawPath(path, 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;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!touchEnable) {
return false;
}
//获取触摸坐标
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
dealViewTouch(event.getX(), false);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
dealViewTouch(event.getX(), true);
break;
}
getParent().requestDisallowInterceptTouchEvent(true);
return true;
}
private void dealViewTouch(float x, boolean complete) {
//判断步阶是否改变了
int targetStep = getTouchStep(x);
boolean stepIsChange = targetStep != curStep;
if (stepIsChange) {//步阶改变了才去重绘
curStep = targetStep;
invalidate();
if (onDragStepListener != null)
onDragStepListener.onDragStep(curStep, complete);
}
}
//获取触点所属第几段
private int getTouchStep(float x) {
//先计算出触点范围属于第几段
int targetStep = curStep;
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) {
targetStep = i + 1;
break;
}
}
return targetStep;
}
//获取当前步阶
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);
}
}