实现渐变进度条的一种方案,解决进度小于头部半圆时变形的问题。
![](https://img.haomeiwen.com/i1689476/1664e47c820baf25.jpg)
上方的进度条为系统的ProgressBar,可以看到,第二个进度条progress背景发生了变形(比如progress设置为1)。
custom_progress_primary.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="10dp" />
<solid android:color="@color/colorAccent" />
</shape>
bg_progressbar.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<corners android:radius="15dp" />
<solid android:color="#eeeeee" />
<item android:id="@android:id/progress">
android:drawable="@drawable/custom_progress_primary"
android:scaleWidth="98%" />
</layer-list>
布局中的代码:
android:id="@+id/progress_first"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="15dp"
android:layout_marginTop="12dp"
android:layout_marginRight="20dp"
android:max="100"
android:progress="50"
android:progressDrawable="@drawable/bg_progressbar"
app:layout_constraintLeft_toLeftOf="@id/tv_first"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_first" />
核心代码即为:paint.setXfermode(new PorterDuffXfermode(mode));在进度小于半圆时,使用混合模式绘制两个圆相交的形状。以下为CornerProgressBar的代码
package com.allan.demo.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
import com.allan.demo.R;
import java.math.BigDecimal;
public class CornerProgressBarextends View {
private static final int DEFAULT_MAX =100;
private static final int DEFAULT_PROGRESS =0;
private static final int DEFAULT_START_COLOR =0xFFFFE60D;
private static final int DEFAULT_END_COLOR =0xFFFFC629;
private static final int DEFAULT_BACKGROUD_COLOR =0xFFEFF1F4;
private static final int DEFAULT_TEXT_COLOR =0xFF333333;
private static final int DEFAULT_TEXT_SIZE =14;
private double mMax =DEFAULT_MAX;
private double mProgress =DEFAULT_PROGRESS;//当前进度
private int mColor0 =DEFAULT_START_COLOR;//开始渐变色
private int mColor1 =DEFAULT_END_COLOR;//结束渐变色
private int mColorBackgroud =DEFAULT_BACKGROUD_COLOR;//背景色
private int mTextColor =DEFAULT_TEXT_COLOR;
private float mTextSize;//字体大小
private Paintpaint;
private PaintmBackgroudPaint;
private PorterDuff.Modemode;
public CornerProgressBar(Context context) {
super(context);
initStyle(context, null, 0, 0);
initPaint();
}
public CornerProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
initStyle(context, attrs, 0, 0);
initPaint();
}
public CornerProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initStyle(context, attrs, defStyleAttr, 0);
initPaint();
}
public CornerProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initStyle(context, attrs, defStyleAttr, defStyleRes);
initPaint();
}
/**
* 当progress大于max时,显示格式为150%,200%
*
* @param max
*/
public void setMax(double max) {
if (max <0) {
max =0;
}
this.mMax = max;
// if (mProgress > max) {
// mProgress = max;
// }
invalidate();
}
/**
* 不设定上限 当progress大于max时,显示格式为150%,200%
*
* @param progress
*/
public void setProgress(double progress) {
if (progress <0) {
progress =0;
}
// if (progress > mMax) {
// progress = mMax;
// }
this.mProgress = progress;
invalidate();
}
public double getProgress() {
return mProgress;
}
public double getMax() {
return mMax;
}
private void initStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.CornerProgressBar, defStyleAttr, defStyleRes);
mMax = array.getInt(R.styleable.CornerProgressBar_barMax, DEFAULT_MAX);
mProgress = array.getInt(R.styleable.CornerProgressBar_barProgress, DEFAULT_PROGRESS);
mColor0 = array.getColor(R.styleable.CornerProgressBar_barStartColor, DEFAULT_START_COLOR);
mColor1 = array.getColor(R.styleable.CornerProgressBar_barStartColor, DEFAULT_END_COLOR);
mColorBackgroud = array.getColor(R.styleable.CornerProgressBar_barBackColor, DEFAULT_BACKGROUD_COLOR);
mTextColor = array.getColor(R.styleable.CornerProgressBar_barTextColor, DEFAULT_TEXT_COLOR);
final float scale = getResources().getDisplayMetrics().density;
float defaultTextSize =DEFAULT_TEXT_SIZE * scale +0.5f;
mTextSize = array.getDimension(R.styleable.CornerProgressBar_barTextSize, defaultTextSize);
array.recycle();
}
private void initPaint() {
paint =new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
paint.setStrokeWidth(getHeight());
paint.setStrokeCap(Paint.Cap.ROUND);
mBackgroudPaint =new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
mBackgroudPaint.setColor(mColorBackgroud);
mBackgroudPaint.setStrokeCap(Paint.Cap.ROUND);
mode = PorterDuff.Mode.SRC_IN;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
paint.setStrokeWidth(getHeight());
mBackgroudPaint.setStrokeWidth(getHeight());
invalidate();
}
/**
* 计算进度宽度
*/
private int calProgressWidth() {
int width = getWidth();
int progressWidth = (int) (mProgress /mMax * width);
return progressWidth <= width ? progressWidth : width;//最大宽度不超过进度条总宽度 但数值可以超过最大数值
}
public double getProgressPercent() {
return mProgress /mMax;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (getWidth() ==0) {
return;
}
float radius = (float) getHeight() /2;
float offsetBackgroundWidth = getWidth() - radius;
int progressWidth = calProgressWidth();
float offsetProgressWidth = progressWidth - radius;//需要绘制的长度是控件长度减去左右半圆的宽,即结束x坐标为宽-半径
canvas.drawLine(radius, radius, offsetBackgroundWidth, radius, mBackgroudPaint);
LinearGradient linearGradient =new LinearGradient(0, 0, progressWidth, 0, mColor0, mColor1, Shader.TileMode.CLAMP);
paint.setShader(linearGradient);
if (progressWidth > getHeight()) {
canvas.drawLine(radius, radius, offsetProgressWidth, radius, paint);
}else {
Canvas canvas2 =new Canvas();
int savedState = canvas.save();
canvas2.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));//抗锯齿
Bitmap bt = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
canvas2.setBitmap(bt);
canvas2.drawCircle(radius, radius, radius, paint);
Bitmap bt2 = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
canvas2.setBitmap(bt2);
canvas2.drawCircle(progressWidth - radius, radius, radius, paint);
canvas.saveLayer(0, 0, getWidth(), getHeight(), paint,
Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(bt, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(mode));
//注意这里savelayer的paint 是有叠加模式的!!!!!!!!!!!!!!!!!
canvas.saveLayer(0, 0, getWidth(), getHeight(), paint,
Canvas.ALL_SAVE_FLAG);
paint.setXfermode(null);//注意这里需要吧叠加模式制空 不然会与layer的空位图 进行叠加
canvas.drawBitmap(bt2, 0, 0, paint);
canvas.restoreToCount(savedState);
}
drawProgressText(canvas);
}
private void drawProgressText(Canvas canvas) {
String result;
if (mMax !=0) {
BigDecimal progressDecimal = BigDecimal.valueOf(mProgress);
BigDecimal maxDecimal = BigDecimal.valueOf(mMax);
BigDecimal decimalHundred = BigDecimal.valueOf(100);
BigDecimal decimalPercent = progressDecimal.divide(maxDecimal, 3, BigDecimal.ROUND_HALF_UP);
BigDecimal decimalResult = decimalPercent.multiply(decimalHundred);
result = decimalResult.setScale(1, BigDecimal.ROUND_HALF_UP).toPlainString() +"%";
}else {
result ="0.0%";
}
Paint.FontMetrics fontMetrics =new Paint.FontMetrics();
paint.setTextSize(mTextSize);
paint.getFontMetrics(fontMetrics);
paint.setShader(null);
paint.setColor(mTextColor);
float topOffset = (float) getHeight() /2 - (fontMetrics.descent + fontMetrics.ascent) /2;
float textWidth =paint.measureText(result);
float leftOffset = (((float) getWidth() - textWidth)) /2;
// LogUtil.e("drawProgressText", "current=" + text + " width=" + getWidth() + " topOffset=" + topOffset + " textWidth=" + textWidth + " leftOffset=" + leftOffset);
canvas.drawText(result, leftOffset, topOffset, paint);
}
}
<declare-styleable name="CornerProgressBar">
<attr name="barTextSize" format="dimension|reference" />
<attr name="barTextColor" format="color|reference" />
<attr name="barStartColor" format="color|reference" />
<attr name="barEndColor" format="color|reference" />
<attr name="barBackColor" format="color|reference" />
<attr name="barMax" format="integer|reference" />
<attr name="barProgress" format="integer|reference" />
</declare-styleable>
网友评论