前言:拿到UI设计好的进度展示效果图后,脑中浮现的第一个想法是,如此简单的进度条,何必我亲自动手去写,随便网上百度一个就解决问题了。于是就网上百度了一番,结果让我大失所望。没有一款是我的菜,总有和我想要的效果有差距,因此只好老老实实自己去实现啦!在此,简单记录一下实现过程与原理,与大家简单分享一下。
最终效果图:废话不多说,首先上要实现的最终效果图
实现原理及过程:看似很简单的功能,其实在实现过程中还是有许多值得注意的地方。这里我们从实现原理和具体实现过程两个方面介绍这个自定义控件。
了解了实现原理,接下来的工作就和简单了,剩下的就是一步一步实现这些效果的绘制就好了。下面我们就通过具体代码讲解View的实现过程。
具体实现过程
一. 绘制进度条背景:Canvas有很多种方式可以实现背景条的绘制(如画线,画矩形,画圆角矩形),考虑到最终效果的美观性,这里我们采用绘制灰色的圆角矩形作为进度条的背景。
代码也很简单,如下所示:
//绘制进度条底部背景
//通过画线实现背景进度条
//canvas.drawLine(0, startLineLocationY, getScreenWidth(), startLineLocationY, mProgressPaint);
//通过画圆角矩形实现背景进度条
canvas.drawRoundRect(0, startLineLocationY, getScreenWidth(), startLineLocationY + mProgressBarHeight, mRectCorn, mRectCorn, mProgressPaint);
绘制出来的效果就是一个灰色的进度背景,真相如下:
二. 绘制当前进度:绘制当前进度所占比例的圆角矩形(和上面绘制背景相似,仅仅是使用不同的前景色以区分总进度和当前进度)
代码也和上面类似,只是需要计算当前进度值占总进度的比例,从而计算出所占比例值的圆角矩形宽度,进而绘制当前进度的矩形效果。代码如下所示:
//绘制已经完成了的进度条,设置当前进度的颜色
mProgressPaint.setColor(mProgressForeColor);
//计算进度比例值
double progress = (currentProgress - startProgress) / (endProgress - startProgress);
//计算比例所占宽度
int currProgress = (int) (getScreenWidth() * progress);
//canvas.drawLine(0, startLineLocationY, currProgress, startLineLocationY, mProgressPaint);
canvas.drawRoundRect(0, startLineLocationY, currProgress, startLineLocationY + mProgressBarHeight, mRectCorn, mRectCorn, mProgressPaint);
三. 绘制进度值提示框:这个过程我是通过Path去实现的,通过Path依次去连接提示框的所有顶点坐标,再通过Canvas去填充这个Path所描述的区域。这里需要注意的是提示框需要跟随当前进度值移动,所以绘制的提示框三角形箭头位置需要由当前进度值去确定。另外,进度框的大小需要去根据提示框显示的文字和字体大小去综合确定,所以这一步的逻辑还是稍显复杂的。
代码如下所示,具体实现逻辑在代码中详细讲解:
/*
绘制显示文字三角形框
*/
//计算文字显示区域的宽度和高度,mTextBound和mTextFontMetrics用于计算文字显示区域的宽度和高度,如果对这些还不熟悉的同学,我也不再去赘述了,可以去看看HenCoder大神的文章,讲解的很细致,连接如下:https://juejin.im/post/5975ba086fb9a06ba0252fd4
int textWidth = mTextBound.right - mTextBound.left;
int textHeight = mTextFontMetrics.bottom - mTextFontMetrics.top;
//计算三角形定点开始时的y坐标
int startTriangleY = startLineLocationY - mProgressBarHeight / 2 - mLine2TextDividerHeight;
//描点确定提示框的位置
mPath.moveTo(currProgress, startTriangleY);
mPath.lineTo(currProgress + 10, startTriangleY - mTriangleHeight);
mPath.lineTo(currProgress + textWidth / 2 + TEXT_LEFT_RIGHT_PADDING, startTriangleY - mTriangleHeight);
mPath.lineTo(currProgress + textWidth / 2 + TEXT_LEFT_RIGHT_PADDING, startTriangleY - mTriangleHeight - textHeight);
mPath.lineTo(currProgress - textWidth / 2 - TEXT_LEFT_RIGHT_PADDING, startTriangleY - mTriangleHeight - textHeight);
mPath.lineTo(currProgress - textWidth / 2 - TEXT_LEFT_RIGHT_PADDING, startTriangleY - mTriangleHeight);
mPath.lineTo(currProgress - 10, startTriangleY - mTriangleHeight);
mPath.close();
//画提示框
canvas.drawPath(mPath, mProgressPaint);
进行到这一步,大体效果就差不多出来了,就差提示框中的文字显示了,效果图如下:
四.绘制提示框显示文字: 最后一步就是在指定的提示框中绘制出当前进度值,就可以完成我们自定义View的定制工作了。这里也有一点注意:绘制文字的时候需要用到如下drawText方法:
drawText(@NonNull String text, float x, float y, @NonNull Paint paint)
这里的x和y有的同学会不清楚,导致绘制出来的文字不能达到我们预期的效果。x代表绘制文字时的起始x坐标,y代表绘制文字的基线(baseLine)的位置。清楚了这一点,接下来就很简单了,仅仅是把当前进度值填充到提示框所在的区域,代码如下:
//绘制文字
canvas.drawText(mProgressText, (float) (currProgress - textWidth / 2), mPaddingTop - mTextFontMetrics.top, mTextPaint);
绘制出来的效果就是我们效果图所预期的那样,基本达到了要求,最终效果如下:
到这里我们这边自定义View(进度条)的功能就开发完成了,可能还有很多可以优化于提高的地方,大家可以自己去增加更多动能,也可以给我留言,我们一起去探讨,一起提高。如果对实现过程和原理还有不清楚的同学合一移步GitOS地址查看完整代码:自定义进度条完整代码,还可以在主项目中看具体使用方式。
到这里,本篇博客就要和大家说再见了,欢迎大家提出宝贵意见。