自定义View实现一个环形比例图以及相关原理

做项目的时候遇到了一个环形比例图的UI,本着不能让自己成为一个面向百度开发的CV工程师的原则,决定通过自定义View来实现,最终效果如此下图所示:


圆环比例图.jpg

很简单的一个效果,实现起来也不难,大致可以分为三步:

  • 绘制底色圆;
  • 绘制圆环;
  • 绘制文字。

OK!边贴代码边讲解
首先是属性设置,已有详细注释,这里就不再赘述了,由于我们项目的需求只有三个比例值,所以这里只设置了三个:


        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    

写一个init方法获取属性值并初始化Paint:

 private void init(Context context, AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.RoundRateView);
        rate1Color = ta.getColor(R.styleable.RoundRateView_rate1Color, context.getResources().getColor(R.color.round_green));
        rate2Color = ta.getColor(R.styleable.RoundRateView_rate2Color, context.getResources().getColor(R.color.round_red));
        rate3Color = ta.getColor(R.styleable.RoundRateView_rate3Color, context.getResources().getColor(R.color.round_grey));
        topTextColor = ta.getColor(R.styleable.RoundRateView_topTextColor, context.getResources().getColor(R.color.dark));
        centerTextColor = ta.getColor(R.styleable.RoundRateView_centerTextColor, context.getResources().getColor(R.color.round_grey));
        bottomTextColor = ta.getColor(R.styleable.RoundRateView_bottomTextColor, context.getResources().getColor(R.color.round_grey));
        topTextSize = ta.getDimension(R.styleable.RoundRateView_topTextSize, PixelUtils.dp2px(10));
        centerTextSize = ta.getDimension(R.styleable.RoundRateView_centerTextSize, PixelUtils.dp2px(10));
        bottomTextSize = ta.getDimension(R.styleable.RoundRateView_bottomTextSize, PixelUtils.dp2px(10));
        topText = ta.getString(R.styleable.RoundRateView_topText);
        centerText = ta.getString(R.styleable.RoundRateView_centerText);
        bottomText = ta.getString(R.styleable.RoundRateView_bottomText);
        //PixelUtils.dp2px()是将dp转化为px,这是我自己封装的一个工具类
        roundWidth = ta.getDimension(R.styleable.RoundRateView_roundWidth, PixelUtils.dp2px(15));
        rate1 = ta.getFloat(R.styleable.RoundRateView_rate1, 0.3f);
        rate2 = ta.getFloat(R.styleable.RoundRateView_rate2,0.3f);
        ta.recycle();
        mRate1Paint = new Paint();
        mRate2Paint = new Paint();
        mRate3Paint = new Paint();
        topTextPaint = new TextPaint();
        centerTextPaint = new TextPaint();
        bottomTextPaint = new TextPaint();
        initRatePaint(mRate1Paint,rate1Color,roundWidth+10);
        initRatePaint(mRate2Paint,rate2Color,roundWidth);
        initRatePaint(mRate3Paint,rate3Color,roundWidth);
        initTextPaint(topTextPaint,topTextColor,topTextSize);
        initTextPaint(centerTextPaint,centerTextColor,centerTextSize);
        initTextPaint(bottomTextPaint,bottomTextColor,bottomTextSize);
        mRate1Paint.setAlpha(180);
    }
  //初始化绘制圆环的画笔
  private void initTextPaint(TextPaint textPaint, int textColor, float textSize) {
        textPaint.setAntiAlias(true);
        textPaint.setColor(textColor);
        textPaint.setTextSize(textSize);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setStrokeWidth(8);
    }
    
    //初始化绘制文字的画笔
    private void initRatePaint(Paint paint, int color,float width) {
        paint.setColor(color);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(width);
    }

因为在onSizeChanged()以后,控件的大小就能确定了,所以一些属性值可以在onSizeChanged()方法中进行设置:

   @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if(getHeight()!=0 && getWidth() !=0) {
            //底色圆以及圆环半径,默认占满控件,所以取测量宽高的最小值来算
            radius = (Math.min(getHeight(), getWidth()) - roundWidth - 10) / 2;
            initAcrBaseRect();
            //topTextBottom和bottomTextTop分别为上面文字的下边界的纵坐标和下面文字的上边界的纵坐标,先都初始化为控件高度的一半
            if (topTextBottom == 0f)
                topTextBottom = getHeight() / 2;
            if (bottomTextTop == 0f)
                bottomTextTop = getHeight() / 2;
            //调整字体大小
            if(topTextY == 0f || bottomTextY == 0f || centerTextY == 0f){
                adjustTextSize();
            }
        }
    }

  private void initAcrBaseRect() {
        arcBaseRect = new RectF(getWidth() / 2 - radius,
                getHeight() / 2 - radius,
                getWidth() / 2 + radius,
                getHeight() / 2 + radius);
    }

