先上效果图,如果这是你想要的效果可以往下看。不是的话也可以借鉴一下。
1、自定义view的基本流程
1)measure();
主要作用是测量view的宽高
2)layout();
主要作用是计算子view的位置,一般是自定义viewgroup时才会用上。(这里可以忽略)
3)draw();
主要作用是绘制view,切记由于该方法会多次执行,切勿在这个方法里面过多的创建对象,以免引起内存泄漏。
2、核心思想
1)从效果图可以看出stepview是有多个步骤的,那么我们可以使用stepbean来封装每一步的属性值。具体内容如下:
/**
* 步骤view的数据基类
*/
public class StepBean {
private String name;
private String time;
private int icon;//图片icon
private int lineColor;
private int nameColor;
private int timeColor;
private boolean isIng = false;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public int getIcon() {
return icon;
}
public void setIcon(int icon) {
this.icon = icon;
}
public int getLineColor() {
return lineColor;
}
public void setLineColor(int lineColor) {
this.lineColor = lineColor;
}
public boolean isIng() {
return isIng;
}
public void setIng(boolean ing) {
isIng = ing;
}
public int getTimeColor() {
return timeColor;
}
public void setTimeColor(int timeColor) {
this.timeColor = timeColor;
}
public int getNameColor() {
return nameColor;
}
public void setNameColor(int nameColor) {
this.nameColor = nameColor;
}
}
2)stepview会传入一个List<StepBean>来决定绘制多少步。下面我们来看看具体实现:代码里面的注解也很详细,无非是画圆画线和绘制图片。
/**
* 自定义步骤view
*/
public class StepView extends View {
/**
* 需要显示的步骤节点集合
*/
private List<StepBean> stepBeanList = new ArrayList<>();
private int mCurrViewWidth;//当前控件宽度
private int mCurrViewHeight;//当前控件高度
private Paint mPaint;//节点画笔
private TextPaint mTextPaint;//文字描述的画笔
private Paint mLinePaint;
private int roundSize = 8;//圆的直径
private int haloRoundSize = 0;//外圆的直径
private int haloThickness = 1;//外圆的厚度
private int haloAlpha = 50;//光晕透明度
private int haloWidth = 4;//光晕的宽度
private int textSize = 12;//文字大小
private int lineHeight = 1;//步骤线的高度
private int startX = 0;//X轴开始位置
private int startY = 10;//Y轴开始位置
private int itemWidth = 0;//每一项的宽度
private int roundType = 0;//0空心圆,2光晕圆
//所有位置以圆心为标准
private int roundX = 0;//大圆心X
private int roundY = 0;//大圆心Y
private int bigRoundSize = 20;//圆的直径
public StepView(Context context) {
super(context);
}
public StepView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
//dp转px
roundSize = DensityUtil.dip2px(context, roundSize);
haloWidth = DensityUtil.dip2px(context, haloWidth);
haloRoundSize = roundSize + haloWidth * 2;
bigRoundSize = DensityUtil.dip2px(context, bigRoundSize);
roundY = DensityUtil.dip2px(context, startY) + bigRoundSize / 2;
haloThickness = DensityUtil.dip2px(context, haloThickness);
lineHeight = DensityUtil.dip2px(context, lineHeight);
textSize = DensityUtil.dip2px(context, textSize);
mPaint = new Paint();
mPaint.setAntiAlias(true); //消除锯齿
mLinePaint = new Paint();
//线高
mLinePaint.setStrokeWidth(lineHeight);
//虚线
DashPathEffect effects = new DashPathEffect(new float[]{lineHeight * 3,
lineHeight * 3}, 0);
mLinePaint.setPathEffect(effects);
mTextPaint = new TextPaint();
mTextPaint.setAntiAlias(true); //消除锯齿
mTextPaint.setTextSize(textSize);
mTextPaint.setTypeface(Typeface.SANS_SERIF);
//文字字体加粗
mTextPaint.setFakeBoldText(false);
}
public void setStepBeanList(List<StepBean> stepBeanList) {
if (stepBeanList != null)
this.stepBeanList = stepBeanList;
//计算控件开始位置
if (stepBeanList.size() > 0)
itemWidth = mCurrViewWidth / stepBeanList.size();
roundX = itemWidth / 2 - bigRoundSize / 2;
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mCurrViewWidth = getMeasuredWidth();
mCurrViewHeight = getMeasuredHeight();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < stepBeanList.size(); i++) {
// 画圆
mPaint.setColor(stepBeanList.get(i).getLineColor());
mPaint.setStyle(Paint.Style.FILL); //绘制实心圆
RectF rf2 = new RectF(roundX - roundSize / 2 + itemWidth * i, roundY - roundSize / 2, roundX + roundSize / 2 + itemWidth * i, roundY + roundSize / 2);
canvas.drawOval(rf2, mPaint);
if (roundType == 0) {
// 画空心圆
mPaint.setStyle(Paint.Style.STROKE); //绘制空心圆
mPaint.setStrokeWidth(haloThickness);//设置空心圆的厚度
} else {
// 画圆光圈
mPaint.setAlpha(haloAlpha);
}
RectF haloRectF = new RectF(roundX - haloRoundSize / 2 + itemWidth * i, roundY - haloRoundSize / 2, roundX + haloRoundSize / 2 + itemWidth * i, roundY + haloRoundSize / 2);
canvas.drawOval(haloRectF, mPaint);
// 绘制线,最后一个点不用绘制线
if (i < stepBeanList.size() - 1) {
mLinePaint.setColor(stepBeanList.get(i + 1).getLineColor());
int x = roundX + haloRoundSize / 2;//开始位置
canvas.drawLine(x + itemWidth * i, roundY, x + itemWidth * (i + 1) - haloRoundSize, roundY, mLinePaint);
}
//绘制正在进行的图片
if (stepBeanList.get(i).isIng()) {
Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), stepBeanList.get(i).getIcon());
int w = mBitmap.getWidth();
int h = mBitmap.getHeight();
// 指定图片绘制区域(全图)
Rect src = new Rect(0, 0, w, h);
// 指定图片在屏幕上显示的区域(原图大小)
Rect dst = new Rect(roundX - w / 2 + itemWidth * i, roundY - h / 2, roundX + w / 2 + itemWidth * i, roundY + h / 2);
canvas.drawBitmap(mBitmap, src, dst, null);
}
// 绘制name文字
mTextPaint.setColor(stepBeanList.get(i).getNameColor());
int tx = roundX - getTextWidth(stepBeanList.get(i).getName(), mTextPaint) / 2 + itemWidth * i;
int ty = roundY + bigRoundSize + getTextHeight(stepBeanList.get(i).getName(), mTextPaint);
canvas.drawText(stepBeanList.get(i).getName(), tx, ty, mTextPaint);
// 绘制time文字
mTextPaint.setColor(stepBeanList.get(i).getTimeColor());
tx = roundX - getTextWidth(stepBeanList.get(i).getTime(), mTextPaint) / 2 + itemWidth * i;
ty = ty + getTextHeight(stepBeanList.get(i).getTime(), mTextPaint) * 2;
canvas.drawText(stepBeanList.get(i).getTime(), tx, ty, mTextPaint);
}
}
//获取文字宽度
private int getTextWidth(String text, Paint paint) {
Rect rect = new Rect(); // 文字所在区域的矩形
paint.getTextBounds(text, 0, text.length(), rect);
return rect.width();
}
//获取文字高度
private int getTextHeight(String text, Paint paint) {
Rect rect = new Rect();
paint.getTextBounds(text, 0, text.length(), rect);
return rect.height();
}
}
3)这里需要注意一下,由于我这边的需求的步骤不多,所以我这边是按:屏幕的宽度/步骤总数 = 每一步的宽度;如果你的步骤过多的话会出现每一步的宽度很小,可以考虑采用滑动显示的方式来实现。
3、如何使用:
1)在布局页面编写以下代码:
<com.library_base.ui.view.stepview.StepView
android:id="@+id/stepView"
android:layout_width="match_parent"
android:layout_height="80dp"/>
2)在Activity里面使用:
StepView stepView = findViewById(R.id.stepView);
private void setStepBean(String type) {
List<StepBean> stepBeans = new ArrayList<>();
//待核销
StepBean stepBean = new StepBean();
stepBean.setName("待核销");
stepBean.setTime(CommonUtils.getDateToString2(Long.parseLong(detailObject.getOrderTime())));
stepBean.setNameColor(Color.parseColor("#333333"));
stepBean.setTimeColor(Color.parseColor("#CCCCCC"));
stepBean.setLineColor(Color.parseColor("#51B3F1"));
stepBeans.add(stepBean);
//核销中
stepBean = new StepBean();
stepBean.setName("核销中");
stepBean.setTime(CommonUtils.getDateToString2(Long.parseLong(detailObject.getOrderTime())));
stepBean.setLineColor(Color.parseColor("#51B3F1"));
stepBean.setTimeColor(Color.parseColor("#CCCCCC"));
if (type.equals("0")) {
stepBean.setNameColor(Color.parseColor("#51B3F1"));
stepBean.setIng(true);
stepBean.setIcon(R.mipmap.check_state_img);
} else {
stepBean.setNameColor(Color.parseColor("#333333"));
}
stepBeans.add(stepBean);
//核销完成
stepBean = new StepBean();
stepBean.setTimeColor(Color.parseColor("#CCCCCC"));
if (type.equals("0")) {
stepBean.setName("核销完成");
stepBean.setTime("");
stepBean.setNameColor(Color.parseColor("#333333"));
stepBean.setLineColor(Color.parseColor("#D8D8D8"));
} else if (type.equals("1")) {
//核销不通过
stepBean.setName("核销不通过");
stepBean.setTime(CommonUtils.getDateToString2(Long.parseLong(detailObject.getVerificationTime())));
stepBean.setNameColor(Color.parseColor("#FF4503"));
stepBean.setLineColor(Color.parseColor("#FF4503"));
stepBean.setIng(true);
stepBean.setIcon(R.mipmap.check_state_un);
} else if (type.equals("2")) {
//核销通过
stepBean.setName("核销通过");
stepBean.setTime(CommonUtils.getDateToString2(Long.parseLong(detailObject.getVerificationTime())));
stepBean.setNameColor(Color.parseColor("#51B3F1"));
stepBean.setLineColor(Color.parseColor("#51B3F1"));
stepBean.setIng(true);
stepBean.setIcon(R.mipmap.check_state_ed);
}
stepBeans.add(stepBean);
stepView.setStepBeanList(stepBeans);
}
3)这里使用了单位转换的工具类
public class DensityUtil {
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
* 获取屏幕宽度
*
* @param context
* @return
*/
public static int getWidth(Context context) {
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
return wm.getDefaultDisplay().getWidth();
}
/**
* 获取屏幕高度
*
* @param context
* @return
*/
public static int getHeight(Context context) {
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
return wm.getDefaultDisplay().getHeight();
}
/**
* 获取图片的宽高
*
* @param context
* @param r
* @return
*/
public static int getImageWidth(Context context, int r) {
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), r);
return bitmap.getWidth();
}
}
至此一个简单的自定义stepview就实现了。