最近新提出需求, 需要自定义一个安全中心页面,表示安全分数的自定义progressbar.效果在最下方贴图.
下面来说下具体的实现方法:
public class ColorArcProgressBar extends View {
private int diameter = 500; //直径
private float centerX; //圆心X坐标
private float centerY; //圆心Y坐标
private Paint allArcPaint;
private Paint progressPaint;
private Paint vTextPaint;
private Paint hintPaint;
private Paint degreePaint;
private Paint curSpeedPaint;
private RectF bgRect;
private ValueAnimator progressAnimator;
private PaintFlagsDrawFilter mDrawFilter;
private SweepGradient sweepGradient;
private Matrix rotateMatrix;
private float startAngle = 155;
private float sweepAngle = 230;
private float currentAngle = 0.01f;
private float lastAngle;
private int[] colors = new int[]{Color.GREEN, Color.YELLOW, Color.RED, Color.RED};
private float maxValues = 60;
private float curValues = 0;
private float bgArcWidth = dipToPx(10);
private float progressWidth = dipToPx(10);
private float textSize = dipToPx(50);
private float hintSize = dipToPx(9);
private float curSpeedSize = dipToPx(9);
private int aniSpeed = 1500;
private float longDegree = dipToPx(13);
private float shortDegree = dipToPx(5);
private final int DEGREE_PROGRESS_DISTANCE = dipToPx(1);
private String hintColor = "#8d9298";
private String bgArcColor = "#F0F0F0";
private String titleString;
private String hintString;
private boolean isNeedTitle;
private boolean isNeedUnit;
private boolean isNeedDial;
private boolean isNeedContent;
private boolean isNeedProgress = false;
// sweepAngle / maxValues 的值
private float k;
public ColorArcProgressBar(Context context) {
super(context, null);
initView();
}
public ColorArcProgressBar(Context context, AttributeSet attrs) {
super(context, attrs, 0);
initCofig(context, attrs);
initView();
}
public ColorArcProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initCofig(context, attrs);
initView();
}
/**
* 初始化布局配置
*/
private void initCofig(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ColorArcProgressBar);
int color1 = a.getColor(R.styleable.ColorArcProgressBar_front_color1, Color.GREEN);
int color2 = a.getColor(R.styleable.ColorArcProgressBar_front_color2, color1);
int color3 = a.getColor(R.styleable.ColorArcProgressBar_front_color3, color1);
int color4 = a.getColor(R.styleable.ColorArcProgressBar_front_color4, color1);
int color5 = a.getColor(R.styleable.ColorArcProgressBar_front_color5, color1);
colors = new int[]{color1, color2, color3, color4, color5, color5};
sweepAngle = a.getInteger(R.styleable.ColorArcProgressBar_total_engle, 230);
bgArcWidth = a.getDimension(R.styleable.ColorArcProgressBar_back_width, dipToPx(10));
progressWidth = a.getDimension(R.styleable.ColorArcProgressBar_front_width, dipToPx(10));
isNeedTitle = a.getBoolean(R.styleable.ColorArcProgressBar_is_need_title, false);
isNeedContent = a.getBoolean(R.styleable.ColorArcProgressBar_is_need_content, false);
isNeedUnit = a.getBoolean(R.styleable.ColorArcProgressBar_is_need_unit, false);
isNeedDial = a.getBoolean(R.styleable.ColorArcProgressBar_is_need_dial, false);
hintString = a.getString(R.styleable.ColorArcProgressBar_string_unit);
titleString = a.getString(R.styleable.ColorArcProgressBar_string_title);
curValues = a.getFloat(R.styleable.ColorArcProgressBar_current_value, 0);
maxValues = a.getFloat(R.styleable.ColorArcProgressBar_max_value, 60);
setCurrentValues(curValues);
setMaxValues(maxValues);
a.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = (int) (2 * longDegree + progressWidth + diameter + 2 * DEGREE_PROGRESS_DISTANCE);
int height = (int) (2 * longDegree + progressWidth + diameter + 2 * DEGREE_PROGRESS_DISTANCE);
setMeasuredDimension(width, height);
}
private void initView() {
diameter = 3 * getScreenWidth() / 5;
//弧形的矩阵区域
bgRect = new RectF();
bgRect.top = longDegree + progressWidth / 2 + DEGREE_PROGRESS_DISTANCE;
bgRect.left = longDegree + progressWidth / 2 + DEGREE_PROGRESS_DISTANCE;
bgRect.right = diameter + (longDegree + progressWidth / 2 + DEGREE_PROGRESS_DISTANCE);
bgRect.bottom = diameter + (longDegree + progressWidth / 2 + DEGREE_PROGRESS_DISTANCE);
//圆心
centerX = (2 * longDegree + progressWidth + diameter + 2 * DEGREE_PROGRESS_DISTANCE) / 2;
centerY = (2 * longDegree + progressWidth + diameter + 2 * DEGREE_PROGRESS_DISTANCE) / 2;
//外部刻度线
degreePaint = new Paint();
degreePaint.setAntiAlias(true);
degreePaint.setColor(Color.parseColor("#C8C8C8"));
degreePaint.setStrokeWidth(dipToPx(1f));
degreePaint.setTextSize(dipToPx(10f));
//整个弧形
allArcPaint = new Paint();
allArcPaint.setAntiAlias(true);
allArcPaint.setStyle(Paint.Style.STROKE);
allArcPaint.setStrokeCap(Paint.Cap.ROUND);
allArcPaint.setStrokeWidth(bgArcWidth);
allArcPaint.setColor(Color.parseColor(bgArcColor));
//当前进度的弧形
progressPaint = new Paint();
progressPaint.setAntiAlias(true);
progressPaint.setStyle(Paint.Style.STROKE);
progressPaint.setStrokeCap(Paint.Cap.ROUND);
progressPaint.setStrokeWidth(progressWidth);
progressPaint.setColor(Color.GREEN);
//内容显示文字
vTextPaint = new Paint();
vTextPaint.setTextSize(textSize);
vTextPaint.setColor(Color.BLACK);
vTextPaint.setTextAlign(Paint.Align.CENTER);
//显示单位文字
hintPaint = new Paint();
hintPaint.setTextSize(hintSize);
hintPaint.setColor(Color.parseColor(hintColor));
hintPaint.setTextAlign(Paint.Align.CENTER);
//显示标题文字
curSpeedPaint = new Paint();
curSpeedPaint.setTextSize(curSpeedSize);
curSpeedPaint.setColor(Color.WHITE);
curSpeedPaint.setTextAlign(Paint.Align.CENTER);
mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
sweepGradient = new SweepGradient(centerX, centerY, colors, null);
rotateMatrix = new Matrix();
}
@Override
protected void onDraw(Canvas canvas) {
//抗锯齿
canvas.setDrawFilter(mDrawFilter);
if (isNeedDial) {
//画刻度线 TODO 画刻度线可以优化
for (int i = 0; i < 156; i++) {
if (i < 28 || i > 128) {
canvas.rotate(2.30769f, centerX, centerY);
continue;
}
canvas.drawLine(centerX, centerY + diameter / 2 - progressWidth / 2 - DEGREE_PROGRESS_DISTANCE - (longDegree - shortDegree) / 2,
centerX, centerY + diameter / 2 - progressWidth / 2 - DEGREE_PROGRESS_DISTANCE - (longDegree - shortDegree) / 2 - shortDegree, degreePaint);
if (((i - 28) % 20) == 0) {
if (i == 128) {
canvas.rotate(-2.30769f * 2, centerX, centerY);
} else {
canvas.rotate(-2.30769f, centerX, centerY);
}
canvas.rotate(180f, centerX, centerY + diameter / 2 - progressWidth / 2 - DEGREE_PROGRESS_DISTANCE - (longDegree - shortDegree) / 2 - shortDegree - dipToPx(10));
canvas.drawText(String.valueOf(i - 28), centerX, centerY + diameter / 2 - progressWidth / 2 - DEGREE_PROGRESS_DISTANCE - (longDegree - shortDegree) / 2 - shortDegree - dipToPx(10), degreePaint);
canvas.rotate(-180f, centerX, centerY + diameter / 2 - progressWidth / 2 - DEGREE_PROGRESS_DISTANCE - (longDegree - shortDegree) / 2 - shortDegree - dipToPx(10));
if (i == 128) {
canvas.rotate(2.30769f * 2, centerX, centerY);
} else {
canvas.rotate(2.30769f, centerX, centerY);
}
}
canvas.rotate(2.30769f, centerX, centerY);
}
}
//整个弧
canvas.drawArc(bgRect, startAngle, sweepAngle, false, allArcPaint);
//设置渐变色
rotateMatrix.setRotate(150, centerX, centerY);
sweepGradient.setLocalMatrix(rotateMatrix);
progressPaint.setShader(sweepGradient);
//当前进度
if (isNeedProgress) {
canvas.drawArc(bgRect, startAngle, currentAngle, false, progressPaint);
}
if (isNeedContent) {
canvas.drawText(String.format("%.0f", curValues), centerX, centerY, vTextPaint);
}
if (isNeedUnit) {
canvas.drawText(hintString, centerX, centerY + textSize / 2.6f, hintPaint);
}
if (isNeedTitle) {
RectF rectF = new RectF();
rectF.left = centerX - dipToPx(32);
rectF.top = centerY + textSize / 1.1f - dipToPx(11);
rectF.right = centerX + dipToPx(32);
rectF.bottom = centerY + textSize / 1.1f + dipToPx(4);
canvas.drawRoundRect(rectF, dipToPx(15), dipToPx(15), vTextPaint); //绘制圆角矩形
canvas.drawText(titleString, centerX, centerY + textSize / 1.1f, curSpeedPaint);
}
invalidate();
}
/**
* 设置最大值
*/
public void setMaxValues(float maxValues) {
this.maxValues = maxValues;
k = sweepAngle / maxValues;
}
/**
* 设置当前值
*/
public void setCurrentValues(float currentValues) {
if (currentValues > maxValues) {
currentValues = maxValues;
}
if (currentValues < 0) {
currentValues = 0;
}
this.curValues = currentValues;
lastAngle = currentAngle;
setAnimation(lastAngle, currentValues * k, aniSpeed);
}
/**
* 设置整个圆弧宽度
*/
public void setBgArcWidth(int bgArcWidth) {
this.bgArcWidth = bgArcWidth;
}
/**
* 设置进度宽度
*/
public void setProgressWidth(int progressWidth) {
this.progressWidth = progressWidth;
}
/**
* 设置速度文字大小
*/
public void setTextSize(int textSize) {
this.textSize = textSize;
}
/**
* 设置单位文字大小
*/
public void setHintSize(int hintSize) {
this.hintSize = hintSize;
}
public void setNeedProgress(boolean needProgress) {
isNeedProgress = needProgress;
}
/**
* 设置单位文字
*/
public void setUnit(String hintString) {
this.hintString = hintString;
invalidate();
}
/**
* 设置直径大小
*/
public void setDiameter(int diameter) {
this.diameter = dipToPx(diameter);
}
/**
* 设置标题
*/
private void setTitle(String title) {
this.titleString = title;
}
/**
* 设置是否显示标题
*/
private void setIsNeedTitle(boolean isNeedTitle) {
this.isNeedTitle = isNeedTitle;
}
/**
* 设置是否显示单位文字
*/
private void setIsNeedUnit(boolean isNeedUnit) {
this.isNeedUnit = isNeedUnit;
}
/**
* 设置是否显示外部刻度盘
*/
private void setIsNeedDial(boolean isNeedDial) {
this.isNeedDial = isNeedDial;
}
/**
* 为进度设置动画
*/
private void setAnimation(final float last, final float current, int length) {
progressAnimator = ValueAnimator.ofFloat(last, current);
progressAnimator.setDuration(length);
progressAnimator.setTarget(currentAngle);
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentAngle = (float) animation.getAnimatedValue();
curValues = currentAngle / k;
if (vTextPaint != null) {
if (0 <= curValues && curValues < 20f) {
vTextPaint.setColor(Color.parseColor("#FB8301"));
titleString = "安全等级低";
} else if (20f <= curValues && curValues < 40f) {
vTextPaint.setColor(Color.parseColor("#FBB004"));
titleString = "安全等级低";
} else if (40f <= curValues && curValues < 60f) {
vTextPaint.setColor(Color.parseColor("#C9C730"));
titleString = "安全等级中";
} else if (60f <= curValues && curValues < 80f) {
vTextPaint.setColor(Color.parseColor("#71CC80"));
titleString = "安全等级中";
} else {
vTextPaint.setColor(Color.parseColor("#0598F6"));
titleString = "安全等级高";
}
}
}
});
progressAnimator.start();
}
/**
* dip 转换成px
*/
private int dipToPx(float dip) {
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
}
/**
* 得到屏幕宽度
*/
private int getScreenWidth() {
WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.widthPixels;
}
}
1.其中再为进度设置动画时,需改变安全等级背景颜色方法较笨, 应该能进一步实现背景颜色随进度条走到的颜色变而变化, 知道的小伙伴,可以在评论中给出答案噢~感谢~
2.在刚开始currentAngle 设置为0.01f,是为了适配oppo手机开始会显示进度条100%的闪烁, 改成0.01f就不会有这种bug了.属于笨方法解决哦~
贴出sttr.xml文件:
<declare-styleable name="ColorArcProgressBar">
<attr name="back_color" format="color"/>
<attr name="front_color1" format="color"/>
<attr name="front_color2" format="color"/>
<attr name="front_color3" format="color"/>
<attr name="front_color4" format="color"/>
<attr name="front_color5" format="color"/>
<attr name="back_width" format="dimension"/>
<attr name="front_width" format="dimension"/>
<attr name="is_need_title" format="boolean"/>
<attr name="is_need_content" format="boolean"/>
<attr name="is_need_unit" format="boolean"/>
<attr name="is_need_dial" format="boolean"/>
<attr name="string_title" format="string"/>
<attr name="string_unit" format="string"/>
<attr name="total_engle" format="integer"/>
<attr name="current_value" format="float"/>
<attr name="max_value" format="float"/>
</declare-styleable>
在xml中使用:
<com.clwapp.hx.widget.ColorArcProgressBar
android:id="@id/color_arc_progressbar"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/common_13dp"
app:back_color="@android:color/darker_gray"
app:back_width="10dp"
app:front_color1="#FB7E00"
app:front_color2="#FAC405"
app:front_color3="#73CC7C"
app:front_color4="#06A2F3"
app:front_color5="#0079FC"
app:front_width="10dp"
app:is_need_content="true"
app:is_need_dial="true"
app:is_need_title="true"
app:is_need_unit="true"
app:max_value="100"
app:string_title="安全等级低"
app:string_unit="BETA"
app:total_engle="230" />
在Activity中使用:
progressBar.setNeedProgress(true);
progressBar.setCurrentValues(80);
这样, 自定义progressbar就写完了, 有不足的地方,还请各大神门指正!!!
效果如图: