This demo demonstrates the usage of Android Graphics API to create simple 2D Pie chart and handling of touch event on the Pie-chart. This demo draws the 2D Pie chart based on the given data inputs, colors. Data legends are shown at the bottom of the chart as shown in the below image. Also, it handles the touch events and identifies the particular pie slice whether touched or not to support drilled down features.
Let me explain the application in more details, the steps to draw the Pie-Chart.
1. Create a simple view layout containing one Image View. (activity_main.xml)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="@+id/image_placeholder"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:adjustViewBounds="false"
android:background="#000000"
android:scaleType="center" />
</RelativeLayout>
2. Create a main activity which contains the above layout (MainActivity.java)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//main layout
setContentView(R.layout.activity_main);
//pie chart parameters
int data_values[] = { 20,10,25,5,15,25};
int color_values[] = {Color.MAGENTA, Color.RED, Color.GREEN,Color.BLUE,Color.YELLOW,Color.CYAN};
String itemnames[] = { "item 1", "item 2", "item 3", "item 4", "item 5","item 6"};
//get the imageview
ImageView imgView = (ImageView ) findViewById(R.id.image_placeholder);
//create pie chart Drawable and set it to ImageView
PieChart pieChart = new PieChart(this, imgView, itemnames, data_values, color_values);
imgView.setImageDrawable(pieChart);
}
Create the required pie-chart parameters like data values, names, colors and create PieChart drawable Shape instance and assign to Image View.
3. PieChart Drawable Shape
Create a PieChart Class by extending Drawable Class and implementation for OnTouchLister.
public class PieChart extends Drawable implements OnTouchListener {....}
Add the constructor with the required parameters for to draw the Pie Chart.
public PieChart(Context c, View v, String[] data_names, int[] data_values, int[] color_values) {
context = c;
view = v;
this.data_values = data_values;
this.color_values = color_values;
this.data_names = data_names;
paint = new Paint();
view.setOnTouchListener(this);
}
And Now, Overwrite the draw() method with the below steps
- Get the screen width & height
- Calculate the chart area in Rectangle area
- Calculate the start angle and Sweep angle for each data item
- And finally Draw the Arc, legend box, legend text with corresponding paint.
public void draw(Canvas canvas) {
// TODO Auto-generated method stub
//screen width & height
int view_w = view.getWidth();
int view_h = view.getHeight();
//chart area rectangle
arc_bounds = new RectF(
left_edge,
top_edge,
right_edge,
bottom_edge
);
//sum of data values
for (int datum : data_values)
value_sum += datum;
float startAngle = 0;
int i = 0;
for (int datum : data_values) {
if (datum == 0) continue;
//calculate start & end angle for each data value
float endAngle = value_sum == 0 ? 0 : 360 * datum / (float) value_sum;
float newStartAngle = startAngle + endAngle;
int flickr_pink = color_values[i % color_values.length];
paint.setColor(flickr_pink);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(0.5f);
//gradient fill color
LinearGradient linearGradient = new LinearGradient(arc_bounds.left, arc_bounds.top, arc_bounds.right,arc_bounds.bottom, flickr_pink, Color.WHITE, Shader.TileMode.CLAMP);
paint.setShader(linearGradient);
//draw fill arc
canvas.drawArc(arc_bounds, startAngle, endAngle, true, paint);
Paint linePaint = new Paint();
linePaint.setAntiAlias(true);
linePaint.setStyle(Paint.Style.STROKE);
linePaint.setStrokeJoin(Join.ROUND);
linePaint.setStrokeCap(Cap.ROUND);
linePaint.setStrokeWidth(0.5f);
linePaint.setColor(Color.BLACK);
//draw border arc
canvas.drawArc(arc_bounds, startAngle, endAngle, true, linePaint);
int barStartX = 50;
int barWidth = 20;
int barStartY = view_h-padding_bottom+(i-1)*2*barWidth;
Rect barRect = new Rect(barStartX,barStartY,barStartX+barWidth,barStartY+barWidth);
//draw legend box
canvas.drawRect(barRect, paint);
canvas.drawRect(barRect,linePaint);
Paint textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(30);
//draw legend text
canvas.drawText(data_names[i], barStartX+2*barWidth, barStartY+barWidth, textPaint);
startAngle = newStartAngle;
i++;
}
}
4.. Adding Touch Listener
Now, Implement the onTouchListener with below steps,
- Get mouse down event
- Calculate the click angle
- Check the condition with the start angle and Sweep angle for each data item
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
//mouse down event
if( event.getAction() == MotionEvent.ACTION_DOWN)
{
double clickAngle;
//relative x & y position
float xPos = event.getX() - arc_bounds.centerX();
float yPos = event.getY() - arc_bounds.centerY();
//calcuate the click angle
clickAngle = Math.atan2(yPos,xPos) * 180 / Math.PI;
if(clickAngle < 0)
clickAngle = 360 + clickAngle;
float startAngle = 0;
int itemIndex = 0;
for (int datum : data_values) {
if (datum == 0) continue;
float endAngle = value_sum == 0 ? 0 : 360 * datum / (float) value_sum;
float newStartAngle = startAngle + endAngle;
//check the condition of start angle & end angle of data item.
if(arc_bounds.contains(event.getX(),event.getY()) && clickAngle > startAngle && clickAngle < newStartAngle)
{
Toast.makeText(context, data_names[itemIndex] + " is clicked!!", Toast.LENGTH_LONG).show();
Log.d(TAG,"pie item is clicked-->" + data_names[itemIndex]);
break;
}
itemIndex++;
startAngle = newStartAngle;
}
}
return false;
}
the below image shows the Toast message when the particular pie slice is touched. Based on this event, drilled down feature will be supported like starting activity or reloading the view with different data values.