Android自定义view实例PieChart分析

概览

前面有一篇Android自定义view的官方文章的翻译(英语不好,尽力而为),地址:http://blog.csdn.net/zjqblog/article/details/51026243

代码太多,文章中只贴主要的,这里也共享下demo的github地址:https://github.com/softwater/PieChart

先来看下效果图:

Android自定义view实例PieChart分析_第1张图片

饼图可旋转,也可复位,也就是说可以指定选中某一个扇形。


图形结构

图形由文字及饼图下的椭圆,小圆点和线(PieChart.PointerView),饼图(PieChart.PieView)组成。


饼图PieView的绘制

PieView继承自View,在onDraw()方法中实现了饼图的绘制:
@Override protected void onDraw(Canvas canvas) {
......

      for (Item it : mData) {
        Log.e(TAG, "item = " + it);
        mPiePaint.setShader(it.mShader);
        canvas.drawArc(mBounds, 360 - it.mEndAngle, it.mEndAngle - it.mStartAngle, true, mPiePaint);
      }
    }

主要是其中的drawArc方法,所以PieView就是几个扇形拼凑起来的。
下面是输出的Item的内容:
Item{mLabel='Agamemnon', mValue=2.0, mColor=-3737115, mStartAngle=0, mEndAngle=48, mHighlight=-2228225, mShader=android.graphics.SweepGradient@2311cee0}
Item{mLabel='Bocephus', mValue=3.5, mColor=-4789835, mStartAngle=48, mEndAngle=132, mHighlight=-3407926, mShader=android.graphics.SweepGradient@3cdd8599}
Item{mLabel='Calliope', mValue=2.5, mColor=-5842507, mStartAngle=132, mEndAngle=192, mHighlight=-4590646, mShader=android.graphics.SweepGradient@165cb05e}
Item{mLabel='Daedalus', mValue=3.0, mColor=-6895179, mStartAngle=192, mEndAngle=264, mHighlight=-5709366, mShader=android.graphics.SweepGradient@9e7ec3f}
Item{mLabel='Euripides', mValue=1.0, mColor=-7947851, mStartAngle=264, mEndAngle=288, mHighlight=-6893622, mShader=android.graphics.SweepGradient@1974f90c}
Item{mLabel='Ganymede', mValue=3.0, mColor=-9000523, mStartAngle=288, mEndAngle=360, mHighlight=-8077878, mShader=android.graphics.SweepGradient@25f7e255}

在MainActivity中对Item进行了赋值,下面取出其中的一个:
pie.addItem("Agamemnon", 2, res.getColor(R.color.seafoam));
这里设置了每一个扇形的文本内容,所占的大小比重,颜色。
在addItem方法里计算了颜色高亮的值,统计了所有扇形大小比重的总和。
// Calculate the highlight color. Saturate at 0xff to make sure that high values
    // don't result in aliasing.
    it.mHighlight =
        Color.argb(0xff, Math.min((int) (mHighlightStrength * (float) Color.red(color)), 0xff),
            Math.min((int) (mHighlightStrength * (float) Color.green(color)), 0xff),
            Math.min((int) (mHighlightStrength * (float) Color.blue(color)), 0xff));
    mTotal += value;

每次改变了存放扇形Item的集合的时候都要调用onDataChanged()方法进行重新计算。
/**
   * Do all of the recalculations needed when the data array changes.
   */
  private void onDataChanged() {
    Log.e(TAG, "onDataChanged");
    // When the data changes, we have to recalculate
    // all of the angles.
    int currentAngle = 0;
    for (Item it : mData) {
      it.mStartAngle = currentAngle;
      it.mEndAngle = (int) ((float) currentAngle + it.mValue * 360.0f / mTotal);
      currentAngle = it.mEndAngle;

      // Recalculate the gradient shaders. There are
      // three values in this gradient, even though only
      // two are necessary, in order to work around
      // a bug in certain versions of the graphics engine
      // that expects at least three values if the
      // positions array is non-null.
      //
      it.mShader =
          new SweepGradient(mPieBounds.width() / 2.0f, mPieBounds.height() / 2.0f, new int[] {
              it.mHighlight, it.mHighlight, it.mColor, it.mColor,
          }, new float[] {
              0, (float) (360 - it.mEndAngle) / 360.0f, (float) (360 - it.mStartAngle) / 360.0f,
              1.0f
          });
    }
    calcCurrentItem();
    onScrollFinished();
  }


小圆点和线(PieChart.PointerView)的绘制

很简单,使用canvas调用drawLine()绘制线,调用drawCircle()绘制圆点。
/**
   * View that draws the pointer on top of the pie chart
   */
  private class PointerView extends View {

    /**
     * Construct a PointerView object
     */
    public PointerView(Context context) {
      super(context);
    }

    @Override protected void onDraw(Canvas canvas) {
      canvas.drawLine(mTextX, mPointerY, mPointerX, mPointerY, mTextPaint);
      canvas.drawCircle(mPointerX, mPointerY, mPointerRadius, mTextPaint);
    }
  }


文字及饼图下的椭圆的绘制

在PieChart中有这样一个方法:
@Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    // Draw the shadow
    canvas.drawOval(mShadowBounds, mShadowPaint);

    // Draw the label text
    if (getShowText()) {
      canvas.drawText(mData.get(mCurrentItem).mLabel, mTextX, mTextY, mTextPaint);
    }

    // If the API level is less than 11, we can't rely on the view animation system to
    // do the scrolling animation. Need to tick it here and call postInvalidate() until the scrolling is done.
    if (Build.VERSION.SDK_INT < 11) {
      tickScrollAnimation();
      if (!mScroller.isFinished()) {
        postInvalidate();
      }
    }
  }
drawOval()绘制了饼图下的椭圆,drawText()方法绘制了扇形的名字。

在旋转的时候扇形下的椭圆会发生变化,是因为在init方法里设置了如下代码:
mShadowPaint.setMaskFilter(new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL));

























你可能感兴趣的:(android)