这里需要有一个预备知识:
绘制圆环的api是:

public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

其中oval相当于是圆环的一个外框架,什么意思呢?
当我通过drawArc绘制一个圆时就是这样的:

canvas.drawArc(rectF,0,360,false,paint);

外面的蓝色框就是rectF,这里的rectF是一个正方形,如果不是正方形呢?

第二个参数startAngle是起始角度,水平向右方向为0,顺时针为正值,逆时针为负值,第三个参数sweepAngle为圆环扫过的角度,先看一下例子:
canvas.drawArc(rectF,-90,90,false,paint);

canvas.drawArc(rectF,0,110,false,paint);

第四个参数useCenter是指是否经过圆心,在paint设置为描边属性(即paint.setStyle(Paint.Style.STROKE)时是看不出效果的,当设置为填充属性(即paint.setStyle(Paint.Style.FILL))时,效果如下
canvas.drawArc(rectF,0,90,false,paint);

canvas.drawArc(rectF,0,90,true,paint);

这就是绘制圆环的api的使用方法
而我在initAcrBaseRect()方法中arcBaseRect初始化了
adjustTextSize()方法是用来调整字体大小的,因为字体不能想设多大就设多大,最大也都要在圆环内,起码不能”穿模“,而在写这个方法之前还得了解一个预备知识:
我们绘制文字的时候用的是这个api:

canvas.drawText(@NonNull String text, float x, float y, @NonNull Paint paint)

参数除了文字内容和画笔,还多了个坐标xy,这个坐标代表的是什么坐标呢?
其实这个坐标代表的是绘制文字的baseline(基准点)的坐标,其实字体的测量有五个参数,可以通过Paint.getFontMetrics()获取,参数和含义分别是:

  • FontMetrics.ascent:是baseline之上至字符最高处的距离;

  • FontMetrics.descent:是baseline之下至字符最低处的距离;

  • FontMetrics.leading:是上一行字符的descent到下一行的ascent之间的距离,也就是相邻行间的空白距离;

  • FontMetrics.top:是指的是最高字符到baseline的值,即ascent的最大值;

  • FontMetrics.bottom:是指最低字符到baseline的值,即descent的最大值。

我这里又写了个Demo:


这里的两条蓝线的交点就是drawLine传入的坐标(x,y),所以将水平的蓝线当作baseline,并且baseline到上面的距离为负数,到下面的距离为正数的话,那么baseline到四条紫色线的距离从上到下分别就是top,ascent,descent,bottom。换句话说,如果baseline纵坐标为baselineY,四条紫线从上到下纵坐标分别为y1,y2,y3,y4,那么:

top = y1 - baselineY;
ascent = y2 - baselineY;
descent = y3 - baselineY;
bottom = y4 - baselineY;

而这里的基准坐标的位置可以通过Paint.setTextAlign(Align align)来设置,参数值分别可以传Paint.Align.LEFT,Paint.Align.CENTER,Paint.Align.RIGHT,分别可以将基准点设置在文字的左边,中间和右边,不设置的时候默认是Paint.Align.LEFT,中间和右边的效果如下图所示:

textPaint.setTextAlign(Paint.Align.CENTER)

textPaint.setTextAlign(Paint.Align.RIGHT)

再回到这个自定义VIew:
看一下adjustTextSize()方法具体是怎么实现的:

 //调整字体大小
    private void adjustTextSize(){
        if(!TextUtils.isEmpty(centerText))
            adjustCenterTextSize();
        if(!TextUtils.isEmpty(topText))
            adjustTopTextSize();
        if(!TextUtils.isEmpty(bottomText))
            adjustBottomTextSize();
    }

  //调整中间字体大小
    private void adjustCenterTextSize() {
        //测量文字内容宽度
        float centerTextWidth = centerTextPaint.measureText(centerText);
        Paint.FontMetrics centerFontMetrics = centerTextPaint.getFontMetrics();
        //获取文字高度
        float textHeight = centerFontMetrics.bottom - centerFontMetrics.top;
        //这里为了美观,防止中间文字挤压上下文字空间,规定中间文字高度不能超过圆环内径的一半,可自行设置,超过的话就将字体大小-1dp再递归调用
        if(textHeight > (radius - (roundWidth + 10) / 2)){
            centerTextSize -= PixelUtils.dp2px(1);
            centerTextPaint.setTextSize(centerTextSize);
            adjustCenterTextSize();
        } else {
            //根据勾股定理算出中间文字最大宽度:文字最大宽度^2 + 文字高度^2 = 内圆直径^2
            double centerMaxWidth = Math.sqrt(Math.pow(2 * radius - roundWidth, 2) -
                    Math.pow(textHeight, 2));
            //如果文字宽度大于最大宽度,则将字体大小-1dp再递归调用
            if (centerTextWidth > centerMaxWidth && centerTextSize > 0) {
                centerTextSize -= PixelUtils.dp2px(1);
                centerTextPaint.setTextSize(centerTextSize);
                adjustCenterTextSize();
            } else {
                //根据最终字体大小将中间文字的上边界和下边界纵坐标分别作为上面文字的下边界和下面文字的上边界赋值给topTextBottom和bottomTextTop
                topTextBottom = (getHeight() - textHeight) / 2;
                bottomTextTop = (getHeight() + textHeight) / 2;
                //centerTextY 是中间文字的最终的baseline的纵坐标值
                centerTextY = bottomTextTop - centerFontMetrics.bottom;
            }
        }
    }

 //调整上面字体大小
   private void adjustTopTextSize() {
        //测量文字内容宽度
        float topTextWidth = topTextPaint.measureText(topText);
        Paint.FontMetrics topFontMetrics = topTextPaint.getFontMetrics();
        //获取文字高度
        float textHeight = topFontMetrics.bottom - topFontMetrics.top;
        //文字的上边界不能超过圆环内边界,超过的话就将字体大小-1dp再递归调用
        if(textHeight + getHeight() / 2 - topTextBottom > (radius - ((roundWidth + 10) / 2))&& topTextSize > 0){
            topTextSize-=PixelUtils.dp2px(1);
            topTextPaint.setTextSize(topTextSize);
            adjustTopTextSize();
        }else {
            //根据勾股定理求得文字最大宽度:文字最大宽度^2 + (2*文字高度+中间文字高度)^2 = 内圆直径^2
            double topMaxWidth = Math.sqrt( Math.pow(2 * radius - roundWidth, 2) -
                    Math.pow((textHeight + (getHeight() / 2 - topTextBottom)) * 2, 2));
            //如果文字宽度大于最大宽度,则将字体大小-1dp再递归调用
            if (topTextWidth > topMaxWidth && topTextSize > 0) {
                topTextSize -= PixelUtils.dp2px(1);
                topTextPaint.setTextSize(topTextSize);
                adjustTopTextSize();
            }else {
                //topTextY 是上面文字的最终baseline的纵坐标
                topTextY = topTextBottom - topFontMetrics.bottom;
            }
        }
    }

//调整下面字体大小,原理和调整上面字体大小相同,这里不再赘述
    private void adjustBottomTextSize() {
        float bottomTextWidth = bottomTextPaint.measureText(bottomText);
        Paint.FontMetrics bottomFontMetrics = bottomTextPaint.getFontMetrics();
        float textHeight = bottomFontMetrics.bottom - bottomFontMetrics.top;
        if(textHeight + bottomTextTop - (getHeight() / 2) > (radius - ((roundWidth + 10) / 2))&& topTextSize > 0){
            bottomTextSize -= PixelUtils.dp2px(1);
            bottomTextPaint.setTextSize(bottomTextSize);
            adjustBottomTextSize();
        } else {
            double bottomMaxWidth = Math.sqrt(Math.pow(2 * radius - roundWidth, 2) -
                    Math.pow((textHeight + (bottomTextTop - (getHeight() / 2))) * 2, 2));
            if (bottomTextWidth > bottomMaxWidth && bottomTextSize > 0) {
                bottomTextSize -= PixelUtils.dp2px(1);
                bottomTextPaint.setTextSize(bottomTextSize);
                adjustBottomTextSize();
            } else {
                bottomTextY = bottomTextTop - bottomFontMetrics.top;
            }
        }
    }

因为中间文字是居中显示的,所以要先调整中间字体大小,而上下的字体大小是根据中间字体大小来调整的。
我们可以把文字的显示区域看成一个矩形,设置了文字大小以后根据以上的预备知识获取到矩形的高度,因为文字是居中显示的,所以根据勾股定理就能获取到文字的一个最大宽度。如果文字宽度大于最大宽度,就将文字大小减1dp,再递归调用adjustCenterTextSize(),直到文字宽度大小不大于最大宽度,然后再将中间文字的上边界和下边界纵坐标分别作为上面文字的下边界和下面文字的上边界赋值给topTextBottombottomTextTop
而调整上面字体大小和调整下面字体大小都是差不多的思路,代码中有注释就不再赘述了。

设置好所有属性,onDraw()方法里面就是直接使用相关属性参数就行了,比较简单:

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制底色圆和圆环
        drawRound(canvas);
        //绘制文字
        drawText(canvas);
    }

//绘制底色圆和圆环
 private void drawRound(Canvas canvas) {
        //绘制底色圆环(rate3)
        canvas.drawCircle(getWidth()/2,getHeight()/2,radius,mRate3Paint);
        float rate1Angle = 360 * rate1;
        float rate2Angle = 360 * rate2;
        canvas.drawArc(arcBaseRect,-90,rate1Angle,false,mRate1Paint);
        canvas.drawArc(arcBaseRect,rate1Angle-90,rate2Angle,false,mRate2Paint);
    }

  //绘制文字
  private void drawText(Canvas canvas) {
        if(topTextY == 0f || bottomTextY == 0f || centerTextY == 0f){
            adjustTextSize();
        }
        if(!TextUtils.isEmpty(centerText))
            canvas.drawText(topText,getWidth()/2,topTextY,topTextPaint);
        if(!TextUtils.isEmpty(topText))
            canvas.drawText(centerText,getWidth()/2,centerTextY,centerTextPaint);
        if(!TextUtils.isEmpty(bottomText))
            canvas.drawText(bottomText,getWidth()/2,bottomTextY,bottomTextPaint);
  }

最后再添加一些修改属性的接口,这里只给出了部分,想要给什么属性提供接口都可以加,如果为了一些更加平滑的效果,也可以在设置比例的时候添加动画:

  //设置比值
 public void setRate(float rate1,float rate2){
        this.rate1 = rate1;
        this.rate2 = rate2;
        invalidate();
  }

 //设置上面文字
  public void setTopText(String topText) {
        this.topText = topText;
        adjustTopTextSize();
        invalidate();
  }

   //设置下面文字
  public void setBottomText(String bottomText) {
        this.bottomText = bottomText;
        adjustBottomTextSize();
        invalidate();
  }

 //设置中间文字
  public void setCenterText(String centerText) {
        int oldLength = this.centerText.length();
        int newLength = centerText.length();
        this.centerText = centerText;
        if(oldLength!=newLength)
            adjustTextSize();
        invalidate();
  }

  //三个文字一起设置
  public void setText(String top,String center,String bottom){
        this.topText = top;
        this.centerText = center;
        this.bottomText = bottom;
        adjustTextSize();
        invalidate();
  }

这样一个简单的环形比例图就OK了,下面是全部代码:

public class RoundRateView extends View {

    private Paint mRate1Paint;
    private Paint mRate2Paint;
    private Paint mRate3Paint;
    private TextPaint topTextPaint;
    private TextPaint centerTextPaint;
    private TextPaint bottomTextPaint;
    private int rate1Color;
    private int rate2Color;
    private int rate3Color;
    private int topTextColor;
    private int centerTextColor;
    private int bottomTextColor;
    private float roundWidth;
    private float rate2;
    private float rate1;
    private float radius;
    private RectF arcBaseRect;
    private float topTextSize;
    private float centerTextSize;
    private float bottomTextSize;
    private String topText;
    private String centerText;
    private String bottomText;
    private float bottomTextTop;
    private float topTextBottom;
    private float topTextY;
    private float bottomTextY;
    private float centerTextY;

    public RoundRateView(Context context) {
        this(context,null);
    }

    public RoundRateView(Context context, @Nullable @android.support.annotation.Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public RoundRateView(Context context, @Nullable @android.support.annotation.Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context,attrs);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public RoundRateView(Context context, @Nullable @android.support.annotation.Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.RoundRateView);
        rate1Color = ta.getColor(R.styleable.RoundRateView_rate1Color, context.getResources().getColor(R.color.round_green));
        rate2Color = ta.getColor(R.styleable.RoundRateView_rate2Color, context.getResources().getColor(R.color.round_red));
        rate3Color = ta.getColor(R.styleable.RoundRateView_rate3Color, context.getResources().getColor(R.color.round_grey));
        topTextColor = ta.getColor(R.styleable.RoundRateView_topTextColor, context.getResources().getColor(R.color.dark));
        centerTextColor = ta.getColor(R.styleable.RoundRateView_centerTextColor, context.getResources().getColor(R.color.round_grey));
        bottomTextColor = ta.getColor(R.styleable.RoundRateView_bottomTextColor, context.getResources().getColor(R.color.round_grey));
        topTextSize = ta.getDimension(R.styleable.RoundRateView_topTextSize, PixelUtils.dp2px(10));
        centerTextSize = ta.getDimension(R.styleable.RoundRateView_centerTextSize, PixelUtils.dp2px(10));
        bottomTextSize = ta.getDimension(R.styleable.RoundRateView_bottomTextSize, PixelUtils.dp2px(10));
        topText = ta.getString(R.styleable.RoundRateView_topText);
        centerText = ta.getString(R.styleable.RoundRateView_centerText);
        bottomText = ta.getString(R.styleable.RoundRateView_bottomText);
        roundWidth = ta.getDimension(R.styleable.RoundRateView_roundWidth, PixelUtils.dp2px(15));
        rate1 = ta.getFloat(R.styleable.RoundRateView_rate1, 0.3f);
        rate2 = ta.getFloat(R.styleable.RoundRateView_rate2,0.3f);
        ta.recycle();
        mRate1Paint = new Paint();
        mRate2Paint = new Paint();
        mRate3Paint = new Paint();
        topTextPaint = new TextPaint();
        centerTextPaint = new TextPaint();
        bottomTextPaint = new TextPaint();
        initRatePaint(mRate1Paint,rate1Color,roundWidth+10);
        initRatePaint(mRate2Paint,rate2Color,roundWidth);
        initRatePaint(mRate3Paint,rate3Color,roundWidth);
        initTextPaint(topTextPaint,topTextColor,topTextSize);
        initTextPaint(centerTextPaint,centerTextColor,centerTextSize);
        initTextPaint(bottomTextPaint,bottomTextColor,bottomTextSize);
        mRate1Paint.setAlpha(180);
    }

