概览
先来看下效果图:
饼图可旋转,也可复位,也就是说可以指定选中某一个扇形。
图形结构
图形由文字及饼图下的椭圆,小圆点和线(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));