Android -- 自定义StepView实现个人信息验证进度条
1,项目中要用到个人信息验证的在网上找了一下,好像有封装好了的StepView,首先感谢一下作者,这是作者的地址,效果图如下:
2,正准备撸起袖子就是一顿复制粘贴的时候,发现效果图成这个样子了(其实这里我们可以改变作者提供的方法改变文字的大小来解决这个问题,但是ui就是这个大的文字设计,我们猿拉不下脸来要别人改东西,所以就自己改了)
西坝,这要是给项目经理看到了那中午加鸡腿这件事又泡汤了啊,so,让我们自己先看看作者源码,再自己重新搞一个呗
- 准备阶段
既然是自定义view,让我们先来热身一下,结合我们想要实现的,我们先来画一个空心矩形,线,虚线
首先我们来画一个矩形
//绘制矩形
Paint paint = new Paint();
//设置实心
paint.setStyle(Paint.Style.STROKE);
//设置消除锯齿
paint.setAntiAlias(true);
//设置画笔颜色
paint.setColor(Color.RED);
//设置paint的外边框宽度
paint.setStrokeWidth(40);
//绘制矩形
canvas.drawRect(200, 200, 800, 420, paint);
效果图如下:
绘制虚线
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
/***
* 构造函数为DashPathEffect(float[] intervals, float offset),其中intervals为虚线的ON和OFF数组,该数组的length必须大于等于2,phase为绘制时的偏移量。
本代码中先绘制8长度的实现,再绘制8长度的虚线,再绘制8长度的实现,再绘制8长度的虚线
*/
DashPathEffect mEffect = new DashPathEffect(new float[]{8, 8, 8, 8,}, 1);
Path path = new Path();
//移动画笔到坐标200,600 这个点
path.moveTo(200, 600);
//绘制直线
path.lineTo(800, 600);
paint.setPathEffect(mEffect); canvas.drawPath(path, paint);
这里要注意下DashPathEffect,具体的作用就是帮助绘制虚线,绘制效果图如下:
ok,简单的绘制图形说完了就来说说开始我们控件的绘制吧,先说一下我们的思路,我们打算把我们的控件的绘制分成两步,第一步:绘制上面的图形并记录位置,第二步:在对应每一步的图形下绘制对应的步骤名称,那我们先来将我们的图形的绘制。
- 绘制图形
先来看看我们要实现的效果图,如下图所示:
分析难点,首先我们主要的问题是,怎么样将里面的图形控件放置在整个大的view的最正中间,这关系着我们在OnDraw()方法中绘制具体的view的起点坐标和重点坐标的计算,最主要的是找到第一个控件距离最左端的距离,那么下面我们在具体情况下来分析吧,假设到最左端的距离是mLeftPadding
①假设只存在一个“接单”这一个步骤,那么我们圆形控件最右端的,如图下红线标识的地方
那么我们的是这样计算的
mLeftPadding= (getWidth() - 1*(mCircleRadius*2))/2;
②假设存在个“接单”和“打包”这两个步骤,那么我们圆形控件最右端的该怎么计算呢
其实也很简单,只需要将中间的两个圆的直径减去,再减去虚线距离再除以而就搞定了(这里假设两个圆的半径都相同,每天线的长度也相同),所以公式变成了下面这种
mLeftPadding= (getWidth() - 2*(mCircleRadius*2)-1*mLineLength)/2;
③假设存在个“接单”和“打包”和“发出”这三个步骤,那么我们圆形控件最右端的该怎么计算呢
其实也很简单,只需要将中间的三个圆的直径减去,再减去两虚线距离再除以而就搞定了(这里假设两个圆的半径都相同,每天线的长度也相同),所以公式变成了下面这种
mLeftPadding= (getWidth() - 3*(mCircleRadius*2)-2*mLineLength)/2;
ok,我们其实感觉是不是有点规律了,每一个都是减去n个小圆的直径再减去n-1个直线距离,所以我们可以这样写
mLeftPadding = (getWidth() - n * mCircleRadius * 2 - (n - 1) * mLinePadding) / 2;
然后我们可以通过mLeftPadding这个参数,将所有的图表的X坐标计算出来了,我们这里用一个集合来记录所有绘制的X坐标(方便我们绘制线和图标),具体代码如下:
for(int i = 0; i < num; i++)
{
//先计算全部最左边的padding值(getWidth()-(圆形直径+两圆之间距离)*2)
float paddingLeft = (getWidth() - num * mCircleRadius * 2 - (num - 1) * mLinePadding) / 2;
mComplectedXPosition.add(paddingLeft + mCircleRadius + i * mCircleRadius * 2 + i * mLinePadding);
}
第一步:到了这里我们基本上把难点都解决了,现在我们创建一个StepViewIndicator,继承View,首先重写三个构造函数,再在创建构造函数的同时初始化类似于画笔、数组之类的属性,关键代码如下:
//下面这是重写构造函数
public StepViewIndicator(Context context) {
this(context, null);
} public StepViewIndicator(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
} public StepViewIndicator(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
} //这里是初始化属性 /**
* 初始化常见属性
*/
private void init() {
mStepBeanList = new ArrayList<>();
mPath = new Path();
mEffect = new DashPathEffect(new float[]{8, 8, 8, 8}, 1); //设置已完成的初始化基本设置
mComplectedXPosition = new ArrayList();
mComplectedPaint = new Paint();
mComplectedPaint.setAntiAlias(true);
mComplectedPaint.setColor(mCompletedLineColor);
mComplectedPaint.setStyle(Paint.Style.FILL);
mComplectedPaint.setStrokeWidth(2); //设置未完成的初始化基本设置
mUnComplectedPaint = new Paint();
mUnComplectedPaint.setAntiAlias(true);
mUnComplectedPaint.setColor(mUnCompletedLineColor);
mUnComplectedPaint.setStyle(Paint.Style.STROKE);
mUnComplectedPaint.setStrokeWidth(2);
mUnComplectedPaint.setPathEffect(mEffect); //圆的半径、线的长度、线的高度基本值设置
mCompletedLineHeight = 0.05f * defaultStepIndicatorNum;
mCircleRadius = 0.28f * defaultStepIndicatorNum;
mLinePadding = 1.5f * defaultStepIndicatorNum; //初始化三种状态下的图片
mCompleteIcon = ContextCompat.getDrawable(getContext(), R.drawable.complted);
mAttentionIcon = ContextCompat.getDrawable(getContext(), R.drawable.attention);
mDefaultIcon = ContextCompat.getDrawable(getContext(), R.drawable.default_icon); }
再来看看我们这个view中要用到的所有属性,代码里面都注释好了所有属性的含义了,我就不再给大家一一解释了
//定义默认的高度
private int defaultStepIndicatorNum = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, getResources().getDisplayMetrics());
//直线
private Path mPath;
//虚线绘制函数
private DashPathEffect mEffect;
//完成的下标集合
private List<Float> mComplectedXPosition; //已完成的部分绘制对象
private Paint mComplectedPaint;
//定义默认完成线的颜色 ;
private int mCompletedLineColor = Color.WHITE;
//已完成线的高度
private float mCompletedLineHeight; //未完成的部分绘制对象
private Paint mUnComplectedPaint;
//定义默认未完成线的颜色 ;
private int mUnCompletedLineColor = Color.WHITE; //定义圆的半径
private float mCircleRadius;
//定义线的长度
private float mLinePadding; //定义三种状态下的图片(已完成的图片,正在进行的图片,未完成的图片)
Drawable mCompleteIcon;
Drawable mAttentionIcon;
Drawable mDefaultIcon; //动态计算view位置
private float mCenterY;
private float mLeftY;
private float mRightY; //总共有多少步骤
private int mStepNum = 0;
private List<StepBean> mStepBeanList;
//正在进行的位置
private int mComplectingPosition; //添加对view的监听
private OnDrawIndicatorListener mOnDrawListener;
第二步:重写OnMeasuer()方法,这里只是丈量了一下控件的宽高
/**
* 测量自身的高度
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = defaultStepIndicatorNum * 2;
if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) {
width = MeasureSpec.getSize(widthMeasureSpec);
}
int height = defaultStepIndicatorNum;
if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) {
height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
}
setMeasuredDimension(width, height);
}
这里要注意一下MeasureSpec的三种测量模式,具体解释如下(这点自定义view经常用到,在这里还是给大家普及一下):
mode共有三种情况,取值分别为MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST。 MeasureSpec.EXACTLY:精确尺寸,当我们将控件的layout_width或layout_height指定为具体数值时andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。 MeasureSpec.AT_MOST:最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。 MeasureSpec.UNSPECIFIED:未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式,例如在ScrollView里面嵌套ListView,里面的listview的高度不确定,这时候要重写listview,将我们的布局撑到最大,代码如下:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(1000>> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
1000的二进制:1111101000
右移2位后:11111010,十进制为:250
这样就指定了listview的高度为250px以内的最大允许值(一般就是250)。
第三步:重写OnSizeChange()方法
/**
* View大小改变
*
* @param w
* @param h
* @param oldw
* @param oldh
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//下面这三句代码只是为了绘制矩形的线
mCenterY = 0.5f * getHeight();
//获取左上方Y的位置,获取该点的意义是为了方便画矩形左上的Y位置
mLeftY = mCenterY - (mCompletedLineHeight / 2);
//获取右下方Y的位置,获取该点的意义是为了方便画矩形右下的Y位置
mRightY = mCenterY + mCompletedLineHeight / 2; mComplectedXPosition.clear();
//计算所有总空间离最左边的距离,并记录所有的圆心x轴的坐标
for (int i = 0; i < mStepNum; i++) {
//计算全部最左边
float paddingLeft = (getWidth() - mStepNum * mCircleRadius * 2 - (mStepNum - 1) * mLinePadding) / 2;
//将所有的圆心X轴坐标记录到集合中
mComplectedXPosition.add(paddingLeft + mCircleRadius + i * mCircleRadius * 2 + i * mLinePadding);
}
}
在这里就使用到了我们上面的公式了,而且再记录我们的矩形线段的起始和结束的Y坐标,还有OnSizeChange()这个方法是在View的大小发生改变的时候进行调用,在这里我们需要了解一下
第四步:重写OnDraw()方法
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas); if (mOnDrawListener != null) {
mOnDrawListener.ondrawIndicator();
}
mUnComplectedPaint.setColor(mUnCompletedLineColor);
mComplectedPaint.setColor(mCompletedLineColor); //首先绘制线
for (int i = 0; i < mComplectedXPosition.size() - 1; i++) {
//前一个ComplectedXPosition
final float preComplectedXPosition = mComplectedXPosition.get(i);
//后一个ComplectedXPosition
final float afterComplectedXPosition = mComplectedXPosition.get(i + 1);
if (i < mComplectingPosition) {
//判断在完成之前的所有的点画完成的线,这里是矩形(这里会有10像素的偏移,没懂)
canvas.drawRect(preComplectedXPosition + mCircleRadius - 10, mLeftY, afterComplectedXPosition - mCircleRadius + 10, mRightY, mComplectedPaint);
} else {
mPath.moveTo(preComplectedXPosition + mCircleRadius, mCenterY);
mPath.lineTo(afterComplectedXPosition - mCircleRadius, mCenterY);
canvas.drawPath(mPath, mUnComplectedPaint);
}
} //再绘制图标
for (int i = 0; i < mComplectedXPosition.size(); i++) {
final float currentComplectedXPosition = mComplectedXPosition.get(i);
//创建矩形
Rect rect = new Rect((int) (currentComplectedXPosition - mCircleRadius), (int) (mCenterY - mCircleRadius), (int) (currentComplectedXPosition + mCircleRadius), (int) (mCenterY + mCircleRadius));
if (i < mComplectingPosition) {
mCompleteIcon.setBounds(rect);
mCompleteIcon.draw(canvas);
} else if (i == mComplectingPosition && mComplectedXPosition.size() != 1) {
canvas.drawCircle(currentComplectedXPosition, mCenterY, mCircleRadius * 1.1f, mComplectedPaint);
mAttentionIcon.setBounds(rect);
mAttentionIcon.draw(canvas);
} else {
mDefaultIcon.setBounds(rect);
mDefaultIcon.draw(canvas);
}
} }
这个方法使我们最核心的,但是我们上面已经把难点的地方都抽出来单个解决了,所以onDraw()方法里面还是很好理解的,大体思路是根据我们onSizeChange()方法中记录的坐标来绘制对应的view,先是绘制线,再是绘制Rect,这里要注意下面这段代码,在做的时候我们发现即使你的坐标是正确的两个步骤之前的连线也会向左偏移一些,所以这里我们试了一下 ,向右偏移10px就差不多了,这个大家可以自己去试试
canvas.drawRect(preComplectedXPosition + mCircleRadius - 10, mLeftY, afterComplectedXPosition - mCircleRadius + 10, mRightY, mComplectedPaint);
这就是这个类的核心了,接下来我们来使用两组测试数据来试试,添加以下代码和以下方法
在init()方法中添加以下代码 //添加测试数据
List<StepBean> datas = new ArrayList<>();
datas.add(new StepBean("接单", StepBean.STEP_COMPLETED));
datas.add(new StepBean("打包", StepBean.STEP_CURRENT));
datas.add(new StepBean("出发", StepBean.STEP_UNDO));
setStepNum(datas); //这是setStepNuw方法具体代码 /**
* 设置步骤的基本流程
*/
public void setStepNum(List<StepBean> stepsBeanList) {
this.mStepBeanList = stepsBeanList;
mStepNum = mStepBeanList.size(); if (mStepBeanList != null && mStepBeanList.size() > 0) {
for (int i = 0; i < mStepNum; i++) {
StepBean stepsBean = mStepBeanList.get(i);
{
if (stepsBean.getState() == StepBean.STEP_CURRENT) {
mComplectingPosition = i;
}
}
}
}
requestLayout();
}
StepBean.java(用实体类将具体的每一步的信息给抽象化)
package com.qianmo.mystepview.bean; /**
* Created by Administrator on 2017/3/16 0016.
* E-Mail:543441727@qq.com
*/ public class StepBean {
public static final int STEP_UNDO = -1;//未完成 undo step
public static final int STEP_CURRENT = 0;//正在进行 current step
public static final int STEP_COMPLETED = 1;//已完成 completed step private String name;
private int state; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getState() {
return state;
} public void setState(int state) {
this.state = state;
} public StepBean() {
} public StepBean(String name, int state) {
this.name = name;
this.state = state;
}
}
看一下我们绘制的效果,这就把我们上面的图标给绘制完成了,我们的自定义view就基本上完成了一大半了!
再看一下我们StepViewIndicator类的所有代码
package com.qianmo.mystepview.view; import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View; import com.qianmo.mystepview.R;
import com.qianmo.mystepview.bean.StepBean; import java.util.ArrayList;
import java.util.List; /**
* Created by wangjitao on 2017/3/16 0016.
* E-Mail:543441727@qq.com
* 横向指示器的创建
*/ public class StepViewIndicator extends View {
//定义默认的高度
private int defaultStepIndicatorNum = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, getResources().getDisplayMetrics()); //直线
private Path mPath;
//虚线绘制函数
private DashPathEffect mEffect;
//完成的下标集合
private List<Float> mComplectedXPosition; //已完成的部分绘制对象
private Paint mComplectedPaint;
//定义默认完成线的颜色 ;
private int mCompletedLineColor = Color.WHITE;
//已完成线的高度
private float mCompletedLineHeight; //未完成的部分绘制对象
private Paint mUnComplectedPaint;
//定义默认未完成线的颜色 ;
private int mUnCompletedLineColor = Color.WHITE; //定义圆的半径
private float mCircleRadius;
//定义线的长度
private float mLinePadding; //定义三种状态下的图片(已完成的图片,正在进行的图片,未完成的图片)
Drawable mCompleteIcon;
Drawable mAttentionIcon;
Drawable mDefaultIcon; //动态计算view位置
private float mCenterY;
private float mLeftY;
private float mRightY; //总共有多少步骤
private int mStepNum = 0;
private List<StepBean> mStepBeanList;
//正在进行的位置
private int mComplectingPosition; //添加对view的监听
private OnDrawIndicatorListener mOnDrawListener; private int screenWidth;//this screen width public StepViewIndicator(Context context) {
this(context, null);
} public StepViewIndicator(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
} public StepViewIndicator(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
} /**
* 初始化常见属性
*/
private void init() {
mStepBeanList = new ArrayList<>();
mPath = new Path();
mEffect = new DashPathEffect(new float[]{8, 8, 8, 8}, 1); //设置已完成的初始化基本设置
mComplectedXPosition = new ArrayList();
mComplectedPaint = new Paint();
mComplectedPaint.setAntiAlias(true);
mComplectedPaint.setColor(mCompletedLineColor);
mComplectedPaint.setStyle(Paint.Style.FILL);
mComplectedPaint.setStrokeWidth(2); //设置未完成的初始化基本设置
mUnComplectedPaint = new Paint();
mUnComplectedPaint.setAntiAlias(true);
mUnComplectedPaint.setColor(mUnCompletedLineColor);
mUnComplectedPaint.setStyle(Paint.Style.STROKE);
mUnComplectedPaint.setStrokeWidth(2);
mUnComplectedPaint.setPathEffect(mEffect); //圆的半径、线的长度、线的高度基本值设置
mCompletedLineHeight = 0.05f * defaultStepIndicatorNum;
mCircleRadius = 0.28f * defaultStepIndicatorNum;
mLinePadding = 1.5f * defaultStepIndicatorNum; //初始化三种状态下的图片
mCompleteIcon = ContextCompat.getDrawable(getContext(), R.drawable.complted);
mAttentionIcon = ContextCompat.getDrawable(getContext(), R.drawable.attention);
mDefaultIcon = ContextCompat.getDrawable(getContext(), R.drawable.default_icon); //添加测试数据
//List<StepBean> datas = new ArrayList<>();
//datas.add(new StepBean("接单", StepBean.STEP_COMPLETED));
//datas.add(new StepBean("打包", StepBean.STEP_CURRENT));
//datas.add(new StepBean("出发", StepBean.STEP_UNDO));
//setStepNum(datas);
} /**
* 测量自身的高度
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = defaultStepIndicatorNum * 2;
if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) {
width = MeasureSpec.getSize(widthMeasureSpec);
}
int height = defaultStepIndicatorNum;
if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) {
height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
}
setMeasuredDimension(width, height);
} /**
* View大小改变
*
* @param w
* @param h
* @param oldw
* @param oldh
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//下面这三句代码只是为了绘制矩形的线
mCenterY = 0.5f * getHeight();
//获取左上方Y的位置,获取该点的意义是为了方便画矩形左上的Y位置
mLeftY = mCenterY - (mCompletedLineHeight / 2);
//获取右下方Y的位置,获取该点的意义是为了方便画矩形右下的Y位置
mRightY = mCenterY + mCompletedLineHeight / 2; mComplectedXPosition.clear();
//计算所有总空间离最左边的距离,并记录所有的圆心x轴的坐标
for (int i = 0; i < mStepNum; i++) {
//计算全部最左边
float paddingLeft = (getWidth() - mStepNum * mCircleRadius * 2 - (mStepNum - 1) * mLinePadding) / 2;
//将所有的圆心X轴坐标记录到集合中
mComplectedXPosition.add(paddingLeft + mCircleRadius + i * mCircleRadius * 2 + i * mLinePadding);
}
Log.i("wangjitao", "screenWidth:" + screenWidth + ",geiwidth:" + getWidth()); //当位置发生改变时的回调监听
// if (mOnDrawListener != null) {
// mOnDrawListener.ondrawIndicator();
// Log.i("wangjitao", "onSizeChanged");
// } } /**
* 绘制view
*
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas); if (mOnDrawListener != null) {
mOnDrawListener.ondrawIndicator();
Log.i("wangjitao", "onDraw");
}
mUnComplectedPaint.setColor(mUnCompletedLineColor);
mComplectedPaint.setColor(mCompletedLineColor); //首先绘制线
for (int i = 0; i < mComplectedXPosition.size() - 1; i++) {
//前一个ComplectedXPosition
final float preComplectedXPosition = mComplectedXPosition.get(i);
//后一个ComplectedXPosition
final float afterComplectedXPosition = mComplectedXPosition.get(i + 1);
if (i < mComplectingPosition) {
//判断在完成之前的所有的点画完成的线,这里是矩形(这里会有10像素的偏移,没懂)
canvas.drawRect(preComplectedXPosition + mCircleRadius - 10, mLeftY, afterComplectedXPosition - mCircleRadius + 10, mRightY, mComplectedPaint);
} else {
mPath.moveTo(preComplectedXPosition + mCircleRadius, mCenterY);
mPath.lineTo(afterComplectedXPosition - mCircleRadius, mCenterY);
canvas.drawPath(mPath, mUnComplectedPaint);
}
} //再绘制图标
for (int i = 0; i < mComplectedXPosition.size(); i++) {
final float currentComplectedXPosition = mComplectedXPosition.get(i);
//创建矩形
Rect rect = new Rect((int) (currentComplectedXPosition - mCircleRadius), (int) (mCenterY - mCircleRadius), (int) (currentComplectedXPosition + mCircleRadius), (int) (mCenterY + mCircleRadius));
if (i < mComplectingPosition) {
mCompleteIcon.setBounds(rect);
mCompleteIcon.draw(canvas);
} else if (i == mComplectingPosition && mComplectedXPosition.size() != 1) {
canvas.drawCircle(currentComplectedXPosition, mCenterY, mCircleRadius * 1.1f, mComplectedPaint);
mAttentionIcon.setBounds(rect);
mAttentionIcon.draw(canvas);
} else {
mDefaultIcon.setBounds(rect);
mDefaultIcon.draw(canvas);
}
} } /**
* 设置步骤的基本流程
*/
public void setStepNum(List<StepBean> stepsBeanList) {
this.mStepBeanList = stepsBeanList;
mStepNum = mStepBeanList.size(); if (mStepBeanList != null && mStepBeanList.size() > 0) {
for (int i = 0; i < mStepNum; i++) {
StepBean stepsBean = mStepBeanList.get(i);
{
if (stepsBean.getState() == StepBean.STEP_CURRENT) {
mComplectingPosition = i;
}
}
}
}
requestLayout();
} /**
* 设置监听
*
* @param onDrawListener
*/
public void setOnDrawListener(OnDrawIndicatorListener onDrawListener) {
mOnDrawListener = onDrawListener;
} /**
* 设置对view监听
*/
public interface OnDrawIndicatorListener {
void ondrawIndicator();
} /**
* 得到所有圆点所在的位置
*
* @return
*/
public List<Float> getComplectedXPosition() {
return mComplectedXPosition;
} /**
* 设置正在进行position
*
* @param complectingPosition
*/
public void setComplectingPosition(int complectingPosition) {
this.mComplectingPosition = complectingPosition;
invalidate();
} /**
* 设置正在进行position
*/
public int getComplectingPosition() {
return this.mComplectingPosition;
} /**
* 设置流程步数
*
* @param stepNum 流程步数
*/
public void setStepNum(int stepNum) {
this.mStepNum = stepNum;
invalidate();
} /**
* 设置未完成线的颜色
*
* @param unCompletedLineColor
*/
public void setUnCompletedLineColor(int unCompletedLineColor) {
this.mUnCompletedLineColor = unCompletedLineColor;
} /**
* 设置已完成线的颜色
*
* @param completedLineColor
*/
public void setCompletedLineColor(int completedLineColor) {
this.mCompletedLineColor = completedLineColor;
} /**
* 设置默认图片
*
* @param defaultIcon
*/
public void setDefaultIcon(Drawable defaultIcon) {
this.mDefaultIcon = defaultIcon;
} /**
* 设置已完成图片
*
* @param completeIcon
*/
public void setCompleteIcon(Drawable completeIcon) {
this.mCompleteIcon = completeIcon;
} /**
* 设置正在进行中的图片
*
* @param attentionIcon
*/
public void setAttentionIcon(Drawable attentionIcon) {
this.mAttentionIcon = attentionIcon;
}
}
- 创建StepView类,继承自LinearLayout,用于将我们上面的创建的StepViewIndicator和包含所有文字的LinearLayout合并在一起,其布局文件如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <com.qianmo.mystepview.view.StepViewIndicator
android:id="@+id/steps_indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"/> <RelativeLayout
android:id="@+id/rl_text_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
具体的代码如下
package com.qianmo.mystepview.view; import android.content.Context;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView; import com.qianmo.mystepview.R;
import com.qianmo.mystepview.bean.StepBean; import java.util.List; /**
* 日期:16/6/22 15:47
* <p/>
* 描述:StepView
*/
public class StepView extends LinearLayout implements StepViewIndicator.OnDrawIndicatorListener
{
private RelativeLayout mTextContainer;
private StepViewIndicator mStepsViewIndicator;
private List<StepBean> mStepBeanList;
private int mComplectingPosition;
private int mUnComplectedTextColor = ContextCompat.getColor(getContext(), R.color.uncompleted_text_color);//定义默认未完成文字的颜色;
private int mComplectedTextColor = ContextCompat.getColor(getContext(), android.R.color.white);//定义默认完成文字的颜色;
private int mTextSize = 14;//default textSize
private TextView mTextView; public StepView(Context context)
{
this(context, null);
} public StepView(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
} public StepView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
init();
} private void init()
{
View rootView = LayoutInflater.from(getContext()).inflate(R.layout.widget_horizontal_stepsview, this);
mStepsViewIndicator = (StepViewIndicator) rootView.findViewById(R.id.steps_indicator);
mStepsViewIndicator.setOnDrawListener(this);
mTextContainer = (RelativeLayout) rootView.findViewById(R.id.rl_text_container);
} /**
* 设置显示的文字
*
* @param stepsBeanList
* @return
*/
public StepView setStepViewTexts(List<StepBean> stepsBeanList)
{
mStepBeanList = stepsBeanList;
mStepsViewIndicator.setStepNum(mStepBeanList);
return this;
} /**
* 设置未完成文字的颜色
*
* @param unComplectedTextColor
* @return
*/
public StepView setStepViewUnComplectedTextColor(int unComplectedTextColor)
{
mUnComplectedTextColor = unComplectedTextColor;
return this;
} /**
* 设置完成文字的颜色
*
* @param complectedTextColor
* @return
*/
public StepView setStepViewComplectedTextColor(int complectedTextColor)
{
this.mComplectedTextColor = complectedTextColor;
return this;
} /**
* 设置StepsViewIndicator未完成线的颜色
*
* @param unCompletedLineColor
* @return
*/
public StepView setStepsViewIndicatorUnCompletedLineColor(int unCompletedLineColor)
{
mStepsViewIndicator.setUnCompletedLineColor(unCompletedLineColor);
return this;
} /**
* 设置StepsViewIndicator完成线的颜色
*
* @param completedLineColor
* @return
*/
public StepView setStepsViewIndicatorCompletedLineColor(int completedLineColor)
{
mStepsViewIndicator.setCompletedLineColor(completedLineColor);
return this;
} /**
* 设置StepsViewIndicator默认图片
*
* @param defaultIcon
*/
public StepView setStepsViewIndicatorDefaultIcon(Drawable defaultIcon)
{
mStepsViewIndicator.setDefaultIcon(defaultIcon);
return this;
} /**
* 设置StepsViewIndicator已完成图片
*
* @param completeIcon
*/
public StepView setStepsViewIndicatorCompleteIcon(Drawable completeIcon)
{
mStepsViewIndicator.setCompleteIcon(completeIcon);
return this;
} /**
* 设置StepsViewIndicator正在进行中的图片
*
* @param attentionIcon
*/
public StepView setStepsViewIndicatorAttentionIcon(Drawable attentionIcon)
{
mStepsViewIndicator.setAttentionIcon(attentionIcon);
return this;
} /**
* set textSize
*
* @param textSize
* @return
*/
public StepView setTextSize(int textSize)
{
if(textSize > 0)
{
mTextSize = textSize;
}
return this;
} @Override
public void ondrawIndicator()
{
if(mTextContainer != null)
{
mTextContainer.removeAllViews();
List<Float> complectedXPosition = mStepsViewIndicator.getComplectedXPosition();
if(mStepBeanList != null && complectedXPosition != null && complectedXPosition.size() > 0)
{
for(int i = 0; i < mStepBeanList.size(); i++)
{
mTextView = new TextView(getContext());
mTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSize);
mTextView.setText(mStepBeanList.get(i).getName());
int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
mTextView.measure(spec, spec);
// getMeasuredWidth
int measuredWidth = mTextView.getMeasuredWidth();
mTextView.setX(complectedXPosition.get(i) - measuredWidth / 2);
mTextView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); if(i <= mComplectingPosition)
{
mTextView.setTypeface(null, Typeface.BOLD);
mTextView.setTextColor(mComplectedTextColor);
} else
{
mTextView.setTextColor(mUnComplectedTextColor);
} mTextContainer.addView(mTextView);
}
}
}
} }
但是我们关注ondrawIndicator()这个方法中的逻辑,主要是通过OnDrawIndicatorListener这个类的回调来绘制对应的文字描述,再看看在Activity中只的使用
StepView setpview = (StepView) findViewById(R.id.step_view0);
List<StepBean> stepsBeanList = new ArrayList<>();
StepBean stepBean0 = new StepBean("手机绑定",1);
StepBean stepBean1 = new StepBean("提交信息",1);
StepBean stepBean2 = new StepBean("充值",0);
StepBean stepBean3 = new StepBean("用车",-1); stepsBeanList.add(stepBean0);
stepsBeanList.add(stepBean1);
stepsBeanList.add(stepBean2);
stepsBeanList.add(stepBean3); setpview.setStepViewTexts(stepsBeanList)
.setTextSize(16);//set textSize
很简单有没有,看着一次我们自己写的效果有没有之前作者那种文字重叠的问题 如下图:
很明显,没有,因为我在StepViewIndicator类中将的mLinePadding属性的值修改大了
修改之后
mLinePadding = 1.5f * defaultStepIndicatorNum;
修改之钱
mLinePadding = 0.85f * defaultStepIndicatorNum;
这里其实可以提供一个set方法来设置动态的设置一下实线和虚线的距离的,好了,这样我们就是实现了我们的需求了,再次感谢StepView的作者,如果有需要源码的同学,可以去下一下,Github下载地址,See You Next Time !!!
Android -- 自定义StepView实现个人信息验证进度条的更多相关文章
- 我的Android进阶之旅------>Android自定义View实现带数字的进度条(NumberProgressBar)
今天在Github上面看到一个来自于 daimajia所写的关于Android自定义View实现带数字的进度条(NumberProgressBar)的精彩案例,在这里分享给大家一起来学习学习!同时感谢 ...
- CodePush自定义更新弹框及下载进度条
CodePush 热更新之自定义更新弹框及下载进度 先来几张弹框效果图 非强制更新场景 image 强制更新场景 image 更新包下载进度效果 image 核心代码 这里的热更新Modal框,是封装 ...
- Android——用对话框做登陆界面(自定义对话框AlertDialog,多线程,进度条ProgressDialog,ListView,GridView,SharedPreferences存,读数据,存取文本,assets文件)
效果: 1.点击图标进入页面二 2.页面2图片暂停显示5秒进入页面三 3.点击页面三登陆按钮,打开登陆对话框,输入密码进入页面四 点击下载按钮,显示水平进度条 点击保存和获取用户名和密码 进入页面六 ...
- Android 高手进阶,自己定义圆形进度条
背景介绍 在Android 开发中,我们常常遇到各种各样绚丽的控件,所以,依靠我们Android本身所带的控件是远远不够的,许多时候须要我们自定义控件,在开发的过程中.我们公司遇到了一种须要自己写的一 ...
- Android自定义控件系列之应用篇——圆形进度条
一.概述 在上一篇博文中,我们给大家介绍了Android自定义控件系列的基础篇.链接:http://www.cnblogs.com/jerehedu/p/4360066.html 这一篇博文中,我们将 ...
- R随机森林交叉验证 + 进度条
library(data.table) library(randomForest) data <- iris str(data) #交叉验证,使用rf预测sepal.length k = 5 d ...
- 让android系统中任意一个view变成进度条
1.效果 2.进度条背景drawable文件 结束后可以恢复原背景. <?xml version="1.0" encoding="utf-8"?> ...
- Android中的常用控件之进度条(ProgressBar)
ProgressBar的常用属性:style,进度条的样式,默认为圆形,用style="?android:attr/progressBarStyleHorizontal"可以将进度 ...
- android 仿摩拜单车共享单车进度条实现StepView
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6552712.html 先看效果图: Step1:定义StepBean 定义五个状态,分别为:为完成.正在进行 ...
随机推荐
- Grunt + Bower—前端构建利器
目前比较流行的WEB开发的趋势是前后端分离.前端采用重量级的Javascript框架,比如Angular,Ember等,后端采用restful API的Web Service服务,通过JSON格式进行 ...
- CentOS 6.5下NFS安装配置
[root@local /]# yum -y install nfs-utils rpcbind3.创建共享目录:[root@local /]# mkdir /sharestore4.NFS共享文件路 ...
- linux系统时间同步
1.linux系统时间同步[root@xuegod62 ~]# /usr/sbin/ntpdate ntp1.aliyun.com2.linux系统定时同步[root@xuegod62 ~]# ech ...
- Windows 10 IoT Core环境配置中的那些坑
我使用的设备是Raspberry Pi 3B,想来国内的嵌入式玩具应该还是树莓派最常见吧.这段时间一直在捣鼓Win10 IoT,结果发现,从安装一直到编码调试一路下来全都是坑.写这篇东西一个是为了备忘 ...
- (@WhiteTaken)设计模式学习——工厂方法模式
这个工厂方法模式,是简单工厂的延伸,不同点在于,将某个具体的类继续细分,将核心部分抽象成一个接口.而简单工厂,把核心写在了一个类上,不利于拓展. 举个例子,简单工厂中有苹果类,香蕉类,我们创建了一个F ...
- C++编程练习(2)----“实现简单的线性表的链式存储结构“
单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素. 对于查找操作,单链表的时间复杂度为O(n). 对于插入和删除操作,单链表在确定位置后,插入和删除时间仅为O(1). 单链表不需要分配存储 ...
- 面试之Java知识整理
1.面向对象都有哪些特性 继承.封装.多态性.抽象 2.Java中实现多态的机制是什么? 继承与接口 3.Java中异常分为哪些种类 3.1按照异常需要处理的时机分为编译时异常(CheckedExce ...
- UITableView、UICollectionView行高/尺寸自适应
UITableView 我们都知道UITableView从iOS 8开始实现行高的自适应相对比较简单,首先必须设置estimatedRowHeight给出预估高度,设置rowHeight为UITabl ...
- Swift 2.2 协议和代理
一:代理 两个类之间的传值,类A调用类B的方法,类B在执行过程中遇到问题通知类A,这时候我们需要用到代理(Delegate). 比如:控制器(Controller)与控制器(Controller)之间 ...
- React库
一.React概述 React是一个是一个开源的js库,用来为数据渲染视图的,由facebook,Instagram社区维护的.(例如美团.阿里.airbnb都在使用React开发) 为什么会出现Re ...