Android自定義view實現圓形進度條效果

Android中實現進度條有很多種方式,自定義進度條一般是繼承progressBar或繼承view來實現,本篇中講解的是第二種方式。

先上效果圖:

實現圓形進度條總體來說並不難,還是跟往常一樣繼承view,初始化畫筆,按下面的步驟一步步來就好瞭。對初學者來說動畫效果可能比較陌生,我們可以使用屬性動畫中的valueAnimator來實現動畫效果。

實現步驟:

1、畫出一個灰色的圓環作為背景。

2、畫出上層的圓環覆蓋下方的圓環。

3、加入動畫效果

值得註意的是怎麼設置圓環和文字的位置。

畫出矩形隻需要傳入矩形對角線的坐標即可,如果不加以處理的話畫出來的圓環的邊緣是不完整的,剛開始接觸自定義view的同學們一定要先好好看看Android坐標系相關內容,不然很難理解位置參數為什麼這樣設置。

完整代碼:

package com.example.floatingwindow.widget;
 
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
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 ProgressBarView extends View {
 
    private Paint mPaintBack;
    private Paint mPaint;
    private Paint mPaintText;
    private float process;
    private int strokeWidth = 15;
    private int textSize = 20;
    private long duration = 3000;
    private float startDegree = 0;
    private float endDegree = 360;
    private String text = "完成";
    private String defaultText = "0%";
 
 
    public ProgressBarView(Context context) {
        super(context);
        init();
    }
 
    public ProgressBarView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }
 
    public ProgressBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
 
    private void init() {
        mPaintBack = new Paint();
        mPaintBack.setColor(getResources().getColor(R.color.gray));
        mPaintBack.setStyle(Paint.Style.STROKE);
        mPaintBack.setAntiAlias(true);
        mPaintBack.setStrokeCap(Paint.Cap.ROUND);
        mPaintBack.setStrokeWidth(strokeWidth);
 
        mPaint = new Paint();
        mPaint.setColor(getResources().getColor(R.color.purple_200));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(strokeWidth);
 
        mPaintText = new Paint();
        mPaintText.setAntiAlias(true);
        mPaintText.setStyle(Paint.Style.STROKE);
        mPaintText.setColor(Color.BLACK);
        mPaintBack.setStrokeCap(Paint.Cap.ROUND);
        mPaintText.setTextSize(sp2px(textSize));
    }
 
    public void setStrokeWidth(int width) {
        strokeWidth = width;
    }
 
    public void setTextSize(int textSize) {
        this.textSize = textSize;
    }
 
    public void setDuration(long duration) {
        this.duration = duration;
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //創建圓環矩形
        RectF rectF = new RectF(strokeWidth, strokeWidth, getWidth() - strokeWidth, getHeight() - strokeWidth);
        //畫出灰色進度條作為背景
        canvas.drawArc(rectF, 0, 360, false, mPaintBack);
        //畫進度條
        canvas.drawArc(rectF, 0, process, false, mPaint);
        //計算進度
        int percent = (int) (process / 360 * 100);
        //設置文字在canvas中的位置
        Paint.FontMetrics fm = mPaintText.getFontMetrics();
        int mTxtWidth = (int) mPaintText.measureText(text, 0, defaultText.length());
        int mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent);
        int x = getWidth() / 2 - mTxtWidth / 2;
        int y = getHeight() / 2 + mTxtHeight / 4;
        if (percent < 100) {
            canvas.drawText(percent + "%", x, y, mPaintText);
        } else {
            canvas.drawText(text, x, y, mPaintText);
        }
    }
 
    /**
     * 設置動畫效果
     */
    public void start() {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(startDegree, endDegree);
        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());
    }
}

最後就是動畫效果瞭,使用valueanimator,傳入開始和結束的進度以及執行時間。然後每次進度發生變化時做UI刷新。

xml佈局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
 
    <com.example.floatingwindow.widget.ProgressBarView
        android:id="@+id/progressBar"
        android:layout_width="150dp"
        android:layout_height="150dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">
    </com.example.floatingwindow.widget.ProgressBarView>
 
</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        progressBar.start()
 
        progressBar.setOnClickListener { progressBar.start()}
    }

以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。

推薦閱讀: