效果
attrs.xml
< attr name = " textSpace" format = " dimension|reference" />
< attr name = " barSpace" format = " dimension|reference" />
< attr name = " scaleHeight" format = " dimension|reference" />
< attr name = " progressHeight" format = " dimension|reference" />
< attr name = " barRadius" format = " dimension|reference" />
< attr name = " barColor" format = " color|reference" />
< attr name = " barOutColor" format = " color|reference" />
< attr name = " textSize" format = " dimension|reference" />
< attr name = " strokeColor" format = " color|reference" />
< attr name = " strokeWidth" format = " dimension|reference" />
< attr name = " max" format = " integer|reference" />
< attr name = " progress" format = " integer|reference" />
< declare-styleable name = " AirQualityBar" >
< attr name = " textSpace" />
< attr name = " barSpace" />
< attr name = " scaleHeight" />
< attr name = " progressHeight" />
< attr name = " barRadius" />
< attr name = " barColor" />
< attr name = " barOutColor" />
< attr name = " textSize" />
< attr name = " strokeColor" />
< attr name = " strokeWidth" />
< attr name = " max" />
< attr name = " progress" />
</ declare-styleable>
使用
private String [ ] labels = new String [ ] { "优" , "良" , "轻度" , "中度" , "重度" , "严重" } ;
private int [ ] values = new int [ ] { 35 , 75 , 115 , 150 , 250 } ;
private int [ ] colors = {
Color . parseColor ( "#0BCB81" ) ,
Color . parseColor ( "#E0DE25" ) ,
Color . parseColor ( "#F9A13A" ) ,
Color . parseColor ( "#F93A3A" ) ,
Color . parseColor ( "#B61455" ) ,
Color . parseColor ( "#B61455" ) ,
} ;
private float [ ] positions = {
0f , 0.2f , 0.4f , 0.6f , 0.8f , 1.0f
} ;
AirQualityBar bar = holder. find ( R . id. air_quality_bar) ;
bar. setGradient ( colors , positions) ;
bar. setLabels ( labels ) ;
bar. setValues ( values ) ;
bar. setMax ( 360 ) ;
bar. setProgress ( 270 , true ) ;
源码
import android. animation. Animator ;
import android. animation. ValueAnimator ;
import android. content. Context ;
import android. content. res. TypedArray ;
import android. graphics. Canvas ;
import android. graphics. Color ;
import android. graphics. LinearGradient ;
import android. graphics. Paint ;
import android. graphics. Rect ;
import android. graphics. RectF ;
import android. graphics. Shader ;
import android. util. AttributeSet ;
import android. view. View ;
import androidx. annotation. NonNull ;
import androidx. annotation. Nullable ;
import java. util. ArrayList ;
import java. util. Arrays ;
import java. util. List ;
import cn. anbao. forest. wards. R ;
public class AirQualityBar extends View implements ValueAnimator. AnimatorUpdateListener , Animator. AnimatorListener {
private Paint paint;
private int textSpace = 10 ;
private int barSpace = 4 ;
private int scaleHeight = 4 ;
private int progressHeight = 12 ;
private int barRadius = 6 ;
private int barColor = Color . WHITE ;
private int barOutColor = Color . parseColor ( "#80FFFFFF" ) ;
private int textSize = 14 ;
private int strokeColor = Color . parseColor ( "#687785" ) ;
private int strokeWidth = 2 ;
private int centerY;
private int width, height;
private int max = 100 ;
private int progress = 0 ;
private int paintProgress;
private String [ ] labels = new String [ ] { "优" , "良" , "轻度" , "中度" , "重度" , "严重" } ;
private int [ ] values = new int [ ] { 35 , 75 , 115 , 150 , 250 } ;
private int [ ] colors = {
Color . parseColor ( "#0BCB81" ) ,
Color . parseColor ( "#E0DE25" ) ,
Color . parseColor ( "#F9A13A" ) ,
Color . parseColor ( "#F93A3A" ) ,
Color . parseColor ( "#B61455" ) ,
Color . parseColor ( "#B61455" ) ,
} ;
private int [ ] paintColors;
private float [ ] positions = {
0f , 0.2f , 0.4f , 0.6f , 0.8f , 1.0f
} ;
private float [ ] paintPositions;
private int progressRadius;
private ValueAnimator animator;
public AirQualityBar ( Context context) {
this ( context, null ) ;
}
public AirQualityBar ( Context context, @Nullable AttributeSet attrs) {
this ( context, attrs, 0 ) ;
}
public AirQualityBar ( Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this ( context, attrs, defStyleAttr, 0 ) ;
}
public AirQualityBar ( Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super ( context, attrs, defStyleAttr, defStyleRes) ;
initAttributeSet ( context, attrs, defStyleAttr) ;
}
private void initAttributeSet ( Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
initAnimator ( ) ;
if ( attrs != null ) {
TypedArray array = context. obtainStyledAttributes ( attrs, R . styleable. AirQualityBar, defStyleAttr, 0 ) ;
textSpace = array. getDimensionPixelOffset ( R . styleable. AirQualityBar_textSpace, textSpace) ;
barSpace = array. getDimensionPixelOffset ( R . styleable. AirQualityBar_barSpace, barSpace) ;
scaleHeight = array. getDimensionPixelOffset ( R . styleable. AirQualityBar_scaleHeight, scaleHeight) ;
progressHeight = array. getDimensionPixelOffset ( R . styleable. AirQualityBar_progressHeight, progressHeight) ;
barRadius = array. getDimensionPixelOffset ( R . styleable. AirQualityBar_barRadius, barRadius) ;
barColor = array. getColor ( R . styleable. AirQualityBar_barColor, barColor) ;
barOutColor = array. getColor ( R . styleable. AirQualityBar_barOutColor, barOutColor) ;
textSize = array. getDimensionPixelSize ( R . styleable. AirQualityBar_textSize, textSize) ;
strokeColor = array. getColor ( R . styleable. AirQualityBar_strokeColor, strokeColor) ;
strokeWidth = array. getDimensionPixelOffset ( R . styleable. AirQualityBar_strokeWidth, strokeWidth) ;
max = array. getInt ( R . styleable. AirQualityBar_max, max) ;
progress = array. getInt ( R . styleable. AirQualityBar_progress, progress) ;
array. recycle ( ) ;
}
setProgress ( progress) ;
paintColors = colors;
paintPositions = positions;
paintProgress = progress;
}
private void initAnimator ( ) {
if ( animator == null ) {
animator = new ValueAnimator ( ) ;
animator. setDuration ( 500 ) ;
animator. addUpdateListener ( this ) ;
animator. addListener ( this ) ;
}
}
@Override
public void onAnimationUpdate ( @NonNull ValueAnimator valueAnimator) {
paintProgress = ( int ) valueAnimator. getAnimatedValue ( ) ;
findSuitableColorsPositions ( paintProgress) ;
invalidate ( ) ;
}
@Override
public void onAnimationStart ( @NonNull Animator animator) {
}
@Override
public void onAnimationEnd ( @NonNull Animator animator) {
release ( ) ;
}
@Override
public void onAnimationCancel ( @NonNull Animator animator) {
}
@Override
public void onAnimationRepeat ( @NonNull Animator animator) {
}
@Override
protected void onMeasure ( int widthMeasureSpec, int heightMeasureSpec) {
super . onMeasure ( widthMeasureSpec, heightMeasureSpec) ;
width = getMeasuredWidth ( ) ;
height = getMeasuredHeight ( ) ;
centerY = height / 2 ;
}
@Override
protected void onDraw ( @NonNull Canvas canvas) {
super . onDraw ( canvas) ;
paint = new Paint ( ) ;
paint. setAntiAlias ( true ) ;
paint. setStyle ( Paint. Style . STROKE ) ;
paint. setStrokeWidth ( strokeWidth) ;
paint. setTextSize ( textSize) ;
paint. setColor ( strokeColor) ;
progressRadius = progressHeight / 2 ;
RectF bounds = new RectF ( 0 , centerY - progressRadius, width, centerY + progressRadius) ;
canvas. drawRoundRect ( bounds, progressRadius, progressRadius, paint) ;
float percent = getRealProgressValue ( paintProgress) * 1.0F / max;
paint. setStyle ( Paint. Style . FILL ) ;
RectF rectF = new RectF ( barSpace, centerY - progressRadius + barSpace, barSpace + ( width - 2 * barSpace) * percent, centerY + progressRadius - barSpace) ;
if ( paintColors. length > 1 && paintPositions. length > 1 ) {
paint. setShader ( new LinearGradient ( rectF. left, rectF. top, rectF. right, rectF. top, paintColors, paintPositions, Shader. TileMode . CLAMP ) ) ;
} else {
paint. setColor ( paintColors[ 0 ] ) ;
}
canvas. drawRoundRect ( rectF, progressRadius, progressRadius, paint) ;
if ( percent > 0 ) {
paint. setShader ( null ) ;
int horizontal = width - 2 * barSpace;
boolean isEnd = percent >= 0.95f ;
float cx = isEnd ? horizontal * percent - barSpace : horizontal * percent - barSpace/ 2 ;
float cy = centerY;
paint. setColor ( barOutColor) ;
canvas. drawCircle ( cx + barSpace, cy, barRadius, paint) ;
paint. setColor ( barColor) ;
float innerRadius = barRadius * 0.65f ;
canvas. drawCircle ( cx + innerRadius, cy, innerRadius, paint) ;
}
drawScale ( canvas, labels, values) ;
}
private int getRealProgressValue ( int progress) {
return calculateProgress ( values, max, progress) ;
}
private int calculateProgress ( int [ ] values, int max, int value) {
int [ ] mixValues = Arrays . copyOf ( values, values. length + 1 ) ;
mixValues[ mixValues. length - 1 ] = max;
int segmentValue = max / ( mixValues. length) ;
int progress = 0 ;
for ( int i = 0 ; i < mixValues. length; i++ ) {
int preIndex = i - 1 ;
int preValue = preIndex > - 1 ? mixValues[ preIndex] : 0 ;
int itemValue = mixValues[ i] ;
if ( value > preValue && value <= itemValue) {
int diffValue = itemValue - preValue;
int segmentProgress = ( value - preValue) * segmentValue / diffValue;
progress = ( preIndex < 0 ? 0 : segmentValue * ( preIndex + 1 ) ) + segmentProgress;
}
}
return progress;
}
public float getPercent ( ) {
return getRealProgressValue ( progress) * 1.0F / max;
}
private List < Integer > colorList;
private List < Float > positionList;
private void findSuitableColorsPositions ( int progress) {
float percent = progress * 1.0F / max;
if ( positions == null || colors == null ) {
return ;
}
if ( positions. length == 0 || colors. length == 0 ) {
return ;
}
if ( colorList == null ) {
colorList = new ArrayList < > ( ) ;
} else {
colorList. clear ( ) ;
}
if ( positionList == null ) {
positionList = new ArrayList < > ( ) ;
} else {
positionList. clear ( ) ;
}
for ( int i = 0 ; i < positions. length; i++ ) {
if ( percent > positions[ i] ) {
positionList. add ( positions[ i] ) ;
colorList. add ( colors[ i] ) ;
}
}
int colorSize = colorList. size ( ) ;
if ( colorSize == 0 ) {
return ;
}
paintColors = new int [ colorSize] ;
for ( int i = 0 ; i < colorList. size ( ) ; i++ ) {
paintColors[ i] = colorList. get ( i) ;
}
int positionSize = positionList. size ( ) ;
if ( positionSize == 0 ) {
return ;
}
paintPositions = new float [ positionSize] ;
float itemValue = 1.0F / positionSize;
for ( int i = 0 ; i < positionList. size ( ) ; i++ ) {
paintPositions[ i] = itemValue * ( i + 1 ) ;
}
}
private void drawScale ( Canvas canvas, String [ ] labels, int [ ] values) {
paint = new Paint ( ) ;
paint. setAntiAlias ( true ) ;
paint. setColor ( strokeColor) ;
paint. setStrokeWidth ( strokeWidth) ;
paint. setTextSize ( textSize) ;
int labelSize = labels. length;
int labelItemWidth = width / labelSize;
for ( int i = 0 ; i < labelSize; i++ ) {
float startX = labelItemWidth * ( i + 1 ) ;
float startY = centerY - progressRadius;
float stopX = labelItemWidth * ( i + 1 ) ;
float stopY = startY - scaleHeight;
if ( i < labelSize - 1 ) {
canvas. drawLine ( startX, startY, stopX, stopY, paint) ;
}
String label = labels[ i] ;
float x = startX - labelItemWidth / 2 - measureText ( paint, label) . width ( ) / 2 ;
float y = startY - textSpace;
canvas. drawText ( labels[ i] , x, y, paint) ;
}
int valueCount = values. length;
int valueItemWidth = width / labelSize;
for ( int i = 0 ; i < valueCount; i++ ) {
float startX = valueItemWidth * ( i + 1 ) ;
float startY = centerY + progressRadius;
float stopX = valueItemWidth * ( i + 1 ) ;
float stopY = startY + scaleHeight;
canvas. drawLine ( startX, startY, stopX, stopY, paint) ;
String value = values[ i] + "" ;
Rect bounds = measureText ( paint, value) ;
float x = startX - bounds. width ( ) / 2 ;
float y = stopY + textSpace + bounds. height ( ) / 2 ;
canvas. drawText ( value, x, y, paint) ;
}
}
private Rect measureText ( Paint paint, String text) {
Rect bounds = new Rect ( ) ;
paint. getTextBounds ( text, 0 , text. length ( ) , bounds) ;
return bounds;
}
public void setMax ( int max) {
this . max = max;
}
public int getProgress ( ) {
return progress;
}
public void setProgress ( int progress) {
setProgress ( progress, true ) ;
}
public void setProgress ( int progress, boolean animator) {
if ( getProgress ( ) == progress) {
return ;
}
progress = progress < 0 ? 0 : progress;
if ( progress == 0 ) {
paintProgress = 0 ;
findSuitableColorsPositions ( paintProgress) ;
invalidate ( ) ;
return ;
}
this . progress = progress;
if ( animator) {
startAnimator ( progress) ;
} else {
paintProgress = progress;
findSuitableColorsPositions ( paintProgress) ;
invalidate ( ) ;
}
}
private void startAnimator ( int value) {
initAnimator ( ) ;
animator. setIntValues ( 0 , value) ;
animator. start ( ) ;
}
public void setGradient ( int [ ] colors, float [ ] positions) {
this . colors = colors;
this . positions = positions;
invalidate ( ) ;
}
public void setLabels ( String [ ] labels) {
this . labels = labels;
invalidate ( ) ;
}
public void setValues ( int [ ] values) {
this . values = values;
invalidate ( ) ;
}
public String getLabel ( ) {
String label = "" ;
float value = getPercent ( ) ;
int length = labels. length;
for ( int i = 0 ; i < length; i++ ) {
float min = i * 1.F / length;
float max = ( i + 1 ) * 1.0F / length;
if ( value >= min && value < max) {
label = labels[ i] ;
}
}
return label;
}
public void release ( ) {
if ( animator != null ) {
animator. cancel ( ) ;
animator. removeAllListeners ( ) ;
animator. removeAllUpdateListeners ( ) ;
animator = null ;
}
}
}