    private void initTextPaint(TextPaint textPaint, int textColor, float textSize) {
        textPaint.setAntiAlias(true);
        textPaint.setColor(textColor);
        textPaint.setTextSize(textSize);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setStrokeWidth(8);
    }

    private void initRatePaint(Paint paint, int color,float width) {
        paint.setColor(color);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(width);
    }

    private void initAcrBaseRect() {
        arcBaseRect = new RectF(getWidth() / 2 - radius,
                getHeight() / 2 - radius,
                getWidth() / 2 + radius,
                getHeight() / 2 + radius);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawRound(canvas);
        drawText(canvas);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if(getHeight()!=0 && getWidth() !=0) {
            radius = (Math.min(getHeight(), getWidth()) - roundWidth - 10 ) / 2;
            initAcrBaseRect();
            if (topTextBottom == 0f)
                topTextBottom = getHeight() / 2;
            if (bottomTextTop == 0f)
                bottomTextTop = getHeight() / 2;
            if(topTextY == 0f || bottomTextY == 0f || centerTextY == 0f){
                adjustTextSize();
            }
        }
    }

    private void drawRound(Canvas canvas) {
        //画底色圆环(rate3)
        canvas.drawCircle(getWidth()/2,getHeight()/2,radius,mRate3Paint);
        float rate1Angle = 360 * rate1;
        float rate2Angle = 360 * rate2;
        canvas.drawArc(arcBaseRect,-90,rate1Angle,false,mRate1Paint);
        canvas.drawArc(arcBaseRect,rate1Angle-90,rate2Angle,false,mRate2Paint);
    }

    private void drawText(Canvas canvas) {
        if(topTextY == 0f || bottomTextY == 0f || centerTextY == 0f){
            adjustTextSize();
        }
        if(!TextUtils.isEmpty(centerText))
            canvas.drawText(topText,getWidth()/2,topTextY,topTextPaint);
        if(!TextUtils.isEmpty(topText))
            canvas.drawText(centerText,getWidth()/2,centerTextY,centerTextPaint);
        if(!TextUtils.isEmpty(bottomText))
            canvas.drawText(bottomText,getWidth()/2,bottomTextY,bottomTextPaint);
    }

    
    
    //调整字体大小
    private void adjustTextSize(){
        if(!TextUtils.isEmpty(centerText))
            adjustCenterTextSize();
        if(!TextUtils.isEmpty(topText))
            adjustTopTextSize();
        if(!TextUtils.isEmpty(bottomText))
            adjustBottomTextSize();
    }
    
