自定义View实战之饼状图效果实现

效果图预览
这里写图片描述

1. 分析

1. 饼状图这个首先需要确定几块饼,确定每一块饼需要绘制的角度
2. 根据饼的范围计算百分比的坐标位置
3. 动画处理
4. 中间白色圆和文字的绘制

2. 实现原理

1. 绘制各个饼,可以有两种实现方式 一种通过canvas.drawArc然后中间部分用白色圆给盖住,
另一种方式是Path.arcTo然后用path.op方法取交集
2. 绘制中间文字和白色的圆 通过canvas.drawCircle和canvas.drawText
3.动画处理 计算每一个饼扫描过的角度 计算每一块绘制的角度

3. 初始化一些东西 初始化一般我放在onSizeChanged方法中

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mCenterX = w / 2;
    mCenterY = h / 2;

    mInRadius = dp2px(60);
    mOutRadius = dp2px(120);

    // setProgressAnimation(DURATION);
}

4. 绘制中间白色圆和中心文字

//画中间白色的圆
private void drawWhiteCircle(Canvas canvas) {
    canvas.drawCircle(0,0,mInRadius,mWhiteCirclePaint);
}

//画中间饼状图文字
private void drawText(Canvas canvas) {
    String text = "饼状图";
    //设置文字水平居中
    mTextPaint.setTextAlign(Paint.Align.CENTER);
    //测量文字的宽高 要使文字竖直方向也居中
    mTextPaint.getTextBounds(text,0,text.length(), mTextRect);
    int height = mTextRect.height();
    canvas.drawText(text,0,0+height / 2,mTextPaint);
}

5. 画饼状扇形图 这里我为了简单平分了5份饼

  1. canvas.save()和canvas.restore()是为了保证当前画布操作不会影响之前或者之后的操作,需要注意的是一般是存对出现,不然可能会抛异常
  2. mPieChartNum:你要等分的饼的数量 这里我就为了简单等分了5份
  3. mDrawAngle:每一个饼状图扫描的角度 比如均分5个饼的话 mDrawAngle = 360 / 5 = 72度
  4. //分别是计算x,y方向的坐标值
    mOutRadius*(float) Math.cos(Math.toRadians(mStartAngle))
    mOutRadius*(float) Math.sin(Math.toRadians(mStartAngle))
    mOutPath.arcTo 添加圆环
  5. 取圆和圆环的交集
    // op(a,b,Path.Op.REVERSE_DIFFERENCE) b-a的交集
    mPath.op(mInPath,mOutPath, Path.Op.REVERSE_DIFFERENCE)
//画饼状扇形
private void drawPieChart(Canvas canvas) {

    canvas.save();
    mInPath.reset();
    mOutRectF.set(-mOutRadius, -mOutRadius, mOutRadius, mOutRadius);
    mInPath.addCircle(0,0,mInRadius, Path.Direction.CW);

    for (int i = 0; i < mPieChartNum; i++) {
        mStartAngle = (i == 0) ? 0 : mStartAngle + mScaleAngle;
        if (Math.min(mDrawAngle, mAnimatedValue - mStartAngle) >= 0) {
            float drawAngle = Math.min(mDrawAngle, mAnimatedValue - mStartAngle);
            mOutPaint.setColor(mColors[i]);
            mOutPath.lineTo(mOutRadius*(float) Math.cos(Math.toRadians(mStartAngle)),
                    mOutRadius*(float) Math.sin(Math.toRadians(mStartAngle)));
            mOutPath.arcTo(mOutRectF, mStartAngle, drawAngle);
            // op(a,b,Path.Op.REVERSE_DIFFERENCE)  b-a的交集
            mPath.op(mInPath,mOutPath, Path.Op.REVERSE_DIFFERENCE);
            canvas.drawPath(mPath,mOutPaint);

            //画透明度圆环
            drawInRing(canvas,mStartAngle,drawAngle);

            //画完一段圆弧再画百分比文字
            if(drawAngle % mDrawAngle== 0) {
                drawPercentText(canvas, mStartAngle);
            }
        }
        mPath.reset();
        mOutPath.reset();

    }
    canvas.restore();
}

//画百分比文字
private void drawPercentText(Canvas canvas,float startAngle) {

    float angle = mDrawAngle / 2 + startAngle;
    //计算x,y的时候其实并不需要在不同象限单独计算  比如说 cos0 = 1  -cos(180-180) = cos 180 = -1
    float x = (float) (0.75 * mOutRadius * Math.cos(Math.toRadians(angle))) ;
    float y = (float) (0.75 * mOutRadius * Math.sin(Math.toRadians(angle))) ;
    DecimalFormat df = new DecimalFormat("0");
    String format = df.format(100 * 1.0f / mPieChartNum) + "%";
    mTextPaint.getTextBounds(format,0,format.length(),mPercentRect);
    canvas.drawText(format,x,y + mPercentRect.height() / 2,mTextPaint);
}

6. 动画处理

//设置进度条动画
public void setProgressAnimation(long duration) {
    if(mProgressAnimator != null && mProgressAnimator.isRunning()){
        mProgressAnimator.cancel();
        mProgressAnimator.start();
    }else {
        mProgressAnimator = ValueAnimator.ofFloat(0, 360).setDuration(duration);
        mProgressAnimator.setInterpolator(new AccelerateInterpolator());
        mProgressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                /**每次要绘制的圆弧角度**/
                mAnimatedValue = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        mProgressAnimator.start();
    }
}

7. 项目源代码下载

后面统一提供代码下载地址

8. 联系方式

QQ:1509815887
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值