现在大多牵扯到押金之类的app都会涉及到身份认证及开通业务或者退押金等业务,而这些操作属于一系列的流程操作,也需要一定的时间来完成所有如下图,所以想写一个关于认证流程类的自定义view来呈现这个业务。
一、分析View
我们先来定义状态概念和分析view的绘制流程
- 定义状态:
1)未认证状态:❌
2)认证完成状态:✅+ 外圈圆
3)正在认证办理状态 : 原点和外圈圆 - 显示逻辑
1)若任何一个流程都没有完成,我们则默认正在进行第一步认证流程,其他的流程为未认证;
2)若其中一个流程已经完成,则当前流程之前的流程全部为认证完成,当前流程的下一个流程为正在认证状态,再剩下的就为未完成状态; - 绘制流程
1)首先分为两个部分,一个是文字部分,另一个是图片进度部分;
2)我们先绘制图片进度部分,先对其进行拆分,可以分为中间基线(白色),然后完成和最近未完成之间粗基线,再者是对应的图片绘制;
3)接下来再计算对应文字的位置来绘制对应文字部分
二、编码View
-
创建类CertificationProgress继承View
public class CertificationProgress extends View { public CertificationProgress(Context context) { super(context); } public CertificationProgress(Context context, @Nullable @android.support.annotation.Nullable AttributeSet attrs) { super(context, attrs); } public CertificationProgress(Context context, @Nullable @android.support.annotation.Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } }
-
接下来应该是我们的测量工作,由于我们的view的测量和我们将使用到的三张图片的大小有关系,所以我们先创建
init(Context context, AttributeSet attrs, int defStyleAttr)
方法,并在其中加载获取我们需要的三个图片资源,这样我们就可以获取到图片的尺寸,作为我们对应的测量mode的数据参考;/** * 流程完成提示图 */ private Bitmap certification_finish_img; /** * 流程进行中提示图 */ private Bitmap certification_ing_img; /** * 流程未完成提示图 */ private Bitmap certification_not_finish_img; public CertificationProgress(Context context) { super(context); init(context, null, 0); } public CertificationProgress(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context, attrs, 0); } public CertificationProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr); } public void init(Context context, AttributeSet attrs, int defStyleAttr) { certification_finish_img = BitmapFactory.decodeResource(getResources(), R.drawable.certification_finish); certification_ing_img = BitmapFactory.decodeResource(getResources(), R.drawable.certification_ing); certification_not_finish_img = BitmapFactory.decodeResource(getResources(), R.drawable.certification_not_finish); }
-
测量部分,重写
onMeasure(int widthMeasureSpec, int heightMeasureSpec)
方法,对于MeasureSpec.EXACTLY
模式我们就直接使用系统测量出来的大小,剩下的两种模式MeasureSpec.AT_MOST、MeasureSpec.UNSPECIFIED
,我们需要自己来规定我们view的宽高,对于View宽偷个懒就直接让其于屏幕宽度一致,我们着重来计算取舍一下view的高度,由效果图我们分析可以知道,在垂直方向主要的高度是paddingTop+paddingBottom+文字高度+三个图片中最高的值
,为了更美观简洁大方点,我们需要给文字和对应图片进度条一定的间距,在文字的上下间距我们分别取文字高度的一半,为此根据内容来测量得到的高度就为:textHeight * 2 + (maxImgYRadio << 1) + getPaddingTop() + getPaddingBottom();
我们将其和系统给我们的测量值比较取最小值。public class CertificationProgress extends View { /** * 流程完成提示图 */ private Bitmap certification_finish_img; /** * 流程进行中提示图 */ private Bitmap certification_ing_img; /** * 流程未完成提示图 */ private Bitmap certification_not_finish_img; /** * maxImgYRadio : 图片中Y轴方向最大半径 * maxImgXRadio : 图片中X轴方向最大半径 */ private int maxImgYRadio, maxImgXRadio; /** * 文字高度 */ private int textHeight; /** * 文字画笔 */ private TextPaint mTextPaint; /** * 文字大小 */ private int textSize; /** * 流程步骤 */ private final String[] stepTextContent = {"手机验证", "实名认证", "开通业务", "注册完成"}; /** * 流程步骤文字长度 */ private final float[] stepTextLength = new float[stepTextContent.length]; public CertificationProgress(Context context) { super(context); init(context, null, 0); } public CertificationProgress(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context, attrs, 0); } public CertificationProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr); } public void init(Context context, AttributeSet attrs, int defStyleAttr) { certification_finish_img = BitmapFactory.decodeResource(getResources(), R.drawable.certification_finish); certification_ing_img = BitmapFactory.decodeResource(getResources(), R.drawable.certification_ing); certification_not_finish_img = BitmapFactory.decodeResource(getResources(), R.drawable.certification_not_finish); maxImgYRadio = Math.max(certification_ing_img.getHeight(), Math.max(certification_finish_img.getHeight(), certification_not_finish_img.getHeight())) >> 1; maxImgXRadio = Math.max(certification_ing_img.getWidth(), Math.max(certification_not_finish_img.getWidth(), certification_finish_img.getWidth())) >> 1; mTextPaint = new TextPaint(); mTextPaint.setTextSize(textSize); //计算文字高度和对应文本的长度 Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics(); textHeight = (int) (fontMetrics.bottom - fontMetrics.top); for (int i = 0; i < stepTextContent.length; i++) { stepTextLength[i] = mTextPaint.measureText(stepTextContent[i]); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int setWidth, setHeight; int widthMode = MeasureSpec.getMode(widthMeasureSpec); int measureWidth = MeasureSpec.getSize(widthMeasureSpec); DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); if (widthMode == MeasureSpec.EXACTLY) { setWidth = measureWidth; } else { setWidth = displayMetrics.widthPixels; } int heightMode = MeasureSpec.getMode(heightMeasureSpec); int measureHeight = MeasureSpec.getSize(heightMeasureSpec); if (heightMode == MeasureSpec.EXACTLY) { setHeight = measureHeight; } else { int h = textHeight * 2 + (maxImgYRadio << 1) + getPaddingTop() + getPaddingBottom(); setHeight = Math.min(h, measureHeight); } setMeasuredDimension(setWidth, setHeight); } }
-
在测量完成后就是我们的绘制流程了,由于我们包含了padding值和受到图片中心位置的绘制影响,我们计算出实际绘制的区域宽高w、h,并将画布移动到对应绘制区域,减少我们在绘制时padding的计算工作量:
@Override protected void onDraw(Canvas canvas) { int w = getWidth() - getPaddingLeft() - getPaddingRight() - maxImgXRadio * 2; int h = getHeight() - getPaddingTop() - getPaddingBottom(); canvas.translate(getPaddingLeft() + maxImgXRadio, getPaddingTop()); }
-
根据上面已经分析过的绘制流程,我们可以先绘制文字或者先绘制图形,这里就先绘制图形吧,由于每一个步骤都有对应不同图形呈现,所以我们先定义变量step其取之范围为[0-4]和我们baseLineColor(这里不再讲述),第一步绘制baseLine,其分为2种情况,由于所有情形可以覆盖到对应最细线条上,所以不管任何情况我们先绘制最细的基准线条,接下来在绘制各个其他情况:
1)所有流程均未完成,那么只有最基础的细线条,所以不做任何事情;
2)将第一个流程点到当前完成到达点的下一个步骤点之间的线条加粗,剩下的其他线条全部不变,不做任何操作;最后将画笔重置,防止当前状态发生改变刷新:@Override protected void onDraw(Canvas canvas) { int w = getWidth() - getPaddingLeft() - getPaddingRight() - maxImgXRadio * 2; int h = getHeight() - getPaddingTop() - getPaddingBottom(); canvas.translate(getPaddingLeft() + maxImgXRadio, getPaddingTop()); drawBaseLine(w, h, canvas); } private void drawBaseLine(int w, int h, Canvas canvas) { mPaint.reset(); mPaint.setColor(baseLineColor); canvas.drawLine(0, completeTop(h, null), w, completeTop(h, null), mPaint); mPaint.setStrokeWidth(10); if (step < 1) return; if (step < 2) { canvas.drawLine(0, completeTop(h, null), w / 3.0f, completeTop(h, null), mPaint); } else if (step < 3) { canvas.drawLine(0, completeTop(h, null), w * 2 / 3.0f, completeTop(h, null), mPaint); } else { canvas.drawLine(0, completeTop(h, null), w, completeTop(h, null), mPaint); } mPaint.reset(); }
-
接下来就根据变量值step来绘制显示对应流程的图片,需要注意是,为了均匀绘制,我们需要在对应绘制中心点位置和图片X轴上的做半径来确定图形位置:
@Override protected void onDraw(Canvas canvas) { int w = getWidth() - getPaddingLeft() - getPaddingRight() - maxImgXRadio * 2; int h = getHeight() - getPaddingTop() - getPaddingBottom(); canvas.translate(getPaddingLeft() + maxImgXRadio, getPaddingTop()); drawBaseLine(w, h, canvas); drawStepOne(w, h, canvas); drawStepTwo(w, h, canvas); drawStepThree(w, h, canvas); drawStepFour(w, h, canvas); } private void drawBaseLine(int w, int h, Canvas canvas) { mPaint.reset(); mPaint.setColor(baseLineColor); canvas.drawLine(0, completeTop(h, null), w, completeTop(h, null), mPaint); mPaint.setStrokeWidth(10); if (step < 1) return; if (step < 2) { canvas.drawLine(0, completeTop(h, null), w / 3.0f, completeTop(h, null), mPaint); } else if (step < 3) { canvas.drawLine(0, completeTop(h, null), w * 2 / 3.0f, completeTop(h, null), mPaint); } else { canvas.drawLine(0, completeTop(h, null), w, completeTop(h, null), mPaint); } mPaint.reset(); } /** * 0 为没有一个步骤通过,且正在进行第一个步骤 * 1 第一个步骤完成,正在进行第二个步骤 * 2 第二个步骤完成,正在进行第三个步骤 * 3 第三个步骤完成,正在进行第四个步骤 * 4 所有步骤完成 */ private void drawStepOne(int w, int h, Canvas canvas) { if (step < 1) { canvas.drawBitmap(certification_ing_img, -maxImgXRadio, completeTop(h, certification_ing_img), mPaint); } else { canvas.drawBitmap(certification_ing_img, -maxImgXRadio, completeTop(h, certification_ing_img), mPaint); canvas.drawBitmap(certification_finish_img, -certification_finish_img.getWidth() / 2.0f, completeTop(h, certification_finish_img), mPaint); } } private void drawStepTwo(int w, int h, Canvas canvas) { if (step < 1) { canvas.drawBitmap(certification_not_finish_img, w / 3.0f - certification_not_finish_img.getWidth() / 2.0f, completeTop(h, certification_not_finish_img), mPaint); } else if (step < 2) { canvas.drawBitmap(certification_ing_img, w / 3.0f - maxImgXRadio, completeTop(h, certification_ing_img), mPaint); } else { canvas.drawBitmap(certification_ing_img, w / 3.0f - maxImgXRadio, completeTop(h, certification_ing_img), mPaint); canvas.drawBitmap(certification_finish_img, w / 3.0f - certification_finish_img.getWidth() / 2.0f, completeTop(h, certification_finish_img), mPaint); } } private void drawStepThree(int w, int h, Canvas canvas) { if (step < 2) { canvas.drawBitmap(certification_not_finish_img, w * 2 / 3.0f - certification_not_finish_img.getWidth() / 2.0f, completeTop(h, certification_not_finish_img), mPaint); } else if (step < 3) { canvas.drawBitmap(certification_ing_img, w * 2 / 3.0f - maxImgXRadio, completeTop(h, certification_ing_img), mPaint); } else { canvas.drawBitmap(certification_ing_img, w * 2 / 3.0f - maxImgXRadio, completeTop(h, certification_ing_img), mPaint); canvas.drawBitmap(certification_finish_img, w * 2 / 3.0f - certification_finish_img.getWidth() / 2.0f, completeTop(h, certification_finish_img), mPaint); } } private void drawStepFour(int w, int h, Canvas canvas) { if (step < 3) { canvas.drawBitmap(certification_not_finish_img, w - certification_not_finish_img.getWidth() / 2.0f, completeTop(h, certification_not_finish_img), mPaint); } else if (step < 4) { canvas.drawBitmap(certification_ing_img, w - maxImgXRadio, completeTop(h, certification_ing_img), mPaint); } else { canvas.drawBitmap(certification_ing_img, w - maxImgXRadio, completeTop(h, certification_ing_img), mPaint); canvas.drawBitmap(certification_finish_img, w - certification_finish_img.getWidth() / 2.0f, completeTop(h, certification_finish_img), mPaint); } } private float completeTop(int h, Bitmap bitmap) { return (h + textHeight * 2 - (bitmap == null ? 0 : bitmap.getHeight())) / 2.0f; }
来看一下初步效果:
还是不错的,由于基本效果是出来,由于我们step默认值是0,所以看不出完成部分进度的效果,后面我们将在测试环节来进行测试展示。 -
现在我们来绘制文字,主要还是计算出对应的文字绘制的位置x、y,其中x为
对应位置分割中心点减去文字长度的一半
,y等于textHeight*3/2-fontMetrics.bottom
(由于文字距离图片有间隙,所以计算文字baseline时要取3/2):@Override protected void onDraw(Canvas canvas) { int w = getWidth() - getPaddingLeft() - getPaddingRight() - maxImgXRadio * 2; int h = getHeight() - getPaddingTop() - getPaddingBottom(); canvas.translate(getPaddingLeft() + maxImgXRadio, getPaddingTop()); drawBaseLine(w, h, canvas); drawStepOne(w, h, canvas); drawStepTwo(w, h, canvas); drawStepThree(w, h, canvas); drawStepFour(w, h, canvas); drawStepText(w, h, canvas); } private void drawStepText(int w, int h, Canvas canvas) { Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics(); mTextPaint.setColor(textColor); for (int i = 0; i < stepTextContent.length; i++) { canvas.drawText(stepTextContent[i], i * w / 3.0f - stepTextLength[0] / 2.0f, textHeight * 3 / 2.0f - fontMetrics.bottom, mTextPaint); } }
来看一下效果:
-
到此基本效果已经完成来,最后是添加对于step的动态设置,如下(完整代码在最后给出):
public void setStep(int step) { this.step = step; postInvalidate(); }
三、测试View
编写四个按钮分别对应的step 0 - 4
-
布局文件
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.act.ViewActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/img_bg" android:orientation="horizontal"> <vip.zhuahilong.mydefineview.widget.CertificationProgress android:id="@+id/certificationProgress" android:layout_width="match_parent" android:layout_height="wrap_content" app:textSize="10dp" app:textColor="@color/white" android:layout_gravity="bottom" android:padding="10dp" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:orientation="horizontal" android:weightSum="5"> <Button android:id="@+id/step0" android:layout_width="wrap_content" android:layout_height="?actionBarSize" android:layout_weight="1" android:text="step0" android:textAllCaps="false" /> <Button android:id="@+id/step1" android:layout_width="wrap_content" android:layout_height="?actionBarSize" android:layout_weight="1" android:text="step1" android:textAllCaps="false" /> <Button android:id="@+id/step2" android:layout_width="wrap_content" android:layout_height="?actionBarSize" android:layout_weight="1" android:text="step2" android:textAllCaps="false" /> <Button android:id="@+id/step3" android:layout_width="wrap_content" android:layout_height="?actionBarSize" android:layout_weight="1" android:text="step3" android:textAllCaps="false" /> <Button android:id="@+id/step4" android:layout_width="wrap_content" android:layout_height="?actionBarSize" android:layout_weight="1" android:text="step4" android:textAllCaps="false" /> </LinearLayout> <vip.zhuahilong.mydefineview.widget.ALiPayWaitResultView android:id="@+id/viewAnimator" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" android:visibility="gone" /> </RelativeLayout>
-
activity代码
public class ViewActivity extends BaseActivity { @BindView(R.id.certificationProgress) CertificationProgress mCertificationProgress; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_view); } @OnClick({R.id.step0, R.id.step1, R.id.step2, R.id.step3, R.id.step4}) public void viewClick(View view) { switch (view.getId()) { case R.id.step0: mCertificationProgress.setStep(0); break; case R.id.step1: mCertificationProgress.setStep(1); break; case R.id.step2: mCertificationProgress.setStep(2); break; case R.id.step3: mCertificationProgress.setStep(3); break; case R.id.step4: mCertificationProgress.setStep(4); break; } } }
-
运行效果为:
点击step1,效果为:
点击step2,效果为:
点击step3,效果为:
点击step4,效果为:
点击step0,效果为:
测试完美,这样我们自定义view-流程进度条就完成了,下次我们将对其进行改造,升级为动态设置步骤数量和步骤名称。点我瞬间移动到“自定义view-流程进度条动态调控”篇
四、代码
-
自定义属性文件
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CertificationProgress"> <attr name="textSize" format="dimension" /> <attr name="textColor" format="color" /> <attr name="baseLineColor" format="color" /> </declare-styleable> </resources>
-
代码
/** * @author :朱海龙 * @date :Created in 2019-06-21 15:19 * @description:关于认证进度等 * @modified By: * @version: */ public class CertificationProgress extends View { /** * 字体默认大小 */ public static final int defaultTextSize = 30; /** * 字体默认颜色 */ public static final int defaultTextColor = Color.BLACK; /** * 基线默认颜色 */ public static final int defaultBaseLineColor = Color.WHITE; /** * 文字画笔 */ private TextPaint mTextPaint; /** * 文字大小 */ private int textSize; /** * 文字颜色 */ private int textColor; /** * 文字高度 */ private int textHeight; /** * 基线颜色 */ private int baseLineColor; /** * 通用画笔 */ private Paint mPaint; /** * 流程完成提示图 */ private Bitmap certification_finish_img; /** * 流程进行中提示图 */ private Bitmap certification_ing_img; /** * 流程未完成提示图 */ private Bitmap certification_not_finish_img; /** * maxImgYRadio : 图片中Y轴方向最大半径 * maxImgXRadio : 图片中X轴方向最大半径 */ private int maxImgYRadio, maxImgXRadio; /** * 0 为没有一个步骤通过,且正在进行第一个步骤 * 1 第一个步骤完成,正在进行第二个步骤 * 2 第二个步骤完成,正在进行第三个步骤 * 3 第三个步骤完成,正在进行第四个步骤 * 4 所有步骤完成 */ @Size(min = 0, max = 4) private int step = 0; /** * 流程步骤 */ private final String[] stepTextContent = {"手机验证", "实名认证", "开通业务", "注册完成"}; /** * 流程步骤文字长度 */ private final float[] stepTextLength = new float[stepTextContent.length]; public CertificationProgress(Context context) { super(context); init(context, null, 0); } public CertificationProgress(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context, attrs, 0); } public CertificationProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr); } public void init(Context context, AttributeSet attrs, int defStyleAttr) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CertificationProgress); textSize = (int) typedArray.getDimension(R.styleable.CertificationProgress_textSize, defaultTextSize); textColor = typedArray.getColor(R.styleable.CertificationProgress_textColor, defaultTextColor); baseLineColor = typedArray.getColor(R.styleable.CertificationProgress_baseLineColor, defaultBaseLineColor); typedArray.recycle(); mTextPaint = new TextPaint(); mTextPaint.setTextSize(textSize); Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics(); textHeight = (int) (fontMetrics.bottom - fontMetrics.top); for (int i = 0; i < stepTextContent.length; i++) { stepTextLength[i] = mTextPaint.measureText(stepTextContent[i]); } mPaint = new Paint(); certification_finish_img = BitmapFactory.decodeResource(getResources(), R.drawable.certification_finish); certification_ing_img = BitmapFactory.decodeResource(getResources(), R.drawable.certification_ing); certification_not_finish_img = BitmapFactory.decodeResource(getResources(), R.drawable.certification_not_finish); maxImgYRadio = Math.max(certification_ing_img.getHeight(), Math.max(certification_finish_img.getHeight(), certification_not_finish_img.getHeight())) >> 1; maxImgXRadio = Math.max(certification_ing_img.getWidth(), Math.max(certification_not_finish_img.getWidth(), certification_finish_img.getWidth())) >> 1; } @Override protected void onDraw(Canvas canvas) { int w = getWidth() - getPaddingLeft() - getPaddingRight() - maxImgXRadio * 2; int h = getHeight() - getPaddingTop() - getPaddingBottom(); canvas.translate(getPaddingLeft() + maxImgXRadio, getPaddingTop()); drawBaseLine(w, h, canvas); drawStepOne(w, h, canvas); drawStepTwo(w, h, canvas); drawStepThree(w, h, canvas); drawStepFour(w, h, canvas); drawStepText(w, h, canvas); } private void drawStepText(int w, int h, Canvas canvas) { Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics(); mTextPaint.setColor(textColor); for (int i = 0; i < stepTextContent.length; i++) { canvas.drawText(stepTextContent[i], i * w / 3.0f - stepTextLength[0] / 2.0f, textHeight * 3 / 2.0f - fontMetrics.bottom, mTextPaint); } } private void drawBaseLine(int w, int h, Canvas canvas) { mPaint.reset(); mPaint.setColor(baseLineColor); canvas.drawLine(0, completeTop(h, null), w, completeTop(h, null), mPaint); mPaint.setStrokeWidth(10); if (step < 1) return; if (step < 2) { canvas.drawLine(0, completeTop(h, null), w / 3.0f, completeTop(h, null), mPaint); } else if (step < 3) { canvas.drawLine(0, completeTop(h, null), w * 2 / 3.0f, completeTop(h, null), mPaint); } else { canvas.drawLine(0, completeTop(h, null), w, completeTop(h, null), mPaint); } mPaint.reset(); } /** * 0 为没有一个步骤通过,且正在进行第一个步骤 * 1 第一个步骤完成,正在进行第二个步骤 * 2 第二个步骤完成,正在进行第三个步骤 * 3 第三个步骤完成,正在进行第四个步骤 * 4 所有步骤完成 */ private void drawStepOne(int w, int h, Canvas canvas) { if (step < 1) { canvas.drawBitmap(certification_ing_img, -maxImgXRadio, completeTop(h, certification_ing_img), mPaint); } else { canvas.drawBitmap(certification_ing_img, -maxImgXRadio, completeTop(h, certification_ing_img), mPaint); canvas.drawBitmap(certification_finish_img, -certification_finish_img.getWidth() / 2.0f, completeTop(h, certification_finish_img), mPaint); } } private void drawStepTwo(int w, int h, Canvas canvas) { if (step < 1) { canvas.drawBitmap(certification_not_finish_img, w / 3.0f - certification_not_finish_img.getWidth() / 2.0f, completeTop(h, certification_not_finish_img), mPaint); } else if (step < 2) { canvas.drawBitmap(certification_ing_img, w / 3.0f - maxImgXRadio, completeTop(h, certification_ing_img), mPaint); } else { canvas.drawBitmap(certification_ing_img, w / 3.0f - maxImgXRadio, completeTop(h, certification_ing_img), mPaint); canvas.drawBitmap(certification_finish_img, w / 3.0f - certification_finish_img.getWidth() / 2.0f, completeTop(h, certification_finish_img), mPaint); } } private void drawStepThree(int w, int h, Canvas canvas) { if (step < 2) { canvas.drawBitmap(certification_not_finish_img, w * 2 / 3.0f - certification_not_finish_img.getWidth() / 2.0f, completeTop(h, certification_not_finish_img), mPaint); } else if (step < 3) { canvas.drawBitmap(certification_ing_img, w * 2 / 3.0f - maxImgXRadio, completeTop(h, certification_ing_img), mPaint); } else { canvas.drawBitmap(certification_ing_img, w * 2 / 3.0f - maxImgXRadio, completeTop(h, certification_ing_img), mPaint); canvas.drawBitmap(certification_finish_img, w * 2 / 3.0f - certification_finish_img.getWidth() / 2.0f, completeTop(h, certification_finish_img), mPaint); } } private void drawStepFour(int w, int h, Canvas canvas) { if (step < 3) { canvas.drawBitmap(certification_not_finish_img, w - certification_not_finish_img.getWidth() / 2.0f, completeTop(h, certification_not_finish_img), mPaint); } else if (step < 4) { canvas.drawBitmap(certification_ing_img, w - maxImgXRadio, completeTop(h, certification_ing_img), mPaint); } else { canvas.drawBitmap(certification_ing_img, w - maxImgXRadio, completeTop(h, certification_ing_img), mPaint); canvas.drawBitmap(certification_finish_img, w - certification_finish_img.getWidth() / 2.0f, completeTop(h, certification_finish_img), mPaint); } } private float completeTop(int h, Bitmap bitmap) { return (h + textHeight * 2 - (bitmap == null ? 0 : bitmap.getHeight())) / 2.0f; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int setWidth, setHeight; int widthMode = MeasureSpec.getMode(widthMeasureSpec); int measureWidth = MeasureSpec.getSize(widthMeasureSpec); DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); if (widthMode == MeasureSpec.EXACTLY) { setWidth = measureWidth; } else { setWidth = displayMetrics.widthPixels; } int heightMode = MeasureSpec.getMode(heightMeasureSpec); int measureHeight = MeasureSpec.getSize(heightMeasureSpec); if (heightMode == MeasureSpec.EXACTLY) { setHeight = measureHeight; } else { int h = textHeight * 2 + (maxImgYRadio << 1) + getPaddingTop() + getPaddingBottom(); setHeight = Math.min(h, measureHeight); } setMeasuredDimension(setWidth, setHeight); } public void setStep(int step) { this.step = step; postInvalidate(); } }