Android自定义View实现圆形加载进度条
作者:是只废狗了
这篇文章主要为大家详细介绍了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); } }
以上就是两个效果的源码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。