Draw Android Clock 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.androidclockpiechart.ClockPieView" android:id="@+id/pie_view" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" /> <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" /> </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.androidclockpiechart; import java.util.ArrayList; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class MainActivity extends Activity { ClockPieView clockPieView; Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); clockPieView = (ClockPieView)findViewById(R.id.pie_view); button = (Button)findViewById(R.id.pie_button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { randomSet(clockPieView); } }); set(clockPieView); } private void randomSet(ClockPieView clockPieView){ ArrayList<ClockPieHelper> clockPieHelperArrayList = new ArrayList<ClockPieHelper>(); for(int i=0; i<20; i++){ int startHour = (int)(24*Math.random()); int startMin = (int)(60*Math.random()); int duration = (int)(50*Math.random()); clockPieHelperArrayList.add(new ClockPieHelper(startHour,startMin,0,startHour,startMin+duration,0)); } clockPieView.setDate(clockPieHelperArrayList); } private void set(ClockPieView clockPieView){ ArrayList<ClockPieHelper> clockPieHelperArrayList = new ArrayList<ClockPieHelper>(); clockPieHelperArrayList.add(new ClockPieHelper(1,50,2,30)); clockPieHelperArrayList.add(new ClockPieHelper(6,50,8,30)); clockPieView.setDate(clockPieHelperArrayList); } }
ClockPieHelper.java
package com.example.androidclockpiechart; public class ClockPieHelper { private float start; private float end; private float targetStart; private float targetEnd; int velocity = 5; ClockPieHelper(float startDegree, float endDegree, ClockPieHelper targetPie){ start = startDegree; end = endDegree; targetStart = targetPie.getStart(); targetEnd = targetPie.getEnd(); } public ClockPieHelper(int startHour, int startMin, int endHour, int endMin){ start = 270+startHour*15+startMin*15/60; end = 270+endHour*15+endMin*15/60; while(end<start){ end+=360; } } public ClockPieHelper(int startHour, int startMin, int startSec, int endHour, int endMin, int endSec){ start = 270+startHour*15+startMin*15/60+startSec*15/3600; end = 270+endHour*15+endMin*15/60+endSec*15/3600; while(end<start){ end+=360; } } ClockPieHelper setTarget(float targetStart,float targetEnd){ this.targetStart = targetStart; this.targetEnd = targetEnd; return this; } ClockPieHelper setTarget(ClockPieHelper targetPie){ targetStart = targetPie.getStart(); targetEnd = targetPie.getEnd(); return this; } boolean isAtRest(){ return (start==targetStart)&&(end==targetEnd); } void update(){ start = updateSelf(start, targetStart, velocity); end = updateSelf(end, targetEnd, velocity); } public float getSweep(){ return end-start; } public float getStart(){ return start; } public float getEnd(){ return end; } 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; } }
ClockPieView.java
package com.example.androidclockpiechart; 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.View; import java.util.ArrayList; public class ClockPieView extends View { private Paint textPaint; private Paint redPaint; private Paint linePaint; private Paint whitePaint; private int mViewWidth; private int mViewHeight; private int textSize; private int pieRadius; private Point pieCenterPoint; private Point tempPoint; private Point tempPointRight; private int lineLength; private float leftTextWidth; private float rightTextWidth; private float topTextHeight; private int lineThickness; private RectF cirRect; private Rect textRect; private ArrayList<ClockPieHelper> pieArrayList = new ArrayList<ClockPieHelper>(); private final int TEXT_COLOR = Color.parseColor("#9B9A9B"); private final int GRAY_COLOR = Color.parseColor("#D4D3D4"); private final int RED_COLOR = Color.argb(50, 255, 0, 51); private Runnable animator = new Runnable() { @Override public void run() { boolean needNewFrame = false; for(ClockPieHelper pie : pieArrayList){ pie.update(); if(!pie.isAtRest()){ needNewFrame = true; } } if (needNewFrame) { postDelayed(this, 10); } invalidate(); } }; public ClockPieView(Context context){ this(context,null); } public ClockPieView(Context context, AttributeSet attrs){ super(context, attrs); textSize = MyUtils.sp2px(context, 15); lineThickness = MyUtils.dip2px(context, 1); lineLength = MyUtils.dip2px(context, 10); textPaint = new Paint(); textPaint.setAntiAlias(true); textPaint.setColor(TEXT_COLOR); textPaint.setTextSize(textSize); textPaint.setTextAlign(Paint.Align.CENTER); Paint.FontMetrics fm = new Paint.FontMetrics(); textPaint.getFontMetrics(fm); textRect = new Rect(); textPaint.getTextBounds("18",0,1,textRect); redPaint = new Paint(textPaint); redPaint.setColor(RED_COLOR); linePaint = new Paint(textPaint); linePaint.setColor(GRAY_COLOR); linePaint.setStrokeWidth(lineThickness); whitePaint = new Paint(linePaint); whitePaint.setColor(Color.WHITE); tempPoint = new Point(); pieCenterPoint = new Point(); tempPointRight = new Point(); cirRect = new RectF(); leftTextWidth = textPaint.measureText("18"); rightTextWidth = textPaint.measureText("6"); topTextHeight = textRect.height(); } public void setDate(ArrayList<ClockPieHelper> helperList){ if(helperList != null && !helperList.isEmpty()){ int pieSize = pieArrayList.isEmpty()? 0:pieArrayList.size(); for(int i=0;i<helperList.size();i++){ if(i>pieSize-1){ // float mStart = helperList.get(i).getStart(); pieArrayList.add(new ClockPieHelper(0,0,helperList.get(i))); }else{ pieArrayList.set(i, pieArrayList.get(i).setTarget(helperList.get(i))); } } int temp = pieArrayList.size() - helperList.size(); for(int i=0; i<temp; i++){ pieArrayList.remove(pieArrayList.size()-1); } }else { pieArrayList.clear(); } removeCallbacks(animator); post(animator); } @Override protected void onDraw(Canvas canvas) { drawBackground(canvas); if(pieArrayList != null){ for(ClockPieHelper helper:pieArrayList){ canvas.drawArc(cirRect,helper.getStart(),helper.getSweep(),true,redPaint); } } } private void drawBackground(Canvas canvas){ for(int i=0; i<12; i++){ tempPoint.set(pieCenterPoint.x-(int)(Math.sin(Math.PI / 12 * i)*(pieRadius+lineLength)), pieCenterPoint.y-(int)(Math.cos(Math.PI / 12 * i)*(pieRadius+lineLength))); tempPointRight.set( pieCenterPoint.x+(int)(Math.sin(Math.PI / 12 * i)*(pieRadius+lineLength)), pieCenterPoint.y+(int)(Math.cos(Math.PI / 12 * i)*(pieRadius+lineLength))); canvas.drawLine(tempPoint.x,tempPoint.y,tempPointRight.x,tempPointRight.y,linePaint); } canvas.drawCircle(pieCenterPoint.x,pieCenterPoint.y,pieRadius+lineLength/2, whitePaint); canvas.drawCircle(pieCenterPoint.x,pieCenterPoint.y,pieRadius+lineThickness,linePaint); canvas.drawCircle(pieCenterPoint.x,pieCenterPoint.y,pieRadius,whitePaint); canvas.drawText("0", pieCenterPoint.x, topTextHeight, textPaint); canvas.drawText("12",pieCenterPoint.x,mViewHeight,textPaint); canvas.drawText("18",leftTextWidth/2, pieCenterPoint.y+textRect.height()/2,textPaint); canvas.drawText("6",mViewWidth-rightTextWidth/2, pieCenterPoint.y+textRect.height()/2,textPaint); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mViewWidth = measureWidth(widthMeasureSpec); mViewHeight = measureHeight(heightMeasureSpec); pieRadius = mViewWidth/2-lineLength*2-(int)(textPaint.measureText("18")/2); pieCenterPoint.set(mViewWidth/2-(int)rightTextWidth/2+(int)leftTextWidth/2, mViewHeight/2+textSize/2-(int)(textPaint.measureText("18")/2)); cirRect.set(pieCenterPoint.x-pieRadius, pieCenterPoint.y-pieRadius, pieCenterPoint.x+pieRadius, pieCenterPoint.y+pieRadius); 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