————自定义PercentProgressBar继承自View,五个自定义属性progressWidth(进度条宽度),progressBackColor(进度条背景色),progressFrontColor(进度条前景色),percentTextSize(百分比文字大小),percentTextColor(百分比文字颜色),可在布局文件中直接使用。
如需下载源码,请访问
https://github.com/fengchuanfang/PercentProgressBarDemo2
文章原创,转载请注明出处:
自定义Material Design风格带滚动百分比的圆形进度条
运行效果如下:
引入步骤
访问git地址下载源码,拷贝com.feng.edward.percentprogressbardemo2.view包下的PercentProgressBar类和attrs.xml中的PercentProgressBar的五项属性
或者新建java类PercentProgressBar,直接复制以下代码,再将attrs.xml中的代码放入项目对应attrs文件中。便可以在布局文件中直接使用。
package com.feng.edward.percentprogressbardemo2.view;
import com.feng.edward.percentprogressbardemo2.R;
import com.feng.edward.percentprogressbardemo2.util.DimensionUtils;
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.graphics.Typeface;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
/**
* 功能描述:带滚动百分比的圆形进度条
*
* @author (作者) edward(冯丰枫)
* @link http://www.jianshu.com/u/f7176d6d53d2
* 创建时间: 2018/4/16 0016
*/
public class PercentProgressBar extends View {
private Paint
progressBackPaint, //进度条背景画笔
progressFrontPaint,//进度条前景画笔
percentTextPaint; //百分比文字画笔
private RectF
progressRectF;//进度条圆弧所在的矩形
private float
progressPaintWidth, //进度条画笔的宽度
radiusArc,//圆形进度条半径
textHeight;//“100%”文字的高度
private int percentProgress;//百分比进度(0 ~ 100)
public PercentProgressBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PercentProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.PercentProgressBar, defStyleAttr, 0);
//初始化进度条画笔的宽度
progressPaintWidth = attributes.getDimension(R.styleable.PercentProgressBar_progressWidth, DimensionUtils.dip2px(context, 10));
//初始化百分比文字的大小
float percentTextPaintSize = attributes.getDimensionPixelSize(R.styleable.PercentProgressBar_percentTextSize, DimensionUtils.sp2px(context, 10));
int progressBackColor = attributes.getColor(R.styleable.PercentProgressBar_progressBackColor, 0xffaaaaaa);
int progressFrontColor = attributes.getColor(R.styleable.PercentProgressBar_progressFrontColor, 0xffFF4081);
int percentTextColor = attributes.getColor(R.styleable.PercentProgressBar_percentTextColor, 0xffff0077);
attributes.recycle();
//初始化进度条背景画笔
progressBackPaint = new Paint();
progressBackPaint.setColor(progressBackColor);
progressBackPaint.setStrokeWidth(progressPaintWidth);
progressBackPaint.setAntiAlias(true);
progressBackPaint.setStyle(Paint.Style.STROKE);
//初始化进度条前景画笔
progressFrontPaint = new Paint();
progressFrontPaint.setColor(progressFrontColor);
progressFrontPaint.setStrokeWidth(progressPaintWidth);
progressFrontPaint.setAntiAlias(true);
progressFrontPaint.setStyle(Paint.Style.STROKE);
//初始化百分比文字画笔
percentTextPaint = new Paint();
percentTextPaint.setColor(percentTextColor);
percentTextPaint.setTextSize(percentTextPaintSize);// 设置文字画笔的尺寸(px)
percentTextPaint.setAntiAlias(true);
percentTextPaint.setStyle(Paint.Style.STROKE);
percentTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();// 获取控件的layout_width
int height = getMeasuredHeight(); // 获取控件的layout_height
//获取内切圆圆心坐标
int centerX = width / 2;
int centerY = height / 2;
int radius = Math.min(width, height) / 2;//获取控件内切圆的半径
float textWidth = percentTextPaint.measureText("100%");
//比较进度条的宽度和百分比文字的高度,去两者中较大者,用以计算进度条的半径,保证精度条和百分比文字互为中心
radiusArc = radius - (progressPaintWidth > textWidth ? progressPaintWidth / 2 : textWidth / 2);
Rect rect = new Rect();
percentTextPaint.getTextBounds("100%", 0, "100%".length(), rect);//获取最大百分比文字的高度
textHeight = rect.height();
//初始化进度条圆弧所在的矩形
progressRectF = new RectF();
progressRectF.left = centerX - radiusArc;
progressRectF.top = centerY - radiusArc;
progressRectF.right = centerX + radiusArc;
progressRectF.bottom = centerY + radiusArc;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制进度框的背景
canvas.drawArc(progressRectF, 0, 360, false, progressBackPaint);
//绘制进度框的前景(从圆形最高点中间,顺时针绘制)
progressFrontPaint.setStyle(Paint.Style.STROKE);
canvas.drawArc(progressRectF, -90, percentProgress / 100.0f * 360, false, progressFrontPaint);
//百分比文字
String text = percentProgress + "%";
double cos = Math.cos(Math.toRadians(percentProgress / 100.0f * 360)) * radiusArc;
double sin = Math.sin(Math.toRadians(percentProgress / 100.0f * 360)) * radiusArc;
//获取百分比文字的宽度
float percentTextWidth = percentTextPaint.measureText(text) / 2;
//画百分比文字的实心背景圆
progressFrontPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawCircle(progressRectF.centerX() + (float) sin, progressRectF.centerY() - (float) cos, percentTextWidth, progressFrontPaint);
//画百分比文字
canvas.drawText(text, progressRectF.centerX() - percentTextWidth + (float) sin, progressRectF.centerY() + textHeight / 2 - (float) cos, percentTextPaint);
}
/**
* 设置当前百分比进度
*/
public void setPercentProgress(int percentProgress) {
this.percentProgress = percentProgress;
invalidate();
}
}
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="PercentProgressBar">
<attr name="progressWidth" format="dimension"/><!--进度条宽度-->
<attr name="progressBackColor" format="color"/><!--进度条背景色-->
<attr name="progressFrontColor" format="color"/><!--进度条前景色-->
<attr name="percentTextSize" format="dimension"/><!--百分比文字的大小-->
<attr name="percentTextColor" format="color"/><!--百分比文字的颜色-->
</declare-styleable>
</resources>
图示:
在布局文件dialog_layout.xml中使用如下:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.feng.edward.percentprogressbardemo2.view.PercentProgressBar
android:id="@+id/circle_percent_view"
android:layout_width="120dp"
android:layout_height="150dp"
android:focusable="true"
android:clickable="true"
android:layout_gravity="center"
app:percentTextSize="10sp"
app:percentTextColor="#ffffff"
app:progressFrontColor="@color/colorPrimary"
app:progressWidth="2dp"
/>
<com.feng.edward.percentprogressbardemo2.view.CircleImageView
android:id="@+id/circle_image_view"
android:layout_width="70dp"
android:layout_height="70dp"
android:alpha="0"
android:layout_gravity="center"
android:src="@mipmap/jianshu_logo"/>
</FrameLayout>
如果想了解CircleImageView,请访问上篇文章史上最简洁高效的圆形ImageView
自定义进度条弹出框ProgressDialog,代码如下:
package com.feng.edward.percentprogressbardemo2.dialog;
import com.feng.edward.percentprogressbardemo2.R;
import com.feng.edward.percentprogressbardemo2.view.CircleImageView;
import com.feng.edward.percentprogressbardemo2.view.PercentProgressBar;
import android.app.Dialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
/**
* 功能描述:
*
* @author (作者) edward(冯丰枫)
* @link http://www.jianshu.com/u/f7176d6d53d2
* 创建时间: 2018/4/17 0017
*/
public class ProgressDialog {
private final Dialog mDialog;
private final PercentProgressBar mPercentProgress;
private final CircleImageView mCircleImageView;
public ProgressDialog(Context context) {
View view = LayoutInflater.from(context).inflate(R.layout.dialog_layout, null);
mPercentProgress = view.findViewById(R.id.circle_percent_view);
mCircleImageView = view.findViewById(R.id.circle_image_view);
mDialog = new Dialog(context, R.style.ProgressDialogTheme);
mDialog.setContentView(view);
mDialog.setCanceledOnTouchOutside(true);
}
public void show() {
mDialog.show();
}
public void setPercentProgress(int percentProgress) {
mPercentProgress.setPercentProgress(percentProgress);
mCircleImageView.setAlpha((float) percentProgress/100);
}
public void dismiss() {
mDialog.dismiss();
}
}
在MainActivity中使用如下:
package com.feng.edward.percentprogressbardemo2;
import com.feng.edward.percentprogressbardemo2.dialog.ProgressDialog;
import com.feng.edward.percentprogressbardemo2.util.IPublishProgress;
import com.feng.edward.percentprogressbardemo2.util.MyAsyncTask;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements MyAsyncTask.IIsViewActive {
private ProgressDialog mProgressDialog;
private TextView mainText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mProgressDialog = new ProgressDialog(this);
mainText = findViewById(R.id.main_text);
mainText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mainText.setText(R.string.downloading);
downLoad();
}
});
}
private void downLoad() {
MyAsyncTask.<Void, Integer, Void>newBuilder()
.setPreExecute(new MyAsyncTask.IPreExecute() {
@Override
public void onPreExecute() {
mProgressDialog.show();
}
})
.setDoInBackground(new MyAsyncTask.IDoInBackground<Void, Integer, Void>() {
@Override
public Void doInBackground(IPublishProgress<Integer> publishProgress, Void... voids) {
try {
for (int i = 0; i <= 100; i++) {
Thread.sleep(100);
publishProgress.showProgress(i);
}
} catch (Exception ignore) {
}
return null;
}
})
.setProgressUpdate(new MyAsyncTask.IProgressUpdate<Integer>() {
@Override
public void onProgressUpdate(Integer... values) {
mProgressDialog.setPercentProgress(values[0]);
}
})
.setViewActive(this)
.setPostExecute(new MyAsyncTask.IPostExecute<Void>() {
@Override
public void onPostExecute(Void aVoid) {
mProgressDialog.dismiss();
mainText.setText(R.string.download_finish);
}
})
.start();
}
@Override
public boolean isViewActive() {
return !(isFinishing() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && isDestroyed()));
}
}
如想了解MyAsyncTask,请访问Android AsyncTask 优化封装
activity_mian.xml中的代码如下:
<?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:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/main_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/main_text"
android:layout_centerInParent="true"/>
</RelativeLayout>