Android实现绚丽的自定义进度条
目录
- 前言
- 效果图
- 实现步骤
- 1.绘制背景圆形矩形
- 2.绘制进度
- 3.绘制文字
- 4.加入动画
- 完整代码
前言 进度条是在Android项目中很常用的组件之一,如果想要理解它是怎么实现的,首先还需要了解自定义view和Android坐标系相关的知识,下面我来详细地介绍一下自定义进度条的实现过程。
本项目源码:https://gitee.com/tu_erhongjiang/android-progress-bar
【Android实现绚丽的自定义进度条】
效果图
文章图片
实现步骤
1.绘制背景圆形矩形
文章图片
首先要画出一个圆形矩形,RectF里面传递的是矩形左上角和右下角的xy坐标,这是用来确定矩形的位置和大小,然后在矩形内部画出一个圆形矩形。
核心代码:canvas.drawRoundRect()
private void drawBackground(Canvas canvas){//圆角矩形RectF rectF = new RectF(padding, padding, mWidth - padding, mHeight - padding); canvas.drawRoundRect(rectF, round, round, mPaintRoundRect); }
2.绘制进度
文章图片
其实里面的进度条也是圆形矩形,只不过进度条的画笔是实心的。内部进度条矩形的大小需要略小于外面的矩形,这样就可以实现上面的这种效果。如果进度条矩形大于或等于背景矩形大小的话那就完全压住背景中的边框,显示出来的只是一个没有边框的进度条,所以这里需要减掉strokeWidth。
private void drawProgress(Canvas canvas){if (process!=0){RectF rectProgress = new RectF(padding + strokeWidth, padding + strokeWidth, process, mHeight - padding - strokeWidth); //内部进度条canvas.drawRoundRect(rectProgress, round, round, mPaint); }}
3.绘制文字
文章图片
下面来看看怎么居中文字:
getWidth() / 2 得到的结果是中间位置的x坐标,但是从这里开始绘制文字的话不能实现居中的效果。所以还需要计算出文字的长度,然后把文字整体左移。mTxtWidth / 2 是文字的中心位置,也就是说文字的中心位置移到矩形的中心位置就可以实现居中的效果。
private void updateText(Canvas canvas) {String finishedText = getResources().getString(R.string.finished); String defaultText = getResources().getString(R.string.defaultText); int percent = (int) (process / (mWidth - padding - strokeWidth) * 100); Paint.FontMetrics fm = mPaintText.getFontMetrics(); int mTxtWidth = (int) mPaintText.measureText(finishedText, 0, defaultText.length()); int mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent); int x = getWidth() / 2 - mTxtWidth / 2; //文字在画布中的x坐标int y = getHeight() / 2 + mTxtHeight / 4; //文字在画布中的y坐标if (percent < 100) {canvas.drawText(percent + "%", x, y, mPaintText); } else {canvas.drawText(finishedText, x, y, mPaintText); }}
4.加入动画
最后就是加入动画效果了,让进度条动起来。我这里用到的是属性动画中的valueAnimator。这种动画不能直接修改view,它是类似于timer,需要我们传递一个数值范围和执行时间。比如说3秒内从1加到100。然后在接口回调时拿到当前的进度,执行view的invalidate()方法,刷新UI。
//属性动画public void start() {ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mWidth - padding - strokeWidth); valueAnimator.setDuration(duration); valueAnimator.setInterpolator(new DecelerateInterpolator()); valueAnimator.addUpdateListener(animation -> {process = (float) animation.getAnimatedValue(); invalidate(); }); valueAnimator.start(); }
完整代码
package com.example.floatingwindow.widget; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.view.animation.DecelerateInterpolator; import androidx.annotation.Nullable; import com.example.floatingwindow.R; public class HorizontalProgressView extends View { private Paint mPaint; private Paint mPaintRoundRect; private Paint mPaintText; private int mWidth; private int mHeight; private int padding = 5; private int strokeWidth = 8; private int textSize = 15; private long duration = 3500; private int round; private float process; public HorizontalProgressView(Context context) {super(context); init(); } public HorizontalProgressView(Context context, @Nullable AttributeSet attrs) {super(context, attrs); init(); } public HorizontalProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr); init(); } //初始化画笔private void init(){mPaintRoundRect = new Paint(); //圆角矩形mPaintRoundRect.setColor(getResources().getColor(R.color.back)); //圆角矩形颜色mPaintRoundRect.setAntiAlias(true); // 抗锯齿效果mPaintRoundRect.setStyle(Paint.Style.STROKE); //设置画笔样式mPaintRoundRect.setStrokeWidth(strokeWidth); //设置画笔宽度 mPaint = new Paint(); mPaint.setColor(getResources().getColor(R.color.inner)); mPaint.setStyle(Paint.Style.FILL_AND_STROKE); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(strokeWidth); mPaintText = new Paint(); mPaintText.setAntiAlias(true); mPaintText.setStyle(Paint.Style.FILL); mPaintText.setColor(getResources().getColor(R.color.back)); mPaintText.setTextSize(sp2px(textSize)); } public void setPadding(int padding) {this.padding = padding; } public void setStrokeWidth(int strokeWidth) {this.strokeWidth = strokeWidth; } public void setTextSize(int textSize) {this.textSize = textSize; } public void setDuration(long duration) {this.duration = duration; } @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); //MeasureSpec.EXACTLY,精确尺寸if (widthSpecMode == MeasureSpec.EXACTLY || widthSpecMode == MeasureSpec.AT_MOST) {mWidth = widthSpecSize; } else {mWidth = 0; }//MeasureSpec.AT_MOST,最大尺寸,只要不超过父控件允许的最大尺寸即可,MeasureSpec.UNSPECIFIED未指定尺寸if (heightSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.UNSPECIFIED) {mHeight = defaultHeight(); } else {mHeight = heightSpecSize; }//设置控件实际大小round = mHeight / 2; //圆角半径setMeasuredDimension(mWidth, mHeight); } @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas); drawBackground(canvas); //绘制背景矩形drawProgress(canvas); //绘制进度updateText(canvas); //处理文字} private void drawBackground(Canvas canvas){RectF rectF = new RectF(padding, padding, mWidth - padding, mHeight - padding); //圆角矩形canvas.drawRoundRect(rectF, round, round, mPaintRoundRect); } private void drawProgress(Canvas canvas){if (process!=0){RectF rectProgress = new RectF(padding + strokeWidth, padding + strokeWidth, process, mHeight - padding - strokeWidth); //内部进度条canvas.drawRoundRect(rectProgress, round, round, mPaint); }} private void updateText(Canvas canvas) {String finishedText = getResources().getString(R.string.finished); String defaultText = getResources().getString(R.string.defaultText); int percent = (int) (process / (mWidth - padding - strokeWidth) * 100); Paint.FontMetrics fm = mPaintText.getFontMetrics(); int mTxtWidth = (int) mPaintText.measureText(finishedText, 0, defaultText.length()); int mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent); int x = getWidth() / 2 - mTxtWidth / 2; //文字在画布中的x坐标int y = getHeight() / 2 + mTxtHeight / 4; //文字在画布中的y坐标if (percent < 100) {canvas.drawText(percent + "%", x, y, mPaintText); } else {canvas.drawText(finishedText, x, y, mPaintText); }} //属性动画public void start() {ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mWidth - padding - strokeWidth); valueAnimator.setDuration(duration); valueAnimator.setInterpolator(new DecelerateInterpolator()); valueAnimator.addUpdateListener(animation -> {process = (float) animation.getAnimatedValue(); invalidate(); }); valueAnimator.start(); } private int sp2px(int sp) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,getResources().getDisplayMetrics()); } //进度条默认高度,未设置高度时使用private int defaultHeight() {float scale = getContext().getResources().getDisplayMetrics().density; return (int) (20 * scale + 0.5f * (20 >= 0 ? 1 : -1)); } }
以上就是横向进度条的实现步骤,整体来说还是比较简单的,如果你对Android坐标系和canvas比较熟悉的话自定义view实现起来还是很容易的。
到此这篇关于Android实现绚丽的自定义进度条的文章就介绍到这了,更多相关Android自定义进度条内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
推荐阅读
- flutter实现底部抽屉效果
- 基于Python实现将列表数据生成折线图
- Vue实现验证码登录的方法实例
- javascript|javascript 实现富文本框选中对齐
- flutter实现底部导航栏
- Android实现环信修改头像和昵称
- JAVA实现发送短信
- Android Studio中的线性布局(LinearLayout)
- Android Studio 的帧布局(Framelayout)
- 程序员|Android 这座山,我一定要爬到顶