效果  
 
 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 ; 
        } 
    } 
}