The requirements in the project can be realized by searching the MPAndroidChart library at that time, but it is not very cool to reference such a large library just for one requirement, so I plan to define one by myself. Reference + practice


First, the convention of the effect map

Second, making

The code address, welcome to correct https://github.com/MNXP/XPPieChart

Third, train of thought

Get the current Paint Angle based on the animation. 4. Get the color used for Paint based on the current Angle.Copy the code

Four, implementation,

1. Initialize data for hollow graph (draw a small circle in the center of a large circle)

      paint = new Paint();
      paint.setAntiAlias(true); paint.setStyle(Paint.Style.FILL_AND_STROKE); screenW = DensityUtils.getScreenWidth(context); int width = DensityUtils.dip2px(context, 15); Int widthXY = denSityutils.dip2px (context, 10); Int pieCenterX = screenW / 2; Int pieCenterY = screenW / 3; Int pieRadius = screenW / 4; Rect pieOval = new RectF(); pieOval.left = pieCenterX - pieRadius; pieOval.top = pieCenterY - pieRadius + widthXY; pieOval.right = pieCenterX + pieRadius; pieOval.bottom = pieCenterY + pieRadius + widthXY; Rect pieOvalIn = new RectF(); pieOvalIn.left = pieOval.left + width; pieOvalIn.top = pieOval.top + width; pieOvalIn.right = pieOval.right - width; pieOvalIn.bottom = pieOval.bottom - width; PiePaintIn = new Paint(); piePaintIn.setAntiAlias(true);
      piePaintIn.setStyle(Paint.Style.FILL);
      piePaintIn.setColor(Color.parseColor("#f4f4f4"));
Copy the code

2. Calculate the Angle occupied according to the data

Use recursion to ensure that the values of cakeValues must sum to 100, and then calculate the Angle based on the valuesCopy the code
   private void settleCakeValues(int i) {
       float sum = getSum(cakeValues, i);
       CakeValue value = cakeValues.get(i);
       if (sum <= 100f) {
           value.setItemValue(100f - sum);
           cakeValues.set(i, value);
       } else{ value.setItemValue(0); settleCakeValues(i - 1); }}Copy the code

3. Get the current drawing Angle based on the animation

CurAngle is the current drawing Angle, and drawArc() is the drawing methodCopy the code
cakeValueAnimator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float mAngle = obj2Float(animation.getAnimatedValue("angle")); curAngle = mAngle; drawArc(); }});Copy the code

4. Get the color used for Paint based on the current Angle

SetColor (color.parsecolor (cakeValues. Get (colorIndex).getcolors ()))); To set the paint colorCopy the code
private int getCurItem(float curAngle) {
        int res = 0;
        for (int i = 0; i < itemFrame.length; i++) {
            if (curAngle <= itemFrame[i] * ANGLE_NUM) {
                res = i;
                break; }}return res;
    }
Copy the code

5. Dynamically draw what is to be drawn and what has been drawn

The most important step I need is 4 classes, with different colorsCopy the code

Draws the sector of the current color, the start position of the curStartAngle sector, and the end position of the curSweepAngle sectorCopy the code
  paint.setColor(Color.parseColor(cakeValues.get(colorIndex).getColors()));
  float curStartAngle = 0;
  float curSweepAngle = curAngle;
  if (curItem > 0) {
      curStartAngle = itemFrame[curItem - 1] * ANGLE_NUM;
      curSweepAngle = curAngle - (itemFrame[curItem - 1] * ANGLE_NUM);
   }
   canvas.drawArc(pieOval, curStartAngle, curSweepAngle, true, paint);
Copy the code
Draw the sector that has been drawn. Draw the sector according to the curItem judgmentCopy the code
for (int i = 0; i < curItem; i++) {
    paint.setColor(Color.parseColor(cakeValues.get(i).getColors()));
    if (i == 0) {
        canvas.drawArc(pieOval, startAngle,(float) cakeValues.get(i).getItemValue() * ANGLE_NUM, true, paint);
        continue;
    }
    canvas.drawArc(pieOval,itemFrame[i - 1] * ANGLE_NUM,(float) cakeValues.get(i).getItemValue() * ANGLE_NUM, true, paint);
}

Copy the code
Draw the center circleCopy the code
 canvas.drawArc(pieOvalIn, 0, 360, true, piePaintIn);
Copy the code

6. Pay special attention

IsFirst determines whether it is the first time to draw (after drawing, the home button enters the background and enters again, without dynamic drawing)Copy the code
 @Override
    protected void onDraw(Canvas canvas) {
        if (isFirst && isDrawByAnim) {
            drawCakeByAnim();
        }
        isFirst = false;
    }
Copy the code
IsDrawByAnim Determines whether drawCake() needs to be animated to draw a statically drawn pie chartCopy the code
public void surfaceCreated(SurfaceHolder holder) {
    if(! isFirst||! isDrawByAnim) drawCake(); }Copy the code

update

Add stereo effect, extract configuration parametersCopy the code
<declare-styleable name="CakeSurfaceView">
        <attr name="isDrawByAnim" format="boolean"/>// Animate <attr name="isSolid" format="boolean"/>// Whether stereo <attr name="duration" format="integer|reference"<attr name="defaultColor" format="string"/>// Default color <attr name="ringWidth" format="integer|reference"/>// Ring width <attr name="solidWidth" format="integer|reference"<attr name="fineTuningWidth" format="integer|reference"/>// Adjust the width </declare-styleable>
Copy the code
XML is used inCopy the code
<com.xp.xppiechart.view.CakeSurfaceView
            android:id="@+id/assets_pie_chart"
            android:background="#ffffff"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:defaultColor="#ff8712"
            app:ringWidth="20"
            app:solidWidth="5"
            app:duration="3000"
            app:isSolid="true"
            app:isDrawByAnim="true"/>
Copy the code

The above is a simple implementation of dynamic drawing pie chart, to be improved, will be updated later. If you have any suggestions or comments, please contact us in time.