1,项目中要用到个人信息验证的在网上找了一下,好像有封装好了的StepView,首先感谢一下作者,这是作者的地址,效果图如下:

2,正准备撸起袖子就是一顿复制粘贴的时候,发现效果图成这个样子了(其实这里我们可以改变作者提供的方法改变文字的大小来解决这个问题,但是ui就是这个大的文字设计,我们猿拉不下脸来要别人改东西,所以就自己改了)

  西坝,这要是给项目经理看到了那中午加鸡腿这件事又泡汤了啊,so,让我们自己先看看作者源码,再自己重新搞一个呗

  • 准备阶段

  既然是自定义view,让我们先来热身一下,结合我们想要实现的,我们先来画一个空心矩形,线,虚线

  首先我们来画一个矩形

  1. //绘制矩形
  2. Paint paint = new Paint();
  3. //设置实心
  4. paint.setStyle(Paint.Style.STROKE);
  5. //设置消除锯齿
  6. paint.setAntiAlias(true);
  7. //设置画笔颜色
  8. paint.setColor(Color.RED);
  9. //设置paint的外边框宽度
  10. paint.setStrokeWidth(40);
  11. //绘制矩形
  12. canvas.drawRect(200, 200, 800, 420, paint);

  效果图如下:

  绘制虚线

  1. Paint paint = new Paint();
  2. paint.setAntiAlias(true);
  3. paint.setColor(Color.RED);
  4. paint.setStyle(Paint.Style.STROKE);
  5. paint.setStrokeWidth(2);
  6. /***
  7. * 构造函数为DashPathEffect(float[] intervals, float offset),其中intervals为虚线的ON和OFF数组,该数组的length必须大于等于2,phase为绘制时的偏移量。
  8. 本代码中先绘制8长度的实现,再绘制8长度的虚线,再绘制8长度的实现,再绘制8长度的虚线
  9. */
  10. DashPathEffect mEffect = new DashPathEffect(new float[]{8, 8, 8, 8,}, 1);
  11. Path path = new Path();
  12. //移动画笔到坐标200,600 这个点
  13. path.moveTo(200, 600);
  14. //绘制直线
  15. path.lineTo(800, 600);
  16. paint.setPathEffect(mEffect);
  17.  
  18. canvas.drawPath(path, paint);

  这里要注意下DashPathEffect,具体的作用就是帮助绘制虚线,绘制效果图如下:

 

  ok,简单的绘制图形说完了就来说说开始我们控件的绘制吧,先说一下我们的思路,我们打算把我们的控件的绘制分成两步,第一步:绘制上面的图形并记录位置,第二步:在对应每一步的图形下绘制对应的步骤名称,那我们先来将我们的图形的绘制。

  • 绘制图形

  先来看看我们要实现的效果图,如下图所示:

  分析难点,首先我们主要的问题是,怎么样将里面的图形控件放置在整个大的view的最正中间,这关系着我们在OnDraw()方法中绘制具体的view的起点坐标和重点坐标的计算,最主要的是找到第一个控件距离最左端的距离,那么下面我们在具体情况下来分析吧,假设到最左端的距离是mLeftPadding

  ①假设只存在一个“接单”这一个步骤,那么我们圆形控件最右端的,如图下红线标识的地方

  那么我们的是这样计算的

  1. mLeftPadding= getWidth() - 1*(mCircleRadius*2))/2

  ②假设存在个“接单”和“打包”这两个步骤,那么我们圆形控件最右端的该怎么计算呢

  其实也很简单,只需要将中间的两个圆的直径减去,再减去虚线距离再除以而就搞定了(这里假设两个圆的半径都相同,每天线的长度也相同),所以公式变成了下面这种

  1. mLeftPadding= getWidth() - 2*(mCircleRadius*2)-1*mLineLength)/2

  ③假设存在个“接单”和“打包”和“发出”这三个步骤,那么我们圆形控件最右端的该怎么计算呢

  其实也很简单,只需要将中间的三个圆的直径减去,再减去两虚线距离再除以而就搞定了(这里假设两个圆的半径都相同,每天线的长度也相同),所以公式变成了下面这种

  1. mLeftPadding= getWidth() - 3*(mCircleRadius*2)-2*mLineLength)/2

  ok,我们其实感觉是不是有点规律了,每一个都是减去n个小圆的直径再减去n-1个直线距离,所以我们可以这样写

  1. mLeftPadding = (getWidth() - n * mCircleRadius * 2 - (n - 1) * mLinePadding) / 2;

  然后我们可以通过mLeftPadding这个参数,将所有的图表的X坐标计算出来了,我们这里用一个集合来记录所有绘制的X坐标(方便我们绘制线和图标),具体代码如下:

  1. for(int i = 0; i < num; i++)
  2. {
  3. //先计算全部最左边的padding值(getWidth()-(圆形直径+两圆之间距离)*2)
  4. float paddingLeft = (getWidth() - num * mCircleRadius * 2 - (num - 1) * mLinePadding) / 2;
  5. mComplectedXPosition.add(paddingLeft + mCircleRadius + i * mCircleRadius * 2 + i * mLinePadding);
  6. }

  第一步:到了这里我们基本上把难点都解决了,现在我们创建一个StepViewIndicator,继承View,首先重写三个构造函数,再在创建构造函数的同时初始化类似于画笔、数组之类的属性,关键代码如下:

  1. //下面这是重写构造函数
  2. public StepViewIndicator(Context context) {
  3. this(context, null);
  4. }
  5.  
  6. public StepViewIndicator(Context context, @Nullable AttributeSet attrs) {
  7. this(context, attrs, 0);
  8. }
  9.  
  10. public StepViewIndicator(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  11. super(context, attrs, defStyleAttr);
  12. init();
  13. }
  14.  
  15. //这里是初始化属性
  16.  
  17. /**
  18. * 初始化常见属性
  19. */
  20. private void init() {
  21. mStepBeanList = new ArrayList<>();
  22. mPath = new Path();
  23. mEffect = new DashPathEffect(new float[]{8, 8, 8, 8}, 1);
  24.  
  25. //设置已完成的初始化基本设置
  26. mComplectedXPosition = new ArrayList();
  27. mComplectedPaint = new Paint();
  28. mComplectedPaint.setAntiAlias(true);
  29. mComplectedPaint.setColor(mCompletedLineColor);
  30. mComplectedPaint.setStyle(Paint.Style.FILL);
  31. mComplectedPaint.setStrokeWidth(2);
  32.  
  33. //设置未完成的初始化基本设置
  34. mUnComplectedPaint = new Paint();
  35. mUnComplectedPaint.setAntiAlias(true);
  36. mUnComplectedPaint.setColor(mUnCompletedLineColor);
  37. mUnComplectedPaint.setStyle(Paint.Style.STROKE);
  38. mUnComplectedPaint.setStrokeWidth(2);
  39. mUnComplectedPaint.setPathEffect(mEffect);
  40.  
  41. //圆的半径、线的长度、线的高度基本值设置
  42. mCompletedLineHeight = 0.05f * defaultStepIndicatorNum;
  43. mCircleRadius = 0.28f * defaultStepIndicatorNum;
  44. mLinePadding = 1.5f * defaultStepIndicatorNum;
  45.  
  46. //初始化三种状态下的图片
  47. mCompleteIcon = ContextCompat.getDrawable(getContext(), R.drawable.complted);
  48. mAttentionIcon = ContextCompat.getDrawable(getContext(), R.drawable.attention);
  49. mDefaultIcon = ContextCompat.getDrawable(getContext(), R.drawable.default_icon);
  50.  
  51. }

  再来看看我们这个view中要用到的所有属性,代码里面都注释好了所有属性的含义了,我就不再给大家一一解释了

  1. //定义默认的高度
  2. private int defaultStepIndicatorNum = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, getResources().getDisplayMetrics());
  3. //直线
  4. private Path mPath;
  5. //虚线绘制函数
  6. private DashPathEffect mEffect;
  7. //完成的下标集合
  8. private List<Float> mComplectedXPosition;
  9.  
  10. //已完成的部分绘制对象
  11. private Paint mComplectedPaint;
  12. //定义默认完成线的颜色 ;
  13. private int mCompletedLineColor = Color.WHITE;
  14. //已完成线的高度
  15. private float mCompletedLineHeight;
  16.  
  17. //未完成的部分绘制对象
  18. private Paint mUnComplectedPaint;
  19. //定义默认未完成线的颜色 ;
  20. private int mUnCompletedLineColor = Color.WHITE;
  21.  
  22. //定义圆的半径
  23. private float mCircleRadius;
  24. //定义线的长度
  25. private float mLinePadding;
  26.  
  27. //定义三种状态下的图片(已完成的图片,正在进行的图片,未完成的图片)
  28. Drawable mCompleteIcon;
  29. Drawable mAttentionIcon;
  30. Drawable mDefaultIcon;
  31.  
  32. //动态计算view位置
  33. private float mCenterY;
  34. private float mLeftY;
  35. private float mRightY;
  36.  
  37. //总共有多少步骤
  38. private int mStepNum = 0;
  39. private List<StepBean> mStepBeanList;
  40. //正在进行的位置
  41. private int mComplectingPosition;
  42.  
  43. //添加对view的监听
  44. private OnDrawIndicatorListener mOnDrawListener;

  第二步:重写OnMeasuer()方法,这里只是丈量了一下控件的宽高

  1. /**
  2. * 测量自身的高度
  3. *
  4. * @param widthMeasureSpec
  5. * @param heightMeasureSpec
  6. */
  7. @Override
  8. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  9. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  10. int width = defaultStepIndicatorNum * 2;
  11. if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) {
  12. width = MeasureSpec.getSize(widthMeasureSpec);
  13. }
  14. int height = defaultStepIndicatorNum;
  15. if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) {
  16. height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
  17. }
  18. setMeasuredDimension(width, height);
  19. }

  这里要注意一下MeasureSpec的三种测量模式,具体解释如下(这点自定义view经常用到,在这里还是给大家普及一下):

  1. mode共有三种情况,取值分别为MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST
  2.  
  3. MeasureSpec.EXACTLY:精确尺寸,当我们将控件的layout_widthlayout_height指定为具体数值时andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
  4.  
  5. MeasureSpec.AT_MOST:最大尺寸,当控件的layout_widthlayout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的modeAT_MOSTsize给出了父控件允许的最大尺寸。
  6.  
  7. MeasureSpec.UNSPECIFIED:未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式,例如在ScrollView里面嵌套ListView,里面的listview的高度不确定,这时候要重写listview,将我们的布局撑到最大,代码如下:
  8. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  9. int expandSpec = MeasureSpec.makeMeasureSpec(1000>> 2,
  10. MeasureSpec.AT_MOST);
  11. super.onMeasure(widthMeasureSpec, expandSpec);
  12. }
  13. 1000的二进制:1111101000
  14. 右移2位后:11111010,十进制为:250
  15. 这样就指定了listview的高度为250px以内的最大允许值(一般就是250)。

  第三步:重写OnSizeChange()方法

  1. /**
  2. * View大小改变
  3. *
  4. * @param w
  5. * @param h
  6. * @param oldw
  7. * @param oldh
  8. */
  9. @Override
  10. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  11. super.onSizeChanged(w, h, oldw, oldh);
  12. //下面这三句代码只是为了绘制矩形的线
  13. mCenterY = 0.5f * getHeight();
  14. //获取左上方Y的位置,获取该点的意义是为了方便画矩形左上的Y位置
  15. mLeftY = mCenterY - (mCompletedLineHeight / 2);
  16. //获取右下方Y的位置,获取该点的意义是为了方便画矩形右下的Y位置
  17. mRightY = mCenterY + mCompletedLineHeight / 2;
  18.  
  19. mComplectedXPosition.clear();
  20. //计算所有总空间离最左边的距离,并记录所有的圆心x轴的坐标
  21. for (int i = 0; i < mStepNum; i++) {
  22. //计算全部最左边
  23. float paddingLeft = (getWidth() - mStepNum * mCircleRadius * 2 - (mStepNum - 1) * mLinePadding) / 2;
  24. //将所有的圆心X轴坐标记录到集合中
  25. mComplectedXPosition.add(paddingLeft + mCircleRadius + i * mCircleRadius * 2 + i * mLinePadding);
  26. }
  27. }

  在这里就使用到了我们上面的公式了,而且再记录我们的矩形线段的起始和结束的Y坐标,还有OnSizeChange()这个方法是在View的大小发生改变的时候进行调用,在这里我们需要了解一下

  第四步:重写OnDraw()方法

  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. super.onDraw(canvas);
  4.  
  5. if (mOnDrawListener != null) {
  6. mOnDrawListener.ondrawIndicator();
  7. }
  8. mUnComplectedPaint.setColor(mUnCompletedLineColor);
  9. mComplectedPaint.setColor(mCompletedLineColor);
  10.  
  11. //首先绘制线
  12. for (int i = 0; i < mComplectedXPosition.size() - 1; i++) {
  13. //前一个ComplectedXPosition
  14. final float preComplectedXPosition = mComplectedXPosition.get(i);
  15. //后一个ComplectedXPosition
  16. final float afterComplectedXPosition = mComplectedXPosition.get(i + 1);
  17. if (i < mComplectingPosition) {
  18. //判断在完成之前的所有的点画完成的线,这里是矩形(这里会有10像素的偏移,没懂)
  19. canvas.drawRect(preComplectedXPosition + mCircleRadius - 10, mLeftY, afterComplectedXPosition - mCircleRadius + 10, mRightY, mComplectedPaint);
  20. } else {
  21. mPath.moveTo(preComplectedXPosition + mCircleRadius, mCenterY);
  22. mPath.lineTo(afterComplectedXPosition - mCircleRadius, mCenterY);
  23. canvas.drawPath(mPath, mUnComplectedPaint);
  24. }
  25. }
  26.  
  27. //再绘制图标
  28. for (int i = 0; i < mComplectedXPosition.size(); i++) {
  29. final float currentComplectedXPosition = mComplectedXPosition.get(i);
  30. //创建矩形
  31. Rect rect = new Rect((int) (currentComplectedXPosition - mCircleRadius), (int) (mCenterY - mCircleRadius), (int) (currentComplectedXPosition + mCircleRadius), (int) (mCenterY + mCircleRadius));
  32. if (i < mComplectingPosition) {
  33. mCompleteIcon.setBounds(rect);
  34. mCompleteIcon.draw(canvas);
  35. } else if (i == mComplectingPosition && mComplectedXPosition.size() != 1) {
  36. canvas.drawCircle(currentComplectedXPosition, mCenterY, mCircleRadius * 1.1f, mComplectedPaint);
  37. mAttentionIcon.setBounds(rect);
  38. mAttentionIcon.draw(canvas);
  39. } else {
  40. mDefaultIcon.setBounds(rect);
  41. mDefaultIcon.draw(canvas);
  42. }
  43. }
  44.  
  45. }

  这个方法使我们最核心的,但是我们上面已经把难点的地方都抽出来单个解决了,所以onDraw()方法里面还是很好理解的,大体思路是根据我们onSizeChange()方法中记录的坐标来绘制对应的view,先是绘制线,再是绘制Rect,这里要注意下面这段代码,在做的时候我们发现即使你的坐标是正确的两个步骤之前的连线也会向左偏移一些,所以这里我们试了一下 ,向右偏移10px就差不多了,这个大家可以自己去试试

  1. canvas.drawRect(preComplectedXPosition + mCircleRadius - 10, mLeftY, afterComplectedXPosition - mCircleRadius + 10, mRightY, mComplectedPaint);

  这就是这个类的核心了,接下来我们来使用两组测试数据来试试,添加以下代码和以下方法

  1. init()方法中添加以下代码
  2.  
  3. //添加测试数据
  4. List<StepBean> datas = new ArrayList<>();
  5. datas.add(new StepBean("接单", StepBean.STEP_COMPLETED));
  6. datas.add(new StepBean("打包", StepBean.STEP_CURRENT));
  7. datas.add(new StepBean("出发", StepBean.STEP_UNDO));
  8. setStepNum(datas);
  9.  
  10. //这是setStepNuw方法具体代码
  11.  
  12. /**
  13. * 设置步骤的基本流程
  14. */
  15. public void setStepNum(List<StepBean> stepsBeanList) {
  16. this.mStepBeanList = stepsBeanList;
  17. mStepNum = mStepBeanList.size();
  18.  
  19. if (mStepBeanList != null && mStepBeanList.size() > 0) {
  20. for (int i = 0; i < mStepNum; i++) {
  21. StepBean stepsBean = mStepBeanList.get(i);
  22. {
  23. if (stepsBean.getState() == StepBean.STEP_CURRENT) {
  24. mComplectingPosition = i;
  25. }
  26. }
  27. }
  28. }
  29. requestLayout();
  30. }

  StepBean.java(用实体类将具体的每一步的信息给抽象化)  

  1. package com.qianmo.mystepview.bean;
  2.  
  3. /**
  4. * Created by Administrator on 2017/3/16 0016.
  5. * E-Mail:543441727@qq.com
  6. */
  7.  
  8. public class StepBean {
  9. public static final int STEP_UNDO = -1;//未完成 undo step
  10. public static final int STEP_CURRENT = 0;//正在进行 current step
  11. public static final int STEP_COMPLETED = 1;//已完成 completed step
  12.  
  13. private String name;
  14. private int state;
  15.  
  16. public String getName() {
  17. return name;
  18. }
  19.  
  20. public void setName(String name) {
  21. this.name = name;
  22. }
  23.  
  24. public int getState() {
  25. return state;
  26. }
  27.  
  28. public void setState(int state) {
  29. this.state = state;
  30. }
  31.  
  32. public StepBean() {
  33. }
  34.  
  35. public StepBean(String name, int state) {
  36. this.name = name;
  37. this.state = state;
  38. }
  39. }

  看一下我们绘制的效果,这就把我们上面的图标给绘制完成了,我们的自定义view就基本上完成了一大半了!

  再看一下我们StepViewIndicator类的所有代码

  1. package com.qianmo.mystepview.view;
  2.  
  3. import android.content.Context;
  4. import android.graphics.Canvas;
  5. import android.graphics.Color;
  6. import android.graphics.DashPathEffect;
  7. import android.graphics.Paint;
  8. import android.graphics.Path;
  9. import android.graphics.Rect;
  10. import android.graphics.drawable.Drawable;
  11. import android.support.annotation.Nullable;
  12. import android.support.v4.content.ContextCompat;
  13. import android.util.AttributeSet;
  14. import android.util.Log;
  15. import android.util.TypedValue;
  16. import android.view.View;
  17.  
  18. import com.qianmo.mystepview.R;
  19. import com.qianmo.mystepview.bean.StepBean;
  20.  
  21. import java.util.ArrayList;
  22. import java.util.List;
  23.  
  24. /**
  25. * Created by wangjitao on 2017/3/16 0016.
  26. * E-Mail:543441727@qq.com
  27. * 横向指示器的创建
  28. */
  29.  
  30. public class StepViewIndicator extends View {
  31. //定义默认的高度
  32. private int defaultStepIndicatorNum = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, getResources().getDisplayMetrics());
  33.  
  34. //直线
  35. private Path mPath;
  36. //虚线绘制函数
  37. private DashPathEffect mEffect;
  38. //完成的下标集合
  39. private List<Float> mComplectedXPosition;
  40.  
  41. //已完成的部分绘制对象
  42. private Paint mComplectedPaint;
  43. //定义默认完成线的颜色 ;
  44. private int mCompletedLineColor = Color.WHITE;
  45. //已完成线的高度
  46. private float mCompletedLineHeight;
  47.  
  48. //未完成的部分绘制对象
  49. private Paint mUnComplectedPaint;
  50. //定义默认未完成线的颜色 ;
  51. private int mUnCompletedLineColor = Color.WHITE;
  52.  
  53. //定义圆的半径
  54. private float mCircleRadius;
  55. //定义线的长度
  56. private float mLinePadding;
  57.  
  58. //定义三种状态下的图片(已完成的图片,正在进行的图片,未完成的图片)
  59. Drawable mCompleteIcon;
  60. Drawable mAttentionIcon;
  61. Drawable mDefaultIcon;
  62.  
  63. //动态计算view位置
  64. private float mCenterY;
  65. private float mLeftY;
  66. private float mRightY;
  67.  
  68. //总共有多少步骤
  69. private int mStepNum = 0;
  70. private List<StepBean> mStepBeanList;
  71. //正在进行的位置
  72. private int mComplectingPosition;
  73.  
  74. //添加对view的监听
  75. private OnDrawIndicatorListener mOnDrawListener;
  76.  
  77. private int screenWidth;//this screen width
  78.  
  79. public StepViewIndicator(Context context) {
  80. this(context, null);
  81. }
  82.  
  83. public StepViewIndicator(Context context, @Nullable AttributeSet attrs) {
  84. this(context, attrs, 0);
  85. }
  86.  
  87. public StepViewIndicator(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  88. super(context, attrs, defStyleAttr);
  89. init();
  90. }
  91.  
  92. /**
  93. * 初始化常见属性
  94. */
  95. private void init() {
  96. mStepBeanList = new ArrayList<>();
  97. mPath = new Path();
  98. mEffect = new DashPathEffect(new float[]{8, 8, 8, 8}, 1);
  99.  
  100. //设置已完成的初始化基本设置
  101. mComplectedXPosition = new ArrayList();
  102. mComplectedPaint = new Paint();
  103. mComplectedPaint.setAntiAlias(true);
  104. mComplectedPaint.setColor(mCompletedLineColor);
  105. mComplectedPaint.setStyle(Paint.Style.FILL);
  106. mComplectedPaint.setStrokeWidth(2);
  107.  
  108. //设置未完成的初始化基本设置
  109. mUnComplectedPaint = new Paint();
  110. mUnComplectedPaint.setAntiAlias(true);
  111. mUnComplectedPaint.setColor(mUnCompletedLineColor);
  112. mUnComplectedPaint.setStyle(Paint.Style.STROKE);
  113. mUnComplectedPaint.setStrokeWidth(2);
  114. mUnComplectedPaint.setPathEffect(mEffect);
  115.  
  116. //圆的半径、线的长度、线的高度基本值设置
  117. mCompletedLineHeight = 0.05f * defaultStepIndicatorNum;
  118. mCircleRadius = 0.28f * defaultStepIndicatorNum;
  119. mLinePadding = 1.5f * defaultStepIndicatorNum;
  120.  
  121. //初始化三种状态下的图片
  122. mCompleteIcon = ContextCompat.getDrawable(getContext(), R.drawable.complted);
  123. mAttentionIcon = ContextCompat.getDrawable(getContext(), R.drawable.attention);
  124. mDefaultIcon = ContextCompat.getDrawable(getContext(), R.drawable.default_icon);
  125.  
  126. //添加测试数据
  127. //List<StepBean> datas = new ArrayList<>();
  128. //datas.add(new StepBean("接单", StepBean.STEP_COMPLETED));
  129. //datas.add(new StepBean("打包", StepBean.STEP_CURRENT));
  130. //datas.add(new StepBean("出发", StepBean.STEP_UNDO));
  131. //setStepNum(datas);
  132. }
  133.  
  134. /**
  135. * 测量自身的高度
  136. *
  137. * @param widthMeasureSpec
  138. * @param heightMeasureSpec
  139. */
  140. @Override
  141. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  142. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  143. int width = defaultStepIndicatorNum * 2;
  144. if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) {
  145. width = MeasureSpec.getSize(widthMeasureSpec);
  146. }
  147. int height = defaultStepIndicatorNum;
  148. if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) {
  149. height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
  150. }
  151. setMeasuredDimension(width, height);
  152. }
  153.  
  154. /**
  155. * View大小改变
  156. *
  157. * @param w
  158. * @param h
  159. * @param oldw
  160. * @param oldh
  161. */
  162. @Override
  163. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  164. super.onSizeChanged(w, h, oldw, oldh);
  165. //下面这三句代码只是为了绘制矩形的线
  166. mCenterY = 0.5f * getHeight();
  167. //获取左上方Y的位置,获取该点的意义是为了方便画矩形左上的Y位置
  168. mLeftY = mCenterY - (mCompletedLineHeight / 2);
  169. //获取右下方Y的位置,获取该点的意义是为了方便画矩形右下的Y位置
  170. mRightY = mCenterY + mCompletedLineHeight / 2;
  171.  
  172. mComplectedXPosition.clear();
  173. //计算所有总空间离最左边的距离,并记录所有的圆心x轴的坐标
  174. for (int i = 0; i < mStepNum; i++) {
  175. //计算全部最左边
  176. float paddingLeft = (getWidth() - mStepNum * mCircleRadius * 2 - (mStepNum - 1) * mLinePadding) / 2;
  177. //将所有的圆心X轴坐标记录到集合中
  178. mComplectedXPosition.add(paddingLeft + mCircleRadius + i * mCircleRadius * 2 + i * mLinePadding);
  179. }
  180. Log.i("wangjitao", "screenWidth:" + screenWidth + ",geiwidth:" + getWidth());
  181.  
  182. //当位置发生改变时的回调监听
  183. // if (mOnDrawListener != null) {
  184. // mOnDrawListener.ondrawIndicator();
  185. // Log.i("wangjitao", "onSizeChanged");
  186. // }
  187.  
  188. }
  189.  
  190. /**
  191. * 绘制view
  192. *
  193. * @param canvas
  194. */
  195. @Override
  196. protected void onDraw(Canvas canvas) {
  197. super.onDraw(canvas);
  198.  
  199. if (mOnDrawListener != null) {
  200. mOnDrawListener.ondrawIndicator();
  201. Log.i("wangjitao", "onDraw");
  202. }
  203. mUnComplectedPaint.setColor(mUnCompletedLineColor);
  204. mComplectedPaint.setColor(mCompletedLineColor);
  205.  
  206. //首先绘制线
  207. for (int i = 0; i < mComplectedXPosition.size() - 1; i++) {
  208. //前一个ComplectedXPosition
  209. final float preComplectedXPosition = mComplectedXPosition.get(i);
  210. //后一个ComplectedXPosition
  211. final float afterComplectedXPosition = mComplectedXPosition.get(i + 1);
  212. if (i < mComplectingPosition) {
  213. //判断在完成之前的所有的点画完成的线,这里是矩形(这里会有10像素的偏移,没懂)
  214. canvas.drawRect(preComplectedXPosition + mCircleRadius - 10, mLeftY, afterComplectedXPosition - mCircleRadius + 10, mRightY, mComplectedPaint);
  215. } else {
  216. mPath.moveTo(preComplectedXPosition + mCircleRadius, mCenterY);
  217. mPath.lineTo(afterComplectedXPosition - mCircleRadius, mCenterY);
  218. canvas.drawPath(mPath, mUnComplectedPaint);
  219. }
  220. }
  221.  
  222. //再绘制图标
  223. for (int i = 0; i < mComplectedXPosition.size(); i++) {
  224. final float currentComplectedXPosition = mComplectedXPosition.get(i);
  225. //创建矩形
  226. Rect rect = new Rect((int) (currentComplectedXPosition - mCircleRadius), (int) (mCenterY - mCircleRadius), (int) (currentComplectedXPosition + mCircleRadius), (int) (mCenterY + mCircleRadius));
  227. if (i < mComplectingPosition) {
  228. mCompleteIcon.setBounds(rect);
  229. mCompleteIcon.draw(canvas);
  230. } else if (i == mComplectingPosition && mComplectedXPosition.size() != 1) {
  231. canvas.drawCircle(currentComplectedXPosition, mCenterY, mCircleRadius * 1.1f, mComplectedPaint);
  232. mAttentionIcon.setBounds(rect);
  233. mAttentionIcon.draw(canvas);
  234. } else {
  235. mDefaultIcon.setBounds(rect);
  236. mDefaultIcon.draw(canvas);
  237. }
  238. }
  239.  
  240. }
  241.  
  242. /**
  243. * 设置步骤的基本流程
  244. */
  245. public void setStepNum(List<StepBean> stepsBeanList) {
  246. this.mStepBeanList = stepsBeanList;
  247. mStepNum = mStepBeanList.size();
  248.  
  249. if (mStepBeanList != null && mStepBeanList.size() > 0) {
  250. for (int i = 0; i < mStepNum; i++) {
  251. StepBean stepsBean = mStepBeanList.get(i);
  252. {
  253. if (stepsBean.getState() == StepBean.STEP_CURRENT) {
  254. mComplectingPosition = i;
  255. }
  256. }
  257. }
  258. }
  259. requestLayout();
  260. }
  261.  
  262. /**
  263. * 设置监听
  264. *
  265. * @param onDrawListener
  266. */
  267. public void setOnDrawListener(OnDrawIndicatorListener onDrawListener) {
  268. mOnDrawListener = onDrawListener;
  269. }
  270.  
  271. /**
  272. * 设置对view监听
  273. */
  274. public interface OnDrawIndicatorListener {
  275. void ondrawIndicator();
  276. }
  277.  
  278. /**
  279. * 得到所有圆点所在的位置
  280. *
  281. * @return
  282. */
  283. public List<Float> getComplectedXPosition() {
  284. return mComplectedXPosition;
  285. }
  286.  
  287. /**
  288. * 设置正在进行position
  289. *
  290. * @param complectingPosition
  291. */
  292. public void setComplectingPosition(int complectingPosition) {
  293. this.mComplectingPosition = complectingPosition;
  294. invalidate();
  295. }
  296.  
  297. /**
  298. * 设置正在进行position
  299. */
  300. public int getComplectingPosition() {
  301. return this.mComplectingPosition;
  302. }
  303.  
  304. /**
  305. * 设置流程步数
  306. *
  307. * @param stepNum 流程步数
  308. */
  309. public void setStepNum(int stepNum) {
  310. this.mStepNum = stepNum;
  311. invalidate();
  312. }
  313.  
  314. /**
  315. * 设置未完成线的颜色
  316. *
  317. * @param unCompletedLineColor
  318. */
  319. public void setUnCompletedLineColor(int unCompletedLineColor) {
  320. this.mUnCompletedLineColor = unCompletedLineColor;
  321. }
  322.  
  323. /**
  324. * 设置已完成线的颜色
  325. *
  326. * @param completedLineColor
  327. */
  328. public void setCompletedLineColor(int completedLineColor) {
  329. this.mCompletedLineColor = completedLineColor;
  330. }
  331.  
  332. /**
  333. * 设置默认图片
  334. *
  335. * @param defaultIcon
  336. */
  337. public void setDefaultIcon(Drawable defaultIcon) {
  338. this.mDefaultIcon = defaultIcon;
  339. }
  340.  
  341. /**
  342. * 设置已完成图片
  343. *
  344. * @param completeIcon
  345. */
  346. public void setCompleteIcon(Drawable completeIcon) {
  347. this.mCompleteIcon = completeIcon;
  348. }
  349.  
  350. /**
  351. * 设置正在进行中的图片
  352. *
  353. * @param attentionIcon
  354. */
  355. public void setAttentionIcon(Drawable attentionIcon) {
  356. this.mAttentionIcon = attentionIcon;
  357. }
  358. }
  • 创建StepView类,继承自LinearLayout,用于将我们上面的创建的StepViewIndicator和包含所有文字的LinearLayout合并在一起,其布局文件如下
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:orientation="vertical">
  6.  
  7. <com.qianmo.mystepview.view.StepViewIndicator
  8. android:id="@+id/steps_indicator"
  9. android:layout_width="match_parent"
  10. android:layout_height="wrap_content"
  11. android:layout_marginTop="4dp"/>
  12.  
  13. <RelativeLayout
  14. android:id="@+id/rl_text_container"
  15. android:layout_width="match_parent"
  16. android:layout_height="wrap_content"
  17. />
  18. </LinearLayout>

  具体的代码如下

  1. package com.qianmo.mystepview.view;
  2.  
  3. import android.content.Context;
  4. import android.graphics.Typeface;
  5. import android.graphics.drawable.Drawable;
  6. import android.support.v4.content.ContextCompat;
  7. import android.util.AttributeSet;
  8. import android.util.TypedValue;
  9. import android.view.LayoutInflater;
  10. import android.view.View;
  11. import android.view.ViewGroup;
  12. import android.widget.LinearLayout;
  13. import android.widget.RelativeLayout;
  14. import android.widget.TextView;
  15.  
  16. import com.qianmo.mystepview.R;
  17. import com.qianmo.mystepview.bean.StepBean;
  18.  
  19. import java.util.List;
  20.  
  21. /**
  22. * 日期:16/6/22 15:47
  23. * <p/>
  24. * 描述:StepView
  25. */
  26. public class StepView extends LinearLayout implements StepViewIndicator.OnDrawIndicatorListener
  27. {
  28. private RelativeLayout mTextContainer;
  29. private StepViewIndicator mStepsViewIndicator;
  30. private List<StepBean> mStepBeanList;
  31. private int mComplectingPosition;
  32. private int mUnComplectedTextColor = ContextCompat.getColor(getContext(), R.color.uncompleted_text_color);//定义默认未完成文字的颜色;
  33. private int mComplectedTextColor = ContextCompat.getColor(getContext(), android.R.color.white);//定义默认完成文字的颜色;
  34. private int mTextSize = 14;//default textSize
  35. private TextView mTextView;
  36.  
  37. public StepView(Context context)
  38. {
  39. this(context, null);
  40. }
  41.  
  42. public StepView(Context context, AttributeSet attrs)
  43. {
  44. this(context, attrs, 0);
  45. }
  46.  
  47. public StepView(Context context, AttributeSet attrs, int defStyleAttr)
  48. {
  49. super(context, attrs, defStyleAttr);
  50. init();
  51. }
  52.  
  53. private void init()
  54. {
  55. View rootView = LayoutInflater.from(getContext()).inflate(R.layout.widget_horizontal_stepsview, this);
  56. mStepsViewIndicator = (StepViewIndicator) rootView.findViewById(R.id.steps_indicator);
  57. mStepsViewIndicator.setOnDrawListener(this);
  58. mTextContainer = (RelativeLayout) rootView.findViewById(R.id.rl_text_container);
  59. }
  60.  
  61. /**
  62. * 设置显示的文字
  63. *
  64. * @param stepsBeanList
  65. * @return
  66. */
  67. public StepView setStepViewTexts(List<StepBean> stepsBeanList)
  68. {
  69. mStepBeanList = stepsBeanList;
  70. mStepsViewIndicator.setStepNum(mStepBeanList);
  71. return this;
  72. }
  73.  
  74. /**
  75. * 设置未完成文字的颜色
  76. *
  77. * @param unComplectedTextColor
  78. * @return
  79. */
  80. public StepView setStepViewUnComplectedTextColor(int unComplectedTextColor)
  81. {
  82. mUnComplectedTextColor = unComplectedTextColor;
  83. return this;
  84. }
  85.  
  86. /**
  87. * 设置完成文字的颜色
  88. *
  89. * @param complectedTextColor
  90. * @return
  91. */
  92. public StepView setStepViewComplectedTextColor(int complectedTextColor)
  93. {
  94. this.mComplectedTextColor = complectedTextColor;
  95. return this;
  96. }
  97.  
  98. /**
  99. * 设置StepsViewIndicator未完成线的颜色
  100. *
  101. * @param unCompletedLineColor
  102. * @return
  103. */
  104. public StepView setStepsViewIndicatorUnCompletedLineColor(int unCompletedLineColor)
  105. {
  106. mStepsViewIndicator.setUnCompletedLineColor(unCompletedLineColor);
  107. return this;
  108. }
  109.  
  110. /**
  111. * 设置StepsViewIndicator完成线的颜色
  112. *
  113. * @param completedLineColor
  114. * @return
  115. */
  116. public StepView setStepsViewIndicatorCompletedLineColor(int completedLineColor)
  117. {
  118. mStepsViewIndicator.setCompletedLineColor(completedLineColor);
  119. return this;
  120. }
  121.  
  122. /**
  123. * 设置StepsViewIndicator默认图片
  124. *
  125. * @param defaultIcon
  126. */
  127. public StepView setStepsViewIndicatorDefaultIcon(Drawable defaultIcon)
  128. {
  129. mStepsViewIndicator.setDefaultIcon(defaultIcon);
  130. return this;
  131. }
  132.  
  133. /**
  134. * 设置StepsViewIndicator已完成图片
  135. *
  136. * @param completeIcon
  137. */
  138. public StepView setStepsViewIndicatorCompleteIcon(Drawable completeIcon)
  139. {
  140. mStepsViewIndicator.setCompleteIcon(completeIcon);
  141. return this;
  142. }
  143.  
  144. /**
  145. * 设置StepsViewIndicator正在进行中的图片
  146. *
  147. * @param attentionIcon
  148. */
  149. public StepView setStepsViewIndicatorAttentionIcon(Drawable attentionIcon)
  150. {
  151. mStepsViewIndicator.setAttentionIcon(attentionIcon);
  152. return this;
  153. }
  154.  
  155. /**
  156. * set textSize
  157. *
  158. * @param textSize
  159. * @return
  160. */
  161. public StepView setTextSize(int textSize)
  162. {
  163. if(textSize > 0)
  164. {
  165. mTextSize = textSize;
  166. }
  167. return this;
  168. }
  169.  
  170. @Override
  171. public void ondrawIndicator()
  172. {
  173. if(mTextContainer != null)
  174. {
  175. mTextContainer.removeAllViews();
  176. List<Float> complectedXPosition = mStepsViewIndicator.getComplectedXPosition();
  177. if(mStepBeanList != null && complectedXPosition != null && complectedXPosition.size() > 0)
  178. {
  179. for(int i = 0; i < mStepBeanList.size(); i++)
  180. {
  181. mTextView = new TextView(getContext());
  182. mTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSize);
  183. mTextView.setText(mStepBeanList.get(i).getName());
  184. int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
  185. mTextView.measure(spec, spec);
  186. // getMeasuredWidth
  187. int measuredWidth = mTextView.getMeasuredWidth();
  188. mTextView.setX(complectedXPosition.get(i) - measuredWidth / 2);
  189. mTextView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
  190.  
  191. if(i <= mComplectingPosition)
  192. {
  193. mTextView.setTypeface(null, Typeface.BOLD);
  194. mTextView.setTextColor(mComplectedTextColor);
  195. } else
  196. {
  197. mTextView.setTextColor(mUnComplectedTextColor);
  198. }
  199.  
  200. mTextContainer.addView(mTextView);
  201. }
  202. }
  203. }
  204. }
  205.  
  206. }

  但是我们关注ondrawIndicator()这个方法中的逻辑,主要是通过OnDrawIndicatorListener这个类的回调来绘制对应的文字描述,再看看在Activity中只的使用

  1. StepView setpview = (StepView) findViewById(R.id.step_view0);
  2. List<StepBean> stepsBeanList = new ArrayList<>();
  3. StepBean stepBean0 = new StepBean("手机绑定",1);
  4. StepBean stepBean1 = new StepBean("提交信息",1);
  5. StepBean stepBean2 = new StepBean("充值",0);
  6. StepBean stepBean3 = new StepBean("用车",-1);
  7.  
  8. stepsBeanList.add(stepBean0);
  9. stepsBeanList.add(stepBean1);
  10. stepsBeanList.add(stepBean2);
  11. stepsBeanList.add(stepBean3);
  12.  
  13. setpview.setStepViewTexts(stepsBeanList)
  14. .setTextSize(16);//set textSize

  很简单有没有,看着一次我们自己写的效果有没有之前作者那种文字重叠的问题 如下图:

  很明显,没有,因为我在StepViewIndicator类中将的mLinePadding属性的值修改大了

  修改之后

  1. mLinePadding = 1.5f * defaultStepIndicatorNum;

  修改之钱

  1. mLinePadding = 0.85f * defaultStepIndicatorNum;

  这里其实可以提供一个set方法来设置动态的设置一下实线和虚线的距离的,好了,这样我们就是实现了我们的需求了,再次感谢StepView的作者,如果有需要源码的同学,可以去下一下,Github下载地址,See You Next Time !!!

