本文实例为大家分享了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);
}
}
以上就是两个效果的源码