赞
踩
好久不写博客了,今天来总结一下自定义ProgressBar的实现。上周做一个游戏资源的在线更新功能,设计给的加载进度条设计图,是无法使用Android原生的ProgressBar来实现的。在百度和GitHub上搜了搜相关的资源,都不符合我的要求。于是,我只能自己去写。先给一下最终的效果:
上面就是我实现的ProgressBar的效果,与业务结合起来,分为几个阶段:
(1)检查配置阶段:检查本地现有的资源和服务器的配置,确定是否存在新的资源需要下载。
(2)下载阶段:如果存在新的资源,开始下载新的资源。
(3)解压缩阶段:下载完成新资源后,开始解压缩资源包。
刚工作的时候,做的项目或者功能都有很长的开发周期。我们会在开发前充分的去做需求分析和设计,讨论技术难点和可能遇到的一些坑。而且,由于开发周期长,允许我们在开发的过程中犯一些错误。不过,现在的工作,根本不会给我推倒重来的时间。所以,逐渐的,我养成了习惯,那就是在写代码之前先想一想如何去实现:
(1)我需要画一个进度条,这个进度条至少包括已达到的进度和未达到的进度
(2)这个进度条需要和更新进度联动,需要包括一个展示更新阶段的文本
(3)这个进度条需要一个用于告知用户百分比的文本
(4)这个进度条需要一个小滑块,来使我们的进度条更美观
我们可以把这个进度条看做两个主要部分:已经达到的进度和未达到的进度,还有进度文本和百分比展示,最后是一个滑块。而且,这个进度条是个圆角的矩形(只只不过长度远远大于高度)。所以,我们的思路是,画两个圆角矩形。那么,我们该如何确定我们进度条的各个元素从什么地方开始画,画到哪个位置结束呢?这就需要我们简单的计算一下。
- mUnreachedRectF.left = getPaddingLeft();
- mUnreachedRectF.top = (getHeight() - mBarHeight) / 2.0f;
- mUnreachedRectF.right = getWidth() - getPaddingRight();
- mUnreachedRectF.bottom = (getHeight() + mBarHeight) / 2.0f;
(1)left是矩形的最左边,就是我们画布的最左边,其实就是0。但是为了美观,我们不可能从画布的最左边开始画的,我们可能会设置一个padding,所以,我们使用的初始x坐标为:paddingLeft
(2)top是我们矩形的最上面,我们取的坐标是(画布的高度-进度条的高度)/2。
(3)right是矩形的最右边,取的坐标是:画布的宽度减去右边的padding。
(4)bottom是矩形的底部,这里可以对照top的计算,其实目的就是要让我们的矩形处于画布的中间。因此,以画布高度的中间为参考点,top就是画布高度的一半减去进度条高度的一半,而bottom则是画布高度的一半加上进度条高度的一半:
- mReachedRectF.left = getPaddingLeft() + dp2px(2);
- mReachedRectF.top = (getHeight() - mBarHeight) / 2.0f + dp2px(2);
- mReachedRectF.right = (getWidth() - getPaddingLeft() - getPaddingRight()) /
- (getMax() * 1.0f) * getProgress() + getPaddingLeft();
- mReachedRectF.bottom = (getHeight() + mBarHeight) / 2.0f - dp2px(2);
(1)left:比起背景,我们更往右了2dp,因为我们的已达到进度会比背景小一些,以实现嵌套的效果
(2)top:也是一样,比背景更往下2dp
(3)right:这个虽然很长,其实就是进度条背景的长度去乘上当前的进度(例如当前的进度是80%,那其实就是背景的长度*80%),后面加上paddingleft,其实,还应该加上2dp,不过,我们可以忽略不计。
(4)bottom:比背景往上2dp
(1)比起画矩形我们需要告诉canvas我们的上下左右位置,画文本我们只需要告诉canvas最左边和最上边,下面是百分比的显示位置:
canvas.drawText(mCurrentDrawText, mUnreachedRectF.right - dp2px(36), getHeight() / 2 + 2 * mBarHeight, mPaint);
最左边,就是矩形的最右边减去一个值,这个值就是我们百分比这几个数字的大体宽度。最上边,就是画布高度的一半再加上一块高度,在这里我取了两倍的进度条高度。这样就可以让进度条百分比最右侧与进度条终点大致对齐,让其位于进度条下方。
(2)下面是显示更新阶段的文本的位置计算:
canvas.drawText(mHint, mReachedRectF.left, getHeight() / 2 + 2 * mBarHeight, mPaint);
最左边的位置,就是矩形的最左边,最上边,跟百分比的最上面一样,不再赘述。
最后,是计算滑块的位置,其实它的位置也比较好计算。然后我们调用canvas.drawBitmap即可:
- mSliderLeft = (getWidth() - getPaddingLeft() - getPaddingRight()) / (getMax() * 1.0f) * getProgress() + getPaddingLeft() - mSliderWidth / 2;
- mSliderTop = (getHeight() - mSliderHeight) / 2.0f;
- canvas.drawBitmap(mSlider, mSliderLeft, mSliderTop, mPaint);
left:其实就是已达成进度的最右边减去滑块宽度的一半,也就是让滑块的中间与已达成进度大致重合。
top:与进度条的top一样的计算。为了美观,我们会让滑块的位置稍微超出进度条顶部一点点。
(1)绘制进度条背景(未达成进度):
- private void drawUnReachedRectF(Canvas canvas) {
- mPaint.setShader(null);
- mPaint.setColor(mUnreachedBarColor);
- canvas.drawRoundRect(mUnreachedRectF, mRadius, mRadius, mPaint);
- }
(2)绘制进度条已达到部分:
- private void drawReachedRectF(Canvas canvas) {
- mLinearGradient = new LinearGradient(0, 0, mReachedRectF.right,
- 0,
- startColor,
- endColor,
- Shader.TileMode.CLAMP);
- mPaint.setShader(mLinearGradient);
- canvas.drawRoundRect(mReachedRectF, mRadius, mRadius, mPaint);
- }
(3)绘制进度条进度:
- private void drawProgress(Canvas canvas) {
- mPaint.setShader(null);
- mPaint.setColor(mTextColor);
- mPaint.setTextSize(dp2px(20f));
- String mCurrentDrawText = new DecimalFormat("#").format(getProgress() * 100 / getMax());
- mCurrentDrawText = mCurrentDrawText + "%";
- canvas.drawText(mCurrentDrawText, mUnreachedRectF.right - dp2px(36), getHeight() / 2 + 2 * mBarHeight, mPaint);
- }
(4)绘制进度条文本:
- private void drawHint(Canvas canvas) {
- mPaint.setShader(null);
- mPaint.setColor(mTextColor);
- mPaint.setTextSize(dp2px(12f));
- if (mProgress <= 20) {
- mHint = "正在检查配置...";
- } else if (mProgress > 20 && mProgress <= 60) {
- mHint = "正在下载资源...";
- } else {
- mHint = "正在解压,本过程不消耗流量...";
- }
- canvas.drawText(mHint, mReachedRectF.left, getHeight() / 2 + 2 * mBarHeight, mPaint);
- }
(5)绘制滑块:
- private void drawSlider(Canvas canvas) {
- mPaint.setShader(null);
- mPaint.setColor(Color.WHITE);
- canvas.drawBitmap(mSlider, mSliderLeft, mSliderTop, mPaint);
- }
使用BitmapFactory获取bitmap,然后创建指定大小的滑块:
- mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.idiom_load_cloud);
- mSlider = Bitmap.createScaledBitmap(mBitmap, (int) dp2px(26), (int) dp2px(16), true);
为了防止内存泄漏,需要在合适的时机销毁bitmap:
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mBitmap != null && !mBitmap.isRecycled()) {
- mBitmap.recycle();
- mBitmap = null;
- }
- if (mSlider != null && !mSlider.isRecycled()) {
- mSlider.recycle();
- mSlider = null;
- }
- }
以上就是对自定义进度条的实现。掌握关键的方法后,我们可以根据自己的需求,轻松的自定义进度条。下一篇博客,将会介绍几种让进度条动起来的方式。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。