Android -- 自定义StepView实现个人信息验证进度条的更多相关文章

  1. 我的Android进阶之旅------>Android自定义View实现带数字的进度条(NumberProgressBar)

    今天在Github上面看到一个来自于 daimajia所写的关于Android自定义View实现带数字的进度条(NumberProgressBar)的精彩案例,在这里分享给大家一起来学习学习!同时感谢 ...

  2. CodePush自定义更新弹框及下载进度条

    CodePush 热更新之自定义更新弹框及下载进度 先来几张弹框效果图 非强制更新场景 image 强制更新场景 image 更新包下载进度效果 image 核心代码 这里的热更新Modal框,是封装 ...

  3. Android——用对话框做登陆界面(自定义对话框AlertDialog,多线程,进度条ProgressDialog,ListView,GridView,SharedPreferences存,读数据,存取文本,assets文件)

    效果: 1.点击图标进入页面二 2.页面2图片暂停显示5秒进入页面三 3.点击页面三登陆按钮,打开登陆对话框,输入密码进入页面四 点击下载按钮,显示水平进度条 点击保存和获取用户名和密码 进入页面六  ...

  4. Android 高手进阶,自己定义圆形进度条

    背景介绍 在Android 开发中,我们常常遇到各种各样绚丽的控件,所以,依靠我们Android本身所带的控件是远远不够的,许多时候须要我们自定义控件,在开发的过程中.我们公司遇到了一种须要自己写的一 ...

  5. Android自定义控件系列之应用篇——圆形进度条

    一.概述 在上一篇博文中,我们给大家介绍了Android自定义控件系列的基础篇.链接:http://www.cnblogs.com/jerehedu/p/4360066.html 这一篇博文中,我们将 ...

  6. R随机森林交叉验证 + 进度条

    library(data.table) library(randomForest) data <- iris str(data) #交叉验证,使用rf预测sepal.length k = 5 d ...

  7. 让android系统中任意一个view变成进度条

    1.效果 2.进度条背景drawable文件 结束后可以恢复原背景. <?xml version="1.0" encoding="utf-8"?> ...

  8. Android中的常用控件之进度条(ProgressBar)

    ProgressBar的常用属性:style,进度条的样式,默认为圆形,用style="?android:attr/progressBarStyleHorizontal"可以将进度 ...

  9. android 仿摩拜单车共享单车进度条实现StepView

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6552712.html 先看效果图: Step1:定义StepBean 定义五个状态,分别为:为完成.正在进行 ...

