Android自定义View实现圆形加载进度条

本文实例为大家分享了Android自定义View实现圆形加载进度条的具体代码,供大家参考,具体内容如下

效果图

话不多说,咱们直接看代码

首先第一种:

1、创建自定义View类

public class MyRelative extends View {     public MyRelative(Context context) {         this(context, null); //手动改成this...     }     public MyRelative(Context context, @Nullable AttributeSet attrs) {         this(context, attrs, 0);//手动改成this...     }     @SuppressLint("ResourceAsColor")     public MyRelative(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {         super(context, attrs, defStyleAttr);     } }

2、自定义属性,(在values文件夹下创建一个XML,取名为atts_circle_view.xml)

<?xml version="1.0" encoding="utf-8"?> <resources>     <declare-styleable name="MyRelative"> //这个name最好和你创建的自定义View类名一样         <!--外圆颜色-->         <attr name="outer_color" format="color" />         <!--内圆颜色-->         <attr name="inner_color" format="color" />         <!--圆形宽度-->         <attr name="border_width" format="dimension" />         <!--字体颜色-->         <attr name="step_text_color" format="color" />         <!--字体大小-->         <attr name="step_text_size" format="dimension" />         <!--步数最大值-->         <attr name="max_step" format="integer"/>         <!--当前步数-->         <attr name="curren_step" format="integer"/>     </declare-styleable> </resources>

3、在第三个构造方法中得到自定义属性

@SuppressLint("ResourceAsColor")     public MyRelative(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {         super(context, attrs, defStyleAttr);         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyRelative);         //圆弧宽度         mBorderWidth = (int) typedArray.getDimension(R.styleable.MyRelative_border_width, 10);         //外圆弧颜色         mOuterColor = typedArray.getColor(R.styleable.MyRelative_outer_color, mOuterColor);         //内圆弧颜色         mInnerColor = typedArray.getInteger(R.styleable.MyRelative_inner_color, mInnerColor);         //字体颜色         mTextColor = typedArray.getColor(R.styleable.MyRelative_step_text_color, mTextColor);         //字体大小         mTextSize = (int) typedArray.getDimensionPixelSize(R.styleable.MyRelative_step_text_size, mTextSize);         //最大步数         mMaxStep = typedArray.getInteger(R.styleable.MyRelative_max_step, 10000);         //当前步数         mCurrentStep = typedArray.getInteger(R.styleable.MyRelative_curren_step, 8000);         typedArray.recycle(); }

4、重写onMeasure方法(测量view大小)

 @Override     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         super.onMeasure(widthMeasureSpec, heightMeasureSpec);         //测量宽高         int width = MeasureSpec.getSize(widthMeasureSpec);         int height = MeasureSpec.getSize(heightMeasureSpec);         //将控件截成正方形         //三目运算符取长度短的一边作为宽高         setMeasuredDimension(width > height ? height : width, width > height ? height : width);     }

5、重写onDraw方法(绘制)

 @Override     protected void onDraw(Canvas canvas) {         super.onDraw(canvas);         //绘制内圆弧         int center = getWidth() / 2;         int r = (getWidth() - mBorderWidth) / 2;         RectF rectF = new RectF(mBorderWidth / 2, mBorderWidth / 2, center + r, center + r);         canvas.drawArc(rectF, 135, 270, false, mInnerPaint);         //绘制外圆弧         if (mMaxStep == 0) {             return;         }         float radio = (float) mCurrentStep / mMaxStep;         canvas.drawArc(rectF, 135, 270 * radio, false, mOuterPaint);         //文字         String mText = mCurrentStep + "";         Rect rect = new Rect();         mTextPaint.getTextBounds(mText, 0, mText.length(), rect);         int dx = getWidth() / 2 - rect.width() / 2;         Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();         int dy = fontMetricsInt.bottom - fontMetricsInt.top;         int baseLine = getHeight() / 2 + dy / 2;         canvas.drawText(mText, dx, getHeight() / 2 + rect.height() / 2, mTextPaint);     }

6、要想效果炫酷怎么能少了动画

写一个方法可以直接在activity中调用

public void setAnimator(int mMaxStep, int mCurrentStep, int duration) {         this.mMaxStep = mMaxStep;         ValueAnimator animator = ObjectAnimator.ofFloat(0, mCurrentStep);         animator.setInterpolator(new DecelerateInterpolator());         animator.setDuration(duration);         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {             @Override             public void onAnimationUpdate(ValueAnimator valueAnimator) {                 float value = (float) animator.getAnimatedValue();                 setmCurrentStep((int) value);             }         });         animator.start();     }

下面附上全部代码

package com.example.customviewdome; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; import android.view.animation.DecelerateInterpolator; import androidx.annotation.Nullable; public class MyRelative extends View {     //圆弧宽度     private int mBorderWidth;     //外圆弧默认颜色     private int mOuterColor = R.color.salmon;     //内圆弧默认颜色     private int mInnerColor = R.color.sandybrown;     //字体默认颜色     private int mTextColor = R.color.salmon;     //字体默认大小     private int mTextSize = 40;     //步数     private int mCurrentStep;     //创建内圆画笔     private Paint mInnerPaint;     //创建外圆画笔     private Paint mOuterPaint;     //创建文字画笔     private Paint mTextPaint;     //最大步数值     private int mMaxStep;     public void setmCurrentStep(int mCurrentStep) {         this.mCurrentStep = mCurrentStep;         invalidate();     }     public void setmMaxStep(int mMaxStep) {         this.mMaxStep = mMaxStep;     }     public MyRelative(Context context) {         this(context, null);     }     public MyRelative(Context context, @Nullable AttributeSet attrs) {         this(context, attrs, 0);     }     //1、分析需求     //2、自定义属性     //3、获得自定义属性     //4、重写onMeasure     //5、绘制     //6、其他(动画等等)     @SuppressLint("ResourceAsColor")     public MyRelative(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {         super(context, attrs, defStyleAttr);         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyRelative);         //圆弧宽度         mBorderWidth = (int) typedArray.getDimension(R.styleable.MyRelative_border_width, 10);         //外圆弧颜色         mOuterColor = typedArray.getColor(R.styleable.MyRelative_outer_color, mOuterColor);         //内圆弧颜色         mInnerColor = typedArray.getInteger(R.styleable.MyRelative_inner_color, mInnerColor);         //字体颜色         mTextColor = typedArray.getColor(R.styleable.MyRelative_step_text_color, mTextColor);         //字体大小         mTextSize = (int) typedArray.getDimensionPixelSize(R.styleable.MyRelative_step_text_size, mTextSize);         //最大步数         mMaxStep = typedArray.getInteger(R.styleable.MyRelative_max_step, 10000);         //当前步数         mCurrentStep = typedArray.getInteger(R.styleable.MyRelative_curren_step, 8000);         typedArray.recycle();         //创建画笔         mInnerPaint = new Paint();         mInnerPaint.setStyle(Paint.Style.STROKE);         mInnerPaint.setAntiAlias(true);         mInnerPaint.setColor(mInnerColor);         mInnerPaint.setStrokeWidth(mBorderWidth);         mInnerPaint.setStrokeCap(Paint.Cap.ROUND);         //创建画笔         mOuterPaint = new Paint();         mOuterPaint.setStyle(Paint.Style.STROKE);         mOuterPaint.setAntiAlias(true);         mOuterPaint.setColor(mOuterColor);         mOuterPaint.setStrokeWidth(mBorderWidth);         mOuterPaint.setStrokeCap(Paint.Cap.ROUND);         //创建文字画笔         mTextPaint = new Paint();         mTextPaint.setAntiAlias(true);         mTextPaint.setColor(mTextColor);         mTextPaint.setTextSize(mTextSize);     }     @Override     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         super.onMeasure(widthMeasureSpec, heightMeasureSpec);         //测量宽高         int width = MeasureSpec.getSize(widthMeasureSpec);         int height = MeasureSpec.getSize(heightMeasureSpec);         //将控件截成正方形         //三目运算符取长度短的一边作为宽高         setMeasuredDimension(width > height ? height : width, width > height ? height : width);     }     @Override     protected void onDraw(Canvas canvas) {         super.onDraw(canvas);         //绘制内圆弧         int center = getWidth() / 2;         int r = (getWidth() - mBorderWidth) / 2;         RectF rectF = new RectF(mBorderWidth / 2, mBorderWidth / 2, center + r, center + r);         canvas.drawArc(rectF, 135, 270, false, mInnerPaint);         //绘制外圆弧         if (mMaxStep == 0) {             return;         }         float radio = (float) mCurrentStep / mMaxStep;         canvas.drawArc(rectF, 135, 270 * radio, false, mOuterPaint);         //文字         String mText = mCurrentStep + "";         Rect rect = new Rect();         mTextPaint.getTextBounds(mText, 0, mText.length(), rect);         int dx = getWidth() / 2 - rect.width() / 2;         Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();         int dy = fontMetricsInt.bottom - fontMetricsInt.top;         int baseLine = getHeight() / 2 + dy / 2;         canvas.drawText(mText, dx, getHeight() / 2 + rect.height() / 2, mTextPaint);     }     public void setAnimator(int mMaxStep, int mCurrentStep, int duration) {         this.mMaxStep = mMaxStep;         ValueAnimator animator = ObjectAnimator.ofFloat(0, mCurrentStep);         animator.setInterpolator(new DecelerateInterpolator());         animator.setDuration(duration);         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {             @Override             public void onAnimationUpdate(ValueAnimator valueAnimator) {                 float value = (float) animator.getAnimatedValue();                 setmCurrentStep((int) value);             }         });         animator.start();     } }

第二种圆形进度条

步骤和以上差不多,下面直接贴出源码

自定义属性

<?xml version="1.0" encoding="utf-8"?> <resources>     <declare-styleable name="CircleLoadingView">         <!--内圆颜色-->         <attr name="in_color" format="color" />         <!--外圆颜色-->         <attr name="out_color" format="color" />         <!--字体颜色-->         <attr name="text_color" format="color" />         <!--字体大小-->         <attr name="text_size" format="dimension" />         <!--圆圈颜色-->         <attr name="dot_color" format="color" />     </declare-styleable> </resources>

源码 

package com.example.customviewdome; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.animation.DecelerateInterpolator; import androidx.annotation.Nullable; public class CircleLoadingView extends View {     private Context mContext;     //内圆颜色     private int mInnerColor;     //外圆颜色     private int mOuterColor;     //圆点颜色     private int mDotColor;     //字体颜色     private int mTextColor;     //字体大小     private int mTextSize;     //创建内圆画笔     private Paint mInnerPaint;     //view的宽度     private int mWidth;     //view的高度     private int mHeight;     //当前进度     private int mProgress = 0;     //创建文字画笔     private Paint mTextPaint;     //创建小圆圈画笔     private Paint mDotPaint;     //小圆圈起点位置     private int mDotProgress;     //仿华为圆形加载框     public CircleLoadingView(Context context) {         this(context, null);     }     public CircleLoadingView(Context context, @Nullable AttributeSet attrs) {         this(context, attrs, 0);     }     //1、自定义属性     //2、测量控件大小     //3、绘制内圆     //4、绘制外圆     @SuppressLint("ResourceAsColor")     public CircleLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {         super(context, attrs, defStyleAttr);         mContext = getContext();         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleLoadingView);         mInnerColor = typedArray.getColor(R.styleable.CircleLoadingView_in_color, R.color.antiquewhite);         mOuterColor = typedArray.getColor(R.styleable.CircleLoadingView_out_color, R.color.aqua);         mTextColor = typedArray.getColor(R.styleable.CircleLoadingView_text_color, R.color.aqua);         mDotColor = typedArray.getColor(R.styleable.CircleLoadingView_dot_color, R.color.blueviolet);         mTextSize = typedArray.getDimensionPixelSize(R.styleable.CircleLoadingView_text_size, 20);         typedArray.recycle();         //创建画笔         mInnerPaint = new Paint();         mInnerPaint.setAntiAlias(true);         mInnerPaint.setColor(mInnerColor);         mInnerPaint.setStrokeWidth(DensityUtil.dip2px(mContext, 3));         mInnerPaint.setStrokeCap(Paint.Cap.ROUND);         mInnerPaint.setStyle(Paint.Style.STROKE);         //创建文字画笔         mTextPaint = new Paint();         mTextPaint.setAntiAlias(true);         mTextPaint.setTextSize(mTextSize);         mTextPaint.setColor(mTextColor);         mTextPaint.setStyle(Paint.Style.STROKE);         //创建小圆圈画笔         mDotPaint = new Paint();         mDotPaint.setAntiAlias(true);         mDotPaint.setStrokeWidth(DensityUtil.dip2px(mContext, 10));         mDotPaint.setStyle(Paint.Style.FILL);         mDotPaint.setColor(mDotColor);     }     @Override     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         super.onMeasure(widthMeasureSpec, heightMeasureSpec);         int widthMode = MeasureSpec.getMode(widthMeasureSpec);         int heightMode = MeasureSpec.getMode(heightMeasureSpec);         int widthSize = MeasureSpec.getSize(widthMeasureSpec);         int heightSize = MeasureSpec.getSize(heightMeasureSpec);         //获取宽度         if (widthMode == MeasureSpec.EXACTLY) {             //当宽度为精准值或match_parent时直接使用             mWidth = widthSize;         } else {             //当宽度为wrap_content设置控件大小为120dp             mWidth = DensityUtil.dip2px(mContext, 220);         }         //获取高度         if (heightMode == MeasureSpec.EXACTLY) {             mHeight = heightSize;         } else {             mHeight = DensityUtil.dip2px(mContext, 220);         }         setMeasuredDimension(mWidth > mHeight ? mHeight : mWidth, mWidth > mHeight ? mHeight : mWidth);     }     @Override     protected void onDraw(Canvas canvas) {         super.onDraw(canvas);         //绘制圆形         canvas.save();         for (int i = 0; i < 100; i++) {             if (mProgress > i) {                 mInnerPaint.setColor(mInnerColor);             } else {                 mInnerPaint.setColor(mOuterColor);             }             canvas.drawLine(mWidth / 2, 0, mWidth / 2, DensityUtil.dip2px(mContext, 10), mInnerPaint);             canvas.rotate(3.6f, mWidth / 2, mHeight / 2);         }         canvas.restore();         //绘制文字         String progreStr = mProgress + "";         Rect rect = new Rect();         mTextPaint.getTextBounds(progreStr, 0, progreStr.length(), rect);         int dx = getWidth() / 2 - rect.width() / 2;         canvas.drawText(progreStr, dx, getHeight() / 2 + rect.height() / 2, mTextPaint);         //绘制小圆圈         canvas.save();         canvas.rotate(mDotProgress * 3.6f, mWidth / 2, mHeight / 2);         canvas.drawCircle(mWidth / 2 - (mInnerPaint.getStrokeWidth() * 2), DensityUtil.dip2px(mContext, 10) + DensityUtil.dip2px(mContext, 8), DensityUtil.dip2px(mContext, 3), mDotPaint);         canvas.restore();     }     public void setProgress(int progress) {         mProgress = progress;         mDotProgress = progress;         invalidate();     }     public void setAnimation(int progress, int time) {         ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, progress);         valueAnimator.setDuration(time);         valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {             @Override             public void onAnimationUpdate(ValueAnimator valueAnimator) {                 float value = (float) valueAnimator.getAnimatedValue();                 setProgress((int) value);             }         });         valueAnimator.start();     } }

在xml文件中引用

<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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"     android:background="@color/black"     android:gravity="center"     android:orientation="vertical"     tools:context=".MainActivity">     <com.example.customviewdome.MyRelative         android:id="@+id/my_view"         android:layout_width="500dp"         android:layout_height="200dp"         app:border_width="10dp"         app:inner_color="@color/gray"         app:outer_color="@color/indianred"         app:step_text_color="@color/indianred"         app:step_text_size="30dp" />     <com.example.customviewdome.CircleLoadingView         android:id="@+id/circleloading"         android:layout_width="200dp"         android:layout_height="200dp"         android:layout_marginTop="15dp"         app:text_size="30dp"         app:dot_color="@color/red"         app:text_color="@color/indianred"         app:in_color="@color/indianred"         app:out_color="@color/gray" /> </LinearLayout>

在activity中实例化

package com.example.customviewdome; import androidx.appcompat.app.AppCompatActivity; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.os.Bundle; import android.view.View; import android.view.animation.Animation; import android.view.animation.DecelerateInterpolator; public class MainActivity extends AppCompatActivity {     private MyRelative my_view;     private CircleLoadingView circleloading;     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         circleloading = findViewById(R.id.circleloading);         circleloading.setAnimation(80, 5000);         my_view = findViewById(R.id.my_view);         my_view.setAnimator(100,80,5000);     } }

本文中使用到的DensityUtil类是为了将dp转换为px来使用,以便适配不同的屏幕显示效果

public class DensityUtil {     public static int dip2px(Context context, float dpValue) {         final float scale = context.getResources().getDisplayMetrics().density;         return (int) (dpValue * scale + 0.5f);     } }

以上就是两个效果的源码

推荐阅读