android自定义view圆形百分比progressBar√

    技术2025-08-03  19

    前言

    最近工作太不饱和,基本是这个节奏: 8.40到公司,吃个早餐,边吃边玩手机 9.消化一下,打开电脑看会新闻。阿三们又在搞事,反对made in china的产品、禁用几十款app,加上国安法在香港也有游行不服的,哎,我大清太难了 9.30-10.带薪拉屎时间 10.如果当天有茅台抢购,也是要打开京东抢一抢的,虽然东哥暂时还不认我这个兄弟 11.要开始做点事了(反正不是工作的事) 12.-1.30吃饭、午觉 14.把每个群的消息浏览一遍,最活跃的群非同学们的炒股群莫属(每天如此),都在讨论牛市要来了,准备进场一把梭哈。我也想试试,但是奈何钱不够,今天还要交房租。 15.要开始做点事了(一般是逛知乎、写博客) 16.下半场休息时间,这个时候一般抽根黄鹤楼斗斗地主,豆子很快就会输完,好在我有两个号,想骗我充值、看广告,不存在的 17.继续做“事” 18.准点下班(怪不好意思的)

    h5、小程序开发倒是挺忙的,过几天要正儿八经学一下小程序,希望能一起帮前端的兄弟一起迭代之前的小程序。 现在呢,时间总不能浪费,写写blog吧,记录一下,说不定还能帮到需要的朋友。

    进入正题,继续接着上一篇android自定义view实现进度条动画、按钮渐变及录制状态控制,趁热打铁,继续来画个进度条,这个跟上一篇实现方式类似,多了一个百分比数字显示以及完成后的√。

    上效果:

    没什么好解释的,直接上代码:

    import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import androidx.annotation.Nullable; /** * 进度 * * @author ly * date 2020/3/3 11:02 */ public class CircleProgressView extends View { private static final int COMPLETE = 360; private int okSpeed = 3; private Paint paint; //进度圈颜色 private int circleOutsideColor; //进度颜色 private int circleProgressColor; private float progressW; //进度条圆圈半径 private float circleProgressR; private OnProgressListener onProgressListener; private RectF progressRect; private float progress; private String progressText; private Path okPath; private int sX, sY; private int mX; private int eX; private int cX, cY; public CircleProgressView(Context context) { super(context); init(); } public CircleProgressView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public CircleProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { circleOutsideColor = 0xffF2F4F5; circleProgressColor = 0xff6066DD; paint = new Paint(); paint.setAntiAlias(true);//抗锯齿 paint.setDither(true); //设置防抖动 } /** * @param progress 已加载进度/总进度 * @author ly on 2020/3/3 16:41 */ public void setProgress(float progress) { this.progress = progress * COMPLETE; if (this.progress >= COMPLETE) {//表示录制达到最大时长,自动结束 this.progress = COMPLETE; } progressText = (int) (progress * 100) + "%"; invalidate(); } public void reset() { setProgress(0); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (circleProgressR == 0 || okPath == null) { int w = getWidth(); progressW = w * 0.061f; circleProgressR = (w - progressW) / 2f; progressRect = new RectF(0 + progressW / 2, 0 + progressW / 2, w - progressW / 2, w - progressW / 2); int okW = (int) ((getWidth() - progressW) * 0.45); int okH = (int) ((getHeight() - progressW) * 0.32); cX = sX = (getWidth() - okW) / 2; cY = sY = getHeight() / 2; mX = (int) (sX + 0.39 * okW); int mY = (int) (sY + 0.35 * okW); eX = getWidth() - (getWidth() - okW) / 2; int eY = (getHeight() - okH) / 2; okPath = new Path(); okPath.moveTo(sX, sY); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //画进度圈 paint.setStyle(Paint.Style.STROKE); paint.setColor(circleOutsideColor); paint.setStrokeWidth(progressW);//设置画笔粗细 canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, circleProgressR, paint); //画进度就是圆弧 //时钟3点的方向为0度,顺时钟方向为正,-90是圆弧的开始点,即12点位置开始, //sweepAngle扫过的角度,调整该值即可实现进度顺时针加载(0-360) paint.setColor(circleProgressColor); canvas.drawArc(progressRect, -90, progress, false, paint); if (progress < COMPLETE) { if (!TextUtils.isEmpty(progressText)) { // 将坐标原点移到控件中心 int dx = getWidth() / 2; int dy = getHeight() / 2; canvas.translate(dx, dy); // 绘制居中文字 paint.setTextSize(PixelUtil.sp2px(30)); // 文字宽 float textWidth = paint.measureText(progressText); // 文字baseline在y轴方向的位置 float baseLineY = Math.abs(paint.ascent() + paint.descent()) / 2; paint.setStyle(Paint.Style.FILL); // paint.setStrokeWidth(2); canvas.drawText(progressText, -textWidth / 2, baseLineY, paint); } } else {//加载完成,画√ if (cX < eX) {//来个动画凑合用 cX += okSpeed; if (cX < mX) { cY += okSpeed; } else { cY -= okSpeed; } invalidate(); } else { postDelayed(() -> { if (onProgressListener != null) onProgressListener.complete(); }, 1500); } okPath.lineTo(cX, cY); canvas.drawPath(okPath, paint); } } public float getProgress() { return progress / COMPLETE; } public void setOnProgressListener(OnProgressListener onProgressListener) { this.onProgressListener = onProgressListener; } public interface OnProgressListener { void complete(); } }

    还是说一下那个√的动画吧,其实是个规则的图形,都是直线。x轴的坐标是增加的,y坐标先递减再递增。由于要画的√的两条直线可拆分为两个等腰三角形对应的斜边,所以x和y坐标的增减量(okSpeed)都相等。这里有两个优化点: 1、okSpeed的值是固定的,可添加插值器来改变okSpeed的值,从而达到画√的速率 2、这个√的开始和结束的两端是直角,略生硬,可优化成圆角(画笔设置paint.setStrokeCap(Paint.Cap.ROUND)即可) 其他没什么好说的,上dialog的代码:

    import android.content.Context; import android.view.Gravity; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; /** * @author ly * date 2019/8/1 17:36 */ public class CircleProgressDialog extends BaseDialog { private CircleProgressView circleProgressView; private TextView tv_dialog_progress_status; private ImageView iv_dialog_progress_close; private OnProgressCallBack onProgressCallBack; public CircleProgressDialog(@NonNull Context context, OnProgressCallBack onProgressCallBack) { super(context); this.onProgressCallBack = onProgressCallBack; } @Override public int getLayoutResId() { return R.layout.v_dialog_circle_progress; } @Override public void initViews() { if (getWindow() != null) { getWindow().getAttributes().width = ScreenUtil.getScreenWidth() * 4 / 5; getWindow().setGravity(Gravity.CENTER); } setCanceledOnTouchOutside(false); setCancelable(false); circleProgressView = findViewById(R.id.circleProgressView); tv_dialog_progress_status = findViewById(R.id.tv_dialog_progress_status); iv_dialog_progress_close = findViewById(R.id.iv_dialog_progress_close); iv_dialog_progress_close.setOnClickListener(v -> { if (onProgressCallBack != null) onProgressCallBack.intoNextPage(); dismiss(); }); iv_dialog_progress_close.setVisibility(View.GONE); circleProgressView.setOnProgressListener(() -> { if (onProgressCallBack != null) onProgressCallBack.intoNextPage(); dismiss(); }); setOnDismissListener(dialog -> circleProgressView.reset()); } public void setProgress(float progress) { circleProgressView.setProgress(progress); if (progress >= 1) { tv_dialog_progress_status.setText("发布成功"); iv_dialog_progress_close.setVisibility(View.VISIBLE); } else { tv_dialog_progress_status.setText("正在发布中..."); iv_dialog_progress_close.setVisibility(View.GONE); } } public float getProgress() { return circleProgressView.getProgress(); } public interface OnProgressCallBack { void intoNextPage(); } }

    v_dialog_circle_progress:

    <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@drawable/shape_white_r16" android:padding="12dp"> <ImageView android:id="@+id/iv_dialog_progress_close" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:src="@mipmap/ico_close4dialog" /> <com.xxx.CircleProgressView android:id="@+id/circleProgressView" android:layout_width="117dp" android:layout_height="117dp" android:layout_centerHorizontal="true" android:layout_marginTop="34dp" /> <TextView android:id="@+id/tv_dialog_progress_status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/circleProgressView" android:layout_centerHorizontal="true" android:paddingTop="23dp" android:paddingBottom="28dp" android:textColor="#ff727a7c" android:textSize="15sp" tools:text="正在发布中..." /> </RelativeLayout>

    最后给个fake进度,就可以看到效果啦:

    private ScheduledFuture<?> scheduledFuture; private void fakeProgress() { CircleProgressDialog circleProgressDialog = new CircleProgressDialog(this, () -> {}); circleProgressDialog.show(); scheduledFuture = ExecutorServiceManager.get().getExecutorService().scheduleWithFixedDelay(() -> { runOnUiThread(() -> { float progress = circleProgressDialog.getProgress(); if (!circleProgressDialog.isShowing() || videoNoteInfo != null) { if (scheduledFuture != null) scheduledFuture.cancel(true); scheduledFuture = null; return; } progress += 0.006f; circleProgressDialog.setProgress(progress); }); }, 0, 20, TimeUnit.MILLISECONDS); }

    如果对大家有帮助,请点个赞以鼓励我不断前进~

    end

    Processed: 0.126, SQL: 9