随机推荐

  1. EntityFramework Core解决并发详解

    前言 对过年已经无感,不过还是有很多闲暇时间来学学东西,这一点是极好的,好了,本节我们来讲讲EntityFramewoek Core中的并发问题. 话题(EntityFramework Core并发) ...

  2. node源码详解(五) —— 在main函数之前 —— js和C++的边界,process.binding

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource5 本博客同步在https://cnodejs.o ...

  3. ABP入门系列(7)——分页实现

    ABP入门系列目录--学习Abp框架之实操演练 完成了任务清单的增删改查,咱们来讲一讲必不可少的的分页功能. 首先很庆幸ABP已经帮我们封装了分页实现,实在是贴心啊. 来来来,这一节咱们就来捋一捋如何 ...

  4. 在LINUX上创建GIT服务器

    转载 如果使用git的人数较少,可以使用下面的步骤快速部署一个git服务器环境. 1. 生成 SSH 公钥 每个需要使用git服务器的工程师,自己需要生成一个ssh公钥进入自己的~/.ssh目录,看有 ...

  5. KB奇遇记(6):搞笑的ERP项目团队

    早在我们来之前,KB公司这边就已经组建了ERP项目组了,当时IT就只有一个人,属网管出身.而关键用户分两种类型:专职关键用户和兼职关键用户.专职关键用户组织结构上已经调动到信息部,常驻在项目组里工作, ...

  6. 《JAVASCRIPT高级程序设计》DOM扩展

    虽然DOM为XML及HTML文档交互制定了一系列的API,但仍然有几个规范对标准的DOM进行了扩展.这些扩展中,有很多是浏览器专有的,但后来成了事实标准,于是其他浏览器也提供了相同的实现:浏览器开发商 ...

  7. spring MVC cors跨域实现源码解析

    # spring MVC cors跨域实现源码解析 > 名词解释:跨域资源共享(Cross-Origin Resource Sharing) 简单说就是只要协议.IP.http方法任意一个不同就 ...

  8. 腾讯优图及知脸(ZKface)人脸比对接口测试(python)

    一.腾讯优图 1.开发者地址:http://open.youtu.qq.com/welcome/developer 2.接入流程:按照开发者页面的接入流程接入之后,创建应用即可获得所需的AppID.S ...

  9. 毕向东udp学习笔记3多线程聊天

    项目功能: 实现了多线程下的发送接收,比较好 希望可以加入GUI,类似聊天软件一样,有一个消息输入框,捕获输入消息,作为发送线程 有一个显示消息框,接收消息并显示,作为接收线程 不知道的是,当在线程中 ...

  10. SQL极限函数limit()详解<分页必备>

    limit含义: limit英语中的含义是限制,限定的意思.小日本曾上映过一个电影就是叫limit是由漫画改编的电影,剧情很变态,但不可否认小日本由于地狭人稠的原因,在观念上的资源危机意识还是很强的哈 ...