Draw Android pie Chart With Animation.
—> AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.barchart" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="22" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
IN Layout Folder
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff"> <view android:layout_width="fill_parent" android:layout_height="wrap_content" class="com.example.androidpiechart.PieView" android:id="@+id/pie_view" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_margin="10dp" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Random" android:id="@+id/pie_button" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="New Text" android:id="@+id/textView" android:layout_above="@+id/pie_button" android:layout_centerHorizontal="true" /> </RelativeLayout>
MyUtils.java
package com.example.barchart; import android.content.Context; public class MyUtils { public static int dip2px(Context context, float dipValue){ final float scale = context.getResources().getDisplayMetrics().density; return (int)(dipValue * scale + 0.5f); } public static int px2dip(Context context, float pxValue){ final float scale = context.getResources().getDisplayMetrics().density; return (int)(pxValue / scale + 0.5f); } public static int sp2px(Context context, float spValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } }
MainActivity.java
package com.example.androidpiechart; import java.util.ArrayList; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity { private TextView textView; PieView pieView; Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView)findViewById(R.id.textView); pieView = (PieView)findViewById(R.id.pie_view); button = (Button)findViewById(R.id.pie_button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { randomSet(pieView); } }); set(pieView); } private void randomSet(PieView pieView){ ArrayList<PieHelper> pieHelperArrayList = new ArrayList<PieHelper>(); ArrayList<Integer> intList = new ArrayList<Integer>(); int totalNum = (int) (5*Math.random()) + 5; int totalInt = 0; for(int i=0; i<totalNum; i++){ int ranInt = (int)(Math.random()*10)+1; intList.add(ranInt); totalInt += ranInt; } for(int i=0; i<totalNum; i++){ pieHelperArrayList.add(new PieHelper(100f*intList.get(i)/totalInt)); } pieView.selectedPie(PieView.NO_SELECTED_INDEX); pieView.showPercentLabel(true); pieView.setDate(pieHelperArrayList); } private void set(PieView pieView){ ArrayList<PieHelper> pieHelperArrayList = new ArrayList<PieHelper>(); pieHelperArrayList.add(new PieHelper(20, Color.BLACK)); pieHelperArrayList.add(new PieHelper(6)); pieHelperArrayList.add(new PieHelper(30)); pieHelperArrayList.add(new PieHelper(12)); pieHelperArrayList.add(new PieHelper(32)); pieView.setDate(pieHelperArrayList); pieView.setOnPieClickListener(new PieView.OnPieClickListener() { @Override public void onPieClick(int index) { if(index != PieView.NO_SELECTED_INDEX) { textView.setText(index + " selected"); }else{ textView.setText("No selected pie"); } } }); pieView.selectedPie(2); } }
PieHelper.java
package com.example.androidpiechart; public class PieHelper { private float startDegree; private float endDegree; private float targetStartDegree; private float targetEndDegree; private String title; private int color; private float sweepDegree; int velocity = 5; public PieHelper(float percent){ this(percent, null, 0); } public PieHelper(float percent, int color){ this(percent, null, color); } PieHelper(float percent, String title){ this(percent, title, 0); } PieHelper(float percent, String title, int color){ this.sweepDegree = percent * 360 / 100; this.title = title; this.color = color; } PieHelper(float startDegree, float endDegree, PieHelper targetPie){ this.startDegree = startDegree; this.endDegree = endDegree; targetStartDegree = targetPie.getStartDegree(); targetEndDegree = targetPie.getEndDegree(); this.sweepDegree = targetPie.getSweep(); this.title = targetPie.getTitle(); this.color = targetPie.getColor(); } PieHelper setTarget(PieHelper targetPie){ this.targetStartDegree = targetPie.getStartDegree(); this.targetEndDegree = targetPie.getEndDegree(); this.title = targetPie.getTitle(); this.color = targetPie.getColor(); this.sweepDegree = targetPie.getSweep(); return this; } void setDegree(float startDegree, float endDegree){ this.startDegree = startDegree; this.endDegree = endDegree; } boolean isColorSetted(){return color != 0;} boolean isAtRest(){ return (startDegree==targetStartDegree)&&(endDegree==targetEndDegree); } void update(){ this.startDegree = updateSelf(startDegree, targetStartDegree, velocity); this.endDegree = updateSelf(endDegree, targetEndDegree, velocity); this.sweepDegree = endDegree - startDegree; } String getPercentStr(){ float percent = sweepDegree / 360 * 100; return String.valueOf((int)percent) + "%"; } public int getColor(){ return color; } public String getTitle(){ return title; } public float getSweep(){ return sweepDegree; } public float getStartDegree(){ return startDegree; } public float getEndDegree(){ return endDegree; } private float updateSelf(float origin, float target, int velocity){ if (origin < target) { origin += velocity; } else if (origin > target){ origin-= velocity; } if(Math.abs(target-origin)<velocity){ origin = target; } return origin; } }
PieView.java
package com.example.androidpiechart; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import java.util.ArrayList; public class PieView extends View { public interface OnPieClickListener{ void onPieClick(int index); } private Paint cirPaint; private Paint whiteLinePaint; private Point pieCenterPoint; private Paint textPaint; private RectF cirRect; private RectF cirSelectedRect; private int mViewWidth; private int mViewHeight; private int margin; private int pieRadius; private OnPieClickListener onPieClickListener; private ArrayList<PieHelper> pieHelperList; private int selectedIndex = NO_SELECTED_INDEX; private boolean showPercentLabel = true; public static final int NO_SELECTED_INDEX = -999; private final int[] DEFAULT_COLOR_LIST = {Color.parseColor("#33B5E5"), Color.parseColor("#AA66CC"), Color.parseColor("#99CC00"), Color.parseColor("#FFBB33"), Color.parseColor("#FF4444")}; private Runnable animator = new Runnable() { @Override public void run() { boolean needNewFrame = false; for(PieHelper pie : pieHelperList){ pie.update(); if(!pie.isAtRest()){ needNewFrame = true; } } if (needNewFrame) { postDelayed(this, 10); } invalidate(); } }; public PieView(Context context){ this(context,null); } public PieView(Context context, AttributeSet attrs){ super(context, attrs); pieHelperList = new ArrayList<PieHelper>(); cirPaint = new Paint(); cirPaint.setAntiAlias(true); cirPaint.setColor(Color.GRAY); whiteLinePaint = new Paint(cirPaint); whiteLinePaint.setColor(Color.WHITE); whiteLinePaint.setStrokeWidth(2f); textPaint = new Paint(); textPaint.setAntiAlias(true); textPaint.setColor(Color.WHITE); textPaint.setTextSize(MyUtils.sp2px(getContext(), 13)); textPaint.setStrokeWidth(5); textPaint.setTextAlign(Paint.Align.CENTER); pieCenterPoint = new Point(); cirRect = new RectF(); cirSelectedRect = new RectF(); } public void showPercentLabel(boolean show){ showPercentLabel = show; postInvalidate(); } public void setOnPieClickListener(OnPieClickListener listener){ onPieClickListener = listener; } public void setDate(ArrayList<PieHelper> helperList){ initPies(helperList); pieHelperList.clear(); removeSelectedPie(); if(helperList != null && !helperList.isEmpty()){ for(PieHelper pieHelper:helperList){ pieHelperList.add(new PieHelper(pieHelper.getStartDegree(),pieHelper.getStartDegree(),pieHelper)); } }else { pieHelperList.clear(); } removeCallbacks(animator); post(animator); } private void initPies(ArrayList<PieHelper> helperList){ float totalAngel = 270; for(PieHelper pie:helperList){ pie.setDegree(totalAngel, totalAngel + pie.getSweep()); totalAngel += pie.getSweep(); } } public void selectedPie(int index){ selectedIndex = index; if(onPieClickListener!=null) onPieClickListener.onPieClick(index); postInvalidate(); } public void removeSelectedPie(){ selectedIndex = NO_SELECTED_INDEX; if(onPieClickListener!=null) onPieClickListener.onPieClick(NO_SELECTED_INDEX); postInvalidate(); } @Override protected void onDraw(Canvas canvas) { if(pieHelperList.isEmpty()){ return ; } int index = 0; for(PieHelper pieHelper : pieHelperList){ boolean selected = (selectedIndex == index); RectF rect = selected? cirSelectedRect: cirRect; if(pieHelper.isColorSetted()){ cirPaint.setColor(pieHelper.getColor()); }else { cirPaint.setColor(DEFAULT_COLOR_LIST[index%5]); } canvas.drawArc(rect, pieHelper.getStartDegree(), pieHelper.getSweep(), true, cirPaint); drawPercentText(canvas, pieHelper); drawLineBesideCir(canvas, pieHelper.getStartDegree(), selected); drawLineBesideCir(canvas, pieHelper.getEndDegree(), selected); index++; } } private void drawLineBesideCir(Canvas canvas, float angel, boolean selectedCir){ int sth2 = selectedCir? mViewHeight/2 : pieRadius; // Sorry I'm really don't know how to name the variable.. int sth = 1; // And it's if(angel%360 > 180 && angel%360 < 360){ sth = -1; } float lineToX = (float)(mViewHeight/2 + Math.cos(Math.toRadians(-angel)) * sth2); float lineToY = (float)(mViewHeight/2 + sth * Math.abs(Math.sin(Math.toRadians(-angel))) * sth2); canvas.drawLine(pieCenterPoint.x, pieCenterPoint.y, lineToX, lineToY, whiteLinePaint); } private void drawPercentText(Canvas canvas, PieHelper pieHelper){ if(!showPercentLabel) return ; float angel = (pieHelper.getStartDegree() + pieHelper.getEndDegree()) /2; int sth = 1; if(angel%360 > 180 && angel%360 < 360){ sth = -1; } float x = (float)(mViewHeight/2 + Math.cos(Math.toRadians(-angel)) * pieRadius/2); float y = (float)(mViewHeight/2 + sth * Math.abs(Math.sin(Math.toRadians(-angel))) * pieRadius/2); canvas.drawText(pieHelper.getPercentStr(), x, y, textPaint); } private void drawText(Canvas canvas, PieHelper pieHelper){ if(pieHelper.getTitle() == null) return ; float angel = (pieHelper.getStartDegree() + pieHelper.getEndDegree()) /2; int sth = 1; if(angel%360 > 180 && angel%360 < 360){ sth = -1; } float x = (float)(mViewHeight/2 + Math.cos(Math.toRadians(-angel)) * pieRadius/2); float y = (float)(mViewHeight/2 + sth * Math.abs(Math.sin(Math.toRadians(-angel))) * pieRadius/2); canvas.drawText(pieHelper.getTitle(), x, y, textPaint); } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN ||event.getAction() == MotionEvent.ACTION_MOVE){ selectedIndex = findPointAt((int) event.getX(), (int) event.getY()); if(onPieClickListener != null){ onPieClickListener.onPieClick(selectedIndex); } postInvalidate(); } return true; } private int findPointAt(int x, int y){ double degree = Math.atan2(x-pieCenterPoint.x, y-pieCenterPoint.y)* 180 / Math.PI; degree = -(degree-180) + 270; int index = 0; for(PieHelper pieHelper:pieHelperList){ if(degree>=pieHelper.getStartDegree() && degree<=pieHelper.getEndDegree()){ return index; } index++; } return NO_SELECTED_INDEX; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mViewWidth = measureWidth(widthMeasureSpec); mViewHeight = measureHeight(heightMeasureSpec); margin = mViewWidth/16; pieRadius = (mViewWidth)/2-margin; pieCenterPoint.set(pieRadius+margin, pieRadius+margin); cirRect.set(pieCenterPoint.x-pieRadius, pieCenterPoint.y-pieRadius, pieCenterPoint.x+pieRadius, pieCenterPoint.y+pieRadius); cirSelectedRect.set(2, //minor margin for bigger circle 2, mViewWidth-2, mViewHeight-2); setMeasuredDimension(mViewWidth, mViewHeight); } private int measureWidth(int measureSpec){ int preferred = 3; return getMeasurement(measureSpec, preferred); } private int measureHeight(int measureSpec){ int preferred = mViewWidth; return getMeasurement(measureSpec, preferred); } private int getMeasurement(int measureSpec, int preferred){ int specSize = View.MeasureSpec.getSize(measureSpec); int measurement; switch(View.MeasureSpec.getMode(measureSpec)){ case View.MeasureSpec.EXACTLY: measurement = specSize; break; case View.MeasureSpec.AT_MOST: measurement = Math.min(preferred, specSize); break; default: measurement = preferred; break; } return measurement; } }
Leave a Reply