    //调整上面字体大小
    private void adjustTopTextSize() {
        float topTextWidth = topTextPaint.measureText(topText);
        Paint.FontMetrics topFontMetrics = topTextPaint.getFontMetrics();
        float textHeight = topFontMetrics.bottom - topFontMetrics.top;
        if(textHeight + getHeight() / 2 - topTextBottom > (radius - ((roundWidth + 10) / 2))&& topTextSize > 0){
            topTextSize-=PixelUtils.dp2px(1);
            topTextPaint.setTextSize(topTextSize);
            adjustTopTextSize();
        }else {
            double topMaxWidth = Math.sqrt( Math.pow(2 * radius - roundWidth, 2) -
                    Math.pow((textHeight + (getHeight() / 2 - topTextBottom)) * 2, 2));
            if (topTextWidth > topMaxWidth && topTextSize > 0) {
                topTextSize -= PixelUtils.dp2px(1);
                topTextPaint.setTextSize(topTextSize);
                adjustTopTextSize();
            }else {
                topTextY = topTextBottom - topFontMetrics.bottom;
            }
        }
    }

    //调整中间字体大小
    private void adjustCenterTextSize() {
        float centerTextWidth = centerTextPaint.measureText(centerText);
        Paint.FontMetrics centerFontMetrics = centerTextPaint.getFontMetrics();
        float textHeight = centerFontMetrics.bottom - centerFontMetrics.top;
        if(textHeight > (radius - (roundWidth + 10) / 2)){
            centerTextSize -= PixelUtils.dp2px(1);
            centerTextPaint.setTextSize(centerTextSize);
            adjustCenterTextSize();
        } else {
            double centerMaxWidth = Math.sqrt(Math.pow(2 * radius - roundWidth, 2) -
                    Math.pow(textHeight, 2));
            if (centerTextWidth > centerMaxWidth && centerTextSize > 0) {
                centerTextSize -= PixelUtils.dp2px(1);
                centerTextPaint.setTextSize(centerTextSize);
                adjustCenterTextSize();
            } else {
                topTextBottom = (getHeight() - textHeight) / 2;
                bottomTextTop = (getHeight() + textHeight) / 2;
                centerTextY = bottomTextTop - centerFontMetrics.bottom;
            }
        }
    }

    //调整下面字体大小
    private void adjustBottomTextSize() {
        float bottomTextWidth = bottomTextPaint.measureText(bottomText);
        Paint.FontMetrics bottomFontMetrics = bottomTextPaint.getFontMetrics();
        float textHeight = bottomFontMetrics.bottom - bottomFontMetrics.top;
        if(textHeight + bottomTextTop - (getHeight() / 2) > (radius - ((roundWidth + 10) / 2))&& topTextSize > 0){
            bottomTextSize -= PixelUtils.dp2px(1);
            bottomTextPaint.setTextSize(bottomTextSize);
            adjustBottomTextSize();
        } else {
            double bottomMaxWidth = Math.sqrt(Math.pow(2 * radius - roundWidth, 2) -
                    Math.pow((textHeight + (bottomTextTop - (getHeight() / 2))) * 2, 2));
            if (bottomTextWidth > bottomMaxWidth && bottomTextSize > 0) {
                bottomTextSize -= PixelUtils.dp2px(1);
                bottomTextPaint.setTextSize(bottomTextSize);
                adjustBottomTextSize();
            } else {
                bottomTextY = bottomTextTop - bottomFontMetrics.top;
            }
        }
    }

    public void setRate(float rate1,float rate2){
        this.rate1 = rate1;
        this.rate2 = rate2;
        invalidate();
    }

    public void setTopText(String topText) {
        this.topText = topText;
        adjustTopTextSize();
        invalidate();
    }

    public void setBottomText(String bottomText) {
        this.bottomText = bottomText;
        adjustBottomTextSize();
        invalidate();
    }

    public void setCenterText(String centerText) {
        int oldLength = this.centerText.length();
        int newLength = centerText.length();
        this.centerText = centerText;
        if(oldLength!=newLength)
            adjustTextSize();
        invalidate();
    }

    public void setText(String top,String center,String bottom){
        this.topText = top;
        this.centerText = center;
        this.bottomText = bottom;
        adjustTextSize();
        invalidate();
    }

}


如果有什么不对的地方欢迎指正!

你可能感兴趣的:(自定义View实现一个环形比例图以及相关原理)