目录
前言
之前用华为Android系统的时候总是会想到这种虚线进度条是怎么实现的,因为直接用canvas的drawArc方法画出来的是实线,所以最近在网上搜了很多进度条相关的文章,也了解到了其中的原理,下面分享给大家。下面这两篇文章是我之前写的Android进度条相关的文章,有兴趣的朋友们可以看看:
下面开始讲解虚线进度条的实现方法,首先看一张图:
实现步骤
大家可以先想想这种进度条是怎么实现的?网上有各种各样的方法,也有人用path的lineTo()方法实现了类似的效果。但是我个人觉得canvas的drawArc方法也是个不错的选择,适合自己的才是最好的。
canvas.drawArc(rectF, 0, process, false, mPaint);
阅读了大量文章,最后发现改变paint的样式是最简单好用的方法。给paint设置一个effect就好了。
1.用DashPathEffect给paint加上虚线效果
DashPathEffect dashPathEffect = new DashPathEffect(new float[]{8, 6}, 0); mPaintBack.setPathEffect(dashPathEffect);
build一下项目,看到的结果是这样的:
能实现这个效果就大功告成了,如果看过我前面两篇文章的朋友们就知道,后面的步骤就简单了,加个进度条和动画效果就可以了。
private void drawBack(Canvas canvas) { RectF rectF = new RectF(strokeWidth, strokeWidth, getWidth() - strokeWidth, getHeight() - strokeWidth); PathEffect effects = new DashPathEffect(new float[]{8, 6}, 0); mPaintBack.setPathEffect(effects); canvas.drawArc(rectF, 0, 360, false, mPaintBack); }
2.画出进度条
画进度条和画背景完全一样,只是画笔颜色和小点个数不一样。
代码如下:
private void drawProgress(Canvas canvas) { RectF rectF = new RectF(strokeWidth, strokeWidth, getWidth() - strokeWidth, getHeight() - strokeWidth); PathEffect effects = new DashPathEffect(new float[]{8, 6}, 0); mPaint.setPathEffect(effects); canvas.drawArc(rectF, 0, process, false, mPaint); }
3.绘制文字
接下来是绘制文字,实现文字居中的效果。思路是计算出圆心,调用canvas.drawText的方法就能在canvas上面绘制文字了。这里不需要更新进度文字,所以就更省事了。
EMUI 下面的10.0.0也是一样的方法,只是给Y坐标加一下55,往下移一点就好。
//绘制文字 private void drawText(Canvas canvas) { int mTxtWidth = getTextWidth(); int mTxtHeight = getTextHeight(); int x = getWidth() / 2 - mTxtWidth / 2; int y = getHeight() / 2 + mTxtHeight / 4; canvas.drawText(getResources().getString(R.string.defaultTextEmui), x, y, mPaintText); } //绘制下方文字 private void drawTextBlow(Canvas canvas) { int mTxtWidth = getTextWidthBlow(); int mTxtHeight = getTextHeight(); int x = getWidth() / 2 - mTxtWidth / 2; int y = getHeight() / 2 + mTxtHeight / 4 + 55; canvas.drawText(getResources().getString(R.string.defaultTextBelow), x, y, mPaintTextLevel); }
4.加入动画效果
/** * 设置动画效果 */ public void start() { ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 360); valueAnimator.setDuration(duration); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.addUpdateListener(animation -> { process = (int) 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.DashPathEffect; import android.graphics.Paint; import android.graphics.PathEffect; import android.graphics.RectF; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.view.animation.LinearInterpolator; import androidx.annotation.Nullable; import com.example.floatingwindow.R; public class DottedLineProgressBar extends View { private Paint mPaint; private Paint mPaintBack; private Paint mPaintText; private Paint mPaintTextLevel; private int strokeWidth = 30; private int textSize = 22; private int textSizeBlow = 15; private long duration = 3500; private int process; public DottedLineProgressBar(Context context) { super(context); init(); } public DottedLineProgressBar(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public DottedLineProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public void setStrokeWidth(int width) { strokeWidth = width; } public void setTextSize(int textSize) { this.textSize = textSize; } public void setDuration(long duration) { this.duration = duration; } public void setTextSizeBlow(int textSizeBlow) { this.textSizeBlow = textSizeBlow; } //初始化画笔 private void init() { mPaintBack = new Paint();//圆角矩形 mPaintBack.setColor(getResources().getColor(R.color.gray));//圆角矩形颜色 mPaintBack.setAntiAlias(true);// 抗锯齿效果 mPaintBack.setStyle(Paint.Style.STROKE);//设置画笔样式 mPaintBack.setStrokeWidth(strokeWidth);//设置画笔宽度 mPaint = new Paint(); mPaint.setColor(getResources().getColor(R.color.blue)); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(strokeWidth); mPaintText = new Paint(); mPaintText.setAntiAlias(true); mPaintText.setStyle(Paint.Style.FILL); mPaintText.setColor(getResources().getColor(R.color.blue)); mPaintText.setTextSize(sp2px(textSize)); mPaintTextLevel = new Paint(); mPaintTextLevel.setAntiAlias(true); mPaintTextLevel.setStyle(Paint.Style.FILL); mPaintTextLevel.setColor(getResources().getColor(R.color.gray)); mPaintTextLevel.setTextSize(sp2px(textSizeBlow)); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawBack(canvas); drawProgress(canvas); drawText(canvas); drawTextBlow(canvas); } private void drawBack(Canvas canvas) { RectF rectF = new RectF(strokeWidth, strokeWidth, getWidth() - strokeWidth, getHeight() - strokeWidth); PathEffect effects = new DashPathEffect(new float[]{8, 6}, 0); mPaintBack.setPathEffect(effects); canvas.drawArc(rectF, 0, 360, false, mPaintBack); } private void drawProgress(Canvas canvas) { RectF rectF = new RectF(strokeWidth, strokeWidth, getWidth() - strokeWidth, getHeight() - strokeWidth); PathEffect effects = new DashPathEffect(new float[]{8, 6}, 0); mPaint.setPathEffect(effects); canvas.drawArc(rectF, 0, process, false, mPaint); } //绘制文字 private void drawText(Canvas canvas) { int mTxtWidth = getTextWidth(); int mTxtHeight = getTextHeight(); int x = getWidth() / 2 - mTxtWidth / 2; int y = getHeight() / 2 + mTxtHeight / 4; canvas.drawText(getResources().getString(R.string.defaultTextEmui), x, y, mPaintText); } //绘制下方文字 private void drawTextBlow(Canvas canvas) { int mTxtWidth = getTextWidthBlow(); int mTxtHeight = getTextHeight(); int x = getWidth() / 2 - mTxtWidth / 2; int y = getHeight() / 2 + mTxtHeight / 4 + 55; canvas.drawText(getResources().getString(R.string.defaultTextBelow), x, y, mPaintTextLevel); } private int getTextWidth() { String text = getResources().getString(R.string.defaultTextEmui); return (int) mPaintText.measureText(text, 0, text.length()); } private int getTextWidthBlow() { String text = getResources().getString(R.string.defaultTextBelow); return (int) mPaintTextLevel.measureText(text, 0, text.length()); } private int getTextHeight() { Paint.FontMetrics fm = mPaintText.getFontMetrics(); return (int) Math.ceil(fm.descent - fm.ascent); } private int sp2px(int sp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics()); } /** * 设置动画效果 */ public void start() { ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 360); valueAnimator.setDuration(duration); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.addUpdateListener(animation -> { process = (int) animation.getAnimatedValue(); invalidate(); }); valueAnimator.start(); } }
kotlin调用:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) dottedLineProgressBar.post { dottedLineProgressBar.start() } } }
XML:
<com.example.floatingwindow.widget.DottedLineProgressBar android:id="@+id/dottedLineProgressBar" android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center"> </com.example.floatingwindow.widget.DottedLineProgressBar>
以上就是完整的实现过程了。
项目源码:https://gitee.com/tu_erhongjiang/android-progress-bar