Reputation: 2254
I am using mpandroid charting library. I modified the PieChartRenderer and was able to get a chart like this.
PieChartRenderer
public class ImagePieChartRenderer extends PieChartRenderer {
private ArrayList<Bitmap> bitMaps;
private Paint mEntryLabelsPaint;
public ImagePieChartRenderer(PieChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler, ArrayList<Bitmap> bitMaps) {
super(chart, animator, viewPortHandler);
this.bitMaps = bitMaps;
mEntryLabelsPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mEntryLabelsPaint.setColor(Color.BLACK);
mEntryLabelsPaint.setTextAlign(Paint.Align.CENTER);
mEntryLabelsPaint.setTextSize(Utils.convertDpToPixel(13f));
}
@Override
protected void drawDataSet(Canvas c, IPieDataSet dataSet) {
drawImages(c);
super.drawDataSet(c, dataSet);
}
private void drawImages(Canvas c) {
MPPointF center = mChart.getCenterCircleBox();
// get whole the radius
float radius = mChart.getRadius();
float rotationAngle = mChart.getRotationAngle();
float[] drawAngles = mChart.getDrawAngles();
float[] absoluteAngles = mChart.getAbsoluteAngles();
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
final float holeRadiusPercent = mChart.getHoleRadius() / 100.f;
float labelRadiusOffset = radius / 10f * 3.6f;
labelRadiusOffset = (radius - (radius * holeRadiusPercent)) / 2f;
final float labelRadius = radius - labelRadiusOffset;
PieData data = mChart.getData();
List<IPieDataSet> dataSets = data.getDataSets();
float yValueSum = data.getYValueSum();
float angle;
int xIndex = 0;
c.save();
float offset = Utils.convertDpToPixel(5.f);
for (int i = 0; i < dataSets.size(); i++) {
IPieDataSet dataSet = dataSets.get(i);
// apply the text-styling defined by the DataSet
applyValueTextStyle(dataSet);
float lineHeight = Utils.calcTextHeight(mValuePaint, "Q")
+ Utils.convertDpToPixel(4f);
IValueFormatter formatter = dataSet.getValueFormatter();
int entryCount = dataSet.getEntryCount();
mValueLinePaint.setColor(dataSet.getValueLineColor());
mValueLinePaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getValueLineWidth()));
final float sliceSpace = getSliceSpace(dataSet);
for (int j = 0; j < entryCount; j++) {
PieEntry entry = dataSet.getEntryForIndex(j);
if (xIndex == 0)
angle = 0.f;
else
angle = absoluteAngles[xIndex - 1] * phaseX;
final float sliceAngle = drawAngles[xIndex];
final float sliceSpaceMiddleAngle = sliceSpace / (Utils.FDEG2RAD * labelRadius);
// offset needed to center the drawn text in the slice
final float angleOffset = (sliceAngle - sliceSpaceMiddleAngle / 2.f) / 2.f;
angle = angle + angleOffset;
final float transformedAngle = rotationAngle + angle * phaseY;
float value = mChart.isUsePercentValuesEnabled() ? entry.getY()
/ yValueSum * 100f : entry.getY();
final float sliceXBase = (float) Math.cos(transformedAngle * Utils.FDEG2RAD);
final float sliceYBase = (float) Math.sin(transformedAngle * Utils.FDEG2RAD);
final float valueLineLength1 = dataSet.getValueLinePart1Length();
final float valueLineLength2 = dataSet.getValueLinePart2Length();
final float valueLinePart1OffsetPercentage = dataSet.getValueLinePart1OffsetPercentage() / 100.f;
float pt2x, pt2y;
float labelPtx, labelPty;
float percentX, percentY;
float line1Radius;
line1Radius = (radius - (radius * holeRadiusPercent))
* valueLinePart1OffsetPercentage
+ (radius * holeRadiusPercent);
final float polyline2Width = dataSet.isValueLineVariableLength()
? labelRadius * valueLineLength2 * (float) Math.abs(Math.sin(
transformedAngle * Utils.FDEG2RAD))
: labelRadius * valueLineLength2;
final float pt0x = line1Radius * sliceXBase + center.x;
final float pt0y = line1Radius * sliceYBase + center.y;
final float pt1x = labelRadius * (1 + valueLineLength1) * sliceXBase + center.x;
final float pt1y = labelRadius * (1 + valueLineLength1) * sliceYBase + center.y;
if (transformedAngle % 360.0 >= 40.0 && transformedAngle % 360.0 <= 130.0) {
pt2x = pt1x + polyline2Width;
pt2y = pt1y;
Log.d(entry.getLabel(), "TWO "+ transformedAngle % 360.0);
mValuePaint.setTextAlign(Paint.Align.LEFT);
mEntryLabelsPaint.setTextAlign(Paint.Align.LEFT);
labelPtx = pt2x;
labelPty = pt2y;
percentX = labelPtx - 1.5f*offset ;
percentY = labelPty + bitMaps.get(j).getHeight() + 2.2f*offset;
} else {
pt2x = pt1x - polyline2Width;
pt2y = pt1y;
Log.d(entry.getLabel(), "ONE "+ transformedAngle % 360.0);
mValuePaint.setTextAlign(Paint.Align.RIGHT);
mEntryLabelsPaint.setTextAlign(Paint.Align.RIGHT);
labelPtx = pt2x;
labelPty = pt2y;
percentX = labelPtx + 1.5f*offset;
percentY = labelPty - bitMaps.get(j).getHeight() - 0.5f*offset;
}
mValueLinePaint.setColor(dataSet.getColor(j));
c.drawLine(pt0x, pt0y, pt1x, pt1y, mValueLinePaint);
c.drawLine(pt1x, pt1y, pt2x, pt2y, mValueLinePaint);
// draw everything, depending on settings
drawValue(c,
formatter,
value,
entry,
0,
percentX,
percentY,
dataSet.getValueTextColor(j));
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(dataSet.getColor(j));
c.drawCircle(labelPtx, labelPty, bitMaps.get(j).getWidth(), paint);
c.drawBitmap(bitMaps.get(j), labelPtx-bitMaps.get(j).getWidth()/2f, labelPty-bitMaps.get(j).getHeight()/2, null );
// if (j < data.getEntryCount() && entry.getLabel() != null) {
// drawEntryLabel(c, entry.getLabel(), labelPtx, labelPty + lineHeight);
// }
xIndex++;
}
}
MPPointF.recycleInstance(center);
c.restore();
}
}
Now I need to be able click on the small circles around the chart. Can someone please point me towards the classes and functions that needs to be modified to achieve this .
Basically I want to invoke the onValueSelected(Entry e, Highlight h)
when I touch the circles.
Upvotes: 3
Views: 2708
Reputation: 21407
First of all, it's great to see people writing their own renderers like that. Looks awesome!
For your actual question, The class you want is called OnChartGestureListener
. The source code is here.
First of all, you'll probably need to cache the positions of your circles and bitmaps inside your renderer. Something like this:
public ImagePieChartRenderer(PieChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler, ArrayList<Bitmap> bitMaps, LabelPointCache labelPointCache) {
super(chart, animator, viewPortHandler);
this.bitMaps = bitMaps;
this.labelPointCache = labelPointCache;
mEntryLabelsPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mEntryLabelsPaint.setColor(Color.BLACK);
mEntryLabelsPaint.setTextAlign(Paint.Align.CENTER);
mEntryLabelsPaint.setTextSize(Utils.convertDpToPixel(13f));
}
//snip, then:
c.drawCircle(labelPtx, labelPty, bitMaps.get(j).getWidth(), paint);
c.drawBitmap(bitMaps.get(j), labelPtx-bitMaps.get(j).getWidth()/2f, labelPty-bitMaps.get(j).getHeight()/2, null );
//then:
float entryX = dataSet.getEntry().getX();
float entryY = dataSet.getEntry().getY();
labelPositionCache.add(entryX, entryY, labelPtx, labelPty);
Now create a class that implements that interface, say, MyOnChartGestureListener
. Now set it to your chart:
PieChartRenderer myRenderer = new ImagePieChartRenderer(mChart, mChart.getAnimator(), mChart.getViewPortHandler(), bitmaps, labelPositionCache);
mChart.setOnChartGestureListener(new MyOnChartGestureListener(mChart, labelPointCache));
Then you'll need to override the method for the motion event you want to listen for. Let's say you want to listen for single taps
The following snippet shows how to transform from a raw tapped x
and y
to a chart xValue and yValue on the chart:
@Override
public void onChartSingleTapped(MotionEvent me) {
float tappedX = me.getX();
float tappedY = me.getY();
MPPointD point = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(tappedX, tappedY);
Log.d(TAG, "tapped at: " + point.x + "," + point.y);
}
You will need some way to check that the point.x
and point.y
are within your circle or bitmaps. Then you can check whether the point.x
and point.y
from the gesture fall inside it, like this:
if (labelPositionCache.contains(point.x, point.y)) {
float entryX = labelPositionCache.getEntryXForLabelX(point.x);
float entryY = labelPositionCache.getEntryYForLabelY(point.y);
Highlight highlight = mChart.getHighlightByTouchPoint(entryX, entryY);
mChart.highlightValues(new Highlight [] { highlight });
}
Upvotes: 1