package com.handstudio.android.hzgrapherlib.graphview; import java.util.List; import android.R; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Region; import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; import com.handstudio.android.hzgrapherlib.canvas.GraphCanvasWrapper; import com.handstudio.android.hzgrapherlib.error.ErrorCode; import com.handstudio.android.hzgrapherlib.error.ErrorDetector; import com.handstudio.android.hzgrapherlib.util.Converter; import com.handstudio.android.hzgrapherlib.vo.GraphNameBox; import com.handstudio.android.hzgrapherlib.vo.circlegraph.CircleGraph; import com.handstudio.android.hzgrapherlib.vo.circlegraph.CircleGraphVO; public class CircleGraphView extends SurfaceView implements Callback{ public static final String TAG = "CircleGraphView"; private SurfaceHolder mHolder; private DrawThread mDrawThread; private CircleGraphVO mCircleGraphVO = null; //Constructor public CircleGraphView(Context context, CircleGraphVO vo) { super(context); mCircleGraphVO = vo; initView(context, vo); } private void initView(Context context, CircleGraphVO vo) { ErrorCode ec = ErrorDetector.checkGraphObject(vo); ec.printError(); mHolder = getHolder(); mHolder.addCallback(this); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {} @Override public void surfaceCreated(SurfaceHolder holder) { if(mDrawThread == null){ mDrawThread = new DrawThread(mHolder, getContext()); mDrawThread.start(); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { if(mDrawThread != null){ mDrawThread.setRunFlag(false); mDrawThread = null; } } private static final Object touchLock = new Object(); // touch synchronize @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); if(mDrawThread == null ){ return false; } if(action == MotionEvent.ACTION_DOWN){ synchronized (touchLock) { } return true; }else if(action == MotionEvent.ACTION_MOVE){ synchronized (touchLock) { } return true; }else if(action == MotionEvent.ACTION_UP){ synchronized (touchLock) { } return true; } return super.onTouchEvent(event); } class DrawThread extends Thread{ private boolean isRun = true; private boolean isAnimation = false; private boolean isDirty = true; private int radius; public int mCircleGraphIndex =0; private int height = getHeight(); private int width = getWidth(); private float total; private float mSweep[]; private long animStartTime = -1; private float startAngle = 0; private float anim = 0.0f; public Matrix matrix ; //chartCenter private PointF chartCenter; private Bitmap bitmap ; private Bitmap bg = null; private Bitmap bm = null; public DrawThread(SurfaceHolder holder, Context context) { mHolder = holder; int bgResource = mCircleGraphVO.getGraphBG(); if(bgResource != -1){ Bitmap tempBg = BitmapFactory.decodeResource(getResources(), bgResource); bg = Bitmap.createScaledBitmap(tempBg, width, height, true); tempBg.recycle(); } matrix = new Matrix(); total = calculateDataTotal(mCircleGraphVO.getArrGraph()); mSweep = new float[mCircleGraphVO.getArrGraph().size()]; bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); bitmap = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888); radius = (width -mCircleGraphVO.getPaddingLeft() - mCircleGraphVO.getPaddingRight())/2; chartCenter = new PointF(width/2 + mCircleGraphVO.getCenterX(), height/2 + mCircleGraphVO.getCenterY()); } public void setRunFlag(boolean bool){ isRun = bool; } @Override public void run() { super.run(); Canvas canvas = null; GraphCanvasWrapper graphCanvasWrapper = null; isAnimation(); animStartTime = System.currentTimeMillis(); mCircleGraphVO.setArrGraph(calculateData(mCircleGraphVO.getArrGraph())); if(width < height){ radius = (width - (mCircleGraphVO.getPaddingLeft() + mCircleGraphVO.getPaddingRight()))/2; }else{ radius = (height -(mCircleGraphVO.getPaddingTop() + mCircleGraphVO.getPaddingBottom()))/2; } while(isRun){ //draw only on dirty mode if(!isDirty){ try { Thread.sleep(100); } catch (InterruptedException e1) { e1.printStackTrace(); } continue; } canvas = mHolder.lockCanvas(); if( canvas != null){ canvas.drawColor(Color.WHITE); if(bg != null){ canvas.drawBitmap(bg, 0, 0, null); } } graphCanvasWrapper = new GraphCanvasWrapper(canvas, width, height, mCircleGraphVO.getPaddingLeft(), mCircleGraphVO.getPaddingBottom()); calcTimePass(); synchronized(mHolder){ synchronized (touchLock) { try { // name box; drawGraphName(canvas); // all draw circle drawCircle(canvas ,graphCanvasWrapper, total); // all draw Line drawLine(canvas , mCircleGraphVO.getArrGraph()); // all draw Text drawText(canvas , mCircleGraphVO.getArrGraph()); // draw pie chart innerCircle(canvas); } catch (Exception e) { e.printStackTrace(); } finally { if(graphCanvasWrapper.getCanvas() != null){ mHolder.unlockCanvasAndPost(graphCanvasWrapper.getCanvas()); } } } } } } private Bitmap makeDst() { Canvas c = new Canvas(bm); Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setColor(0xFFFFCC44); c.drawCircle(chartCenter.x, chartCenter.y, radius/2, p); return bm; } private void innerCircle(Canvas canvas){ if(mCircleGraphVO.isPieChart()){ if(bg != null){ Bitmap mask = makeDst(); Canvas c = new Canvas(bitmap); c.drawBitmap(bg, 0, 0, null); Paint paint = new Paint(); paint.setFilterBitmap(false); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN) ); c.drawBitmap(mask, 0, 0, paint); canvas.drawBitmap(bitmap, 0, 0, null); }else{ if(canvas != null){ Paint paint = setPaint(Color.WHITE); canvas.drawCircle(chartCenter.x, chartCenter.y, radius/2, paint); } } } } private void drawCircle(Canvas canvas , GraphCanvasWrapper graphCanvasWrapper,float total) { for (int i = 0; i < mCircleGraphVO.getArrGraph().size(); i++) { this.mCircleGraphIndex = i; Paint paint = setPaint(mCircleGraphVO.getArrGraph().get(i).getColor()); //draw rectF RectF mBigOval = new RectF(chartCenter.x-radius, chartCenter.y-radius, chartCenter.x+radius, chartCenter.y+radius); if(isAnimation){ //draw circle calculateDrawTime(i); graphCanvasWrapper.drawArc(mBigOval, startAngle, mSweep[i], true, paint); }else{ graphCanvasWrapper.drawArc(mBigOval, startAngle, mCircleGraphVO.getArrGraph().get(i).getAngleDegree(), true, paint); } startAngle = startAngle + mCircleGraphVO.getArrGraph().get(i).getAngleDegree(); } } private void calculateDrawTime(int i) { mSweep[i] = mCircleGraphVO.getArrGraph().get(i).getAngleDegree() * anim; } private void drawText(Canvas canvas , List<CircleGraph> circleGraphList) { if(anim == 1){ PointF dot = new PointF(chartCenter.x, chartCenter.y); float preDegree =0; for (CircleGraph circleGraph : circleGraphList) { Paint textPaint = setTextPaint(mCircleGraphVO.getTextColor() , mCircleGraphVO.getTextSize()); float radAngle = (float) (Converter.DegreeToRadian((float) preDegree + (circleGraph.getAngleDegree()/2))); // use radian preDegree = preDegree + circleGraph.getAngleDegree(); float rate = getCircleRate(circleGraph.getAngleArr()); String rateText = changeRateText(rate); PointF rotateDot = getRotatePoint(dot, radAngle,textPaint,rateText); canvas.drawText(rateText, rotateDot.x, rotateDot.y, textPaint); } } } private void drawLine(Canvas canvas , List<CircleGraph> circleGraphList) { if(anim == 1){ PointF dot = new PointF(chartCenter.x, chartCenter.y); float preDegree =0; for (CircleGraph circleGraph : circleGraphList) { Paint linePaint = setLinePaint(mCircleGraphVO.getLineColor()); float radAngle = (float) (Converter.DegreeToRadian((float) preDegree + (circleGraph.getAngleDegree()))); // use radian preDegree = preDegree + circleGraph.getAngleDegree(); PointF rotateDot = getLineRotatePoint(dot, radAngle); canvas.drawLine(chartCenter.x, chartCenter.y, rotateDot.x, rotateDot.y, linePaint); } } } private String changeRateText(float rate){ if(String.valueOf(rate).substring(3,4).equals("0")){ return String.valueOf((int) rate+"%"); }else{ String rateText = String.valueOf(Math.round(rate*10)*0.1); if(rateText.length() > 5){ return (String.valueOf(Math.round(rate*10)*0.1)).substring(0,4)+"%"; }else{ return (String.valueOf(Math.round(rate*10)*0.1))+"%"; } } } private float getCircleRate(float angleArr){ return (angleArr/total * 100); } /** * calc rotated point * @param dot * @param radAngle */ private PointF getRotatePoint(PointF dot, float radAngle, Paint textPaint, String rateText) { PointF rotateDot = new PointF(); Rect rect = new Rect(); textPaint.getTextBounds(rateText, 0, rateText.length(), rect); rotateDot.x = (float) (chartCenter.x + radius/1.4 *Math.cos(radAngle) - rect.width()/2); rotateDot.y = (float) (chartCenter.y + radius/1.4 *Math.sin(radAngle) + rect.height()/2); return rotateDot; } private PointF getLineRotatePoint(PointF dot, float radAngle) { PointF rotateDot = new PointF(); rotateDot.x = (float) (chartCenter.x + radius *Math.cos(radAngle)); rotateDot.y = (float) (chartCenter.y + radius *Math.sin(radAngle)); return rotateDot; } private void calcTimePass(){ if(isAnimation){ long curTime = System.currentTimeMillis(); long gapTime = (long) Math.ceil(curTime - animStartTime); long animDuration = mCircleGraphVO.getAnimation().getDuration(); if(gapTime > animDuration){ gapTime = animDuration; isDirty = false; } anim = (float)gapTime/(float)animDuration; }else{ isDirty = false; } } /** * check graph line animation */ private void isAnimation() { if(mCircleGraphVO.getAnimation() != null){ isAnimation = true; }else{ isAnimation = false; } } private Paint setPaint(int color) { Paint paint = new Paint(); paint.setFlags(Paint.ANTI_ALIAS_FLAG); paint.setAntiAlias(true); //text anti alias paint.setFilterBitmap(true); // bitmap anti alias paint.setColor(color); paint.setStrokeWidth(3); paint.setTextSize(20); return paint; } private Paint setTextPaint(int color , int textSize) { Paint paint = new Paint(); Rect rect = new Rect(); paint.setFlags(Paint.ANTI_ALIAS_FLAG); paint.setAntiAlias(true); //text anti alias paint.setFilterBitmap(true); // bitmap anti alias paint.setColor(color); paint.setStrokeWidth(3); paint.setTextSize(textSize); return paint; } private Paint setLinePaint(int color) { Paint paint = new Paint(); paint.setFlags(Paint.ANTI_ALIAS_FLAG); paint.setAntiAlias(true); //text anti alias paint.setFilterBitmap(true); // bitmap anti alias paint.setColor(color); paint.setStrokeWidth(2); return paint; } private float calculateDataTotal(List<CircleGraph> circleGraphList) { float total = 0; for (CircleGraph circleGraph : circleGraphList) { total += circleGraph.getAngleArr(); } return total; } private List<CircleGraph> calculateData(List<CircleGraph> circleGraphList) { float total = 0; total = calculateDataTotal(circleGraphList); for (CircleGraph circleGraph : circleGraphList) { circleGraph.setAngleDegree((360 * (circleGraph.getAngleArr() / total))); } return circleGraphList; } private void drawGraphName(Canvas canvas) { GraphNameBox gnb = mCircleGraphVO.getGraphNameBox(); if(gnb != null){ int nameboxWidth = 0; int nameboxHeight = 0; int nameboxIconWidth = gnb.getNameboxIconWidth(); int nameboxIconHeight = gnb.getNameboxIconHeight(); int nameboxMarginTop = gnb.getNameboxMarginTop(); int nameboxMarginRight = gnb.getNameboxMarginRight(); int nameboxPadding = gnb.getNameboxPadding(); int nameboxTextIconMargin = gnb.getNameboxIconMargin(); int nameboxIconMargin = gnb.getNameboxIconMargin(); int nameboxTextSize = gnb.getNameboxTextSize(); int maxTextWidth = 0; int maxTextHeight = 0; Paint nameRextPaint = new Paint(); nameRextPaint.setFlags(Paint.ANTI_ALIAS_FLAG); nameRextPaint.setAntiAlias(true); //text anti alias nameRextPaint.setFilterBitmap(true); // bitmap anti alias nameRextPaint.setColor(gnb.getNameboxColor()); nameRextPaint.setStrokeWidth(3); nameRextPaint.setStyle(Style.STROKE); Paint pIcon = new Paint(); pIcon.setFlags(Paint.ANTI_ALIAS_FLAG); pIcon.setAntiAlias(true); //text anti alias pIcon.setFilterBitmap(true); // bitmap anti alias pIcon.setColor(Color.BLUE); pIcon.setStrokeWidth(3); pIcon.setStyle(Style.FILL_AND_STROKE); Paint pNameText = new Paint(); pNameText.setFlags(Paint.ANTI_ALIAS_FLAG); pNameText.setAntiAlias(true); //text anti alias pNameText.setTextSize(nameboxTextSize); pNameText.setColor(gnb.getNameboxTextColor()); int graphSize = mCircleGraphVO.getArrGraph().size(); for (int i = 0; i < graphSize; i++) { String text = mCircleGraphVO.getArrGraph().get(i).getName(); Rect rect = new Rect(); pNameText.getTextBounds(text, 0, text.length(), rect); if(rect.width() > maxTextWidth){ maxTextWidth = rect.width(); maxTextHeight = rect.height(); } mCircleGraphVO.getArrGraph().get(i).getName(); } mCircleGraphVO.getArrGraph().get(0).getName(); nameboxWidth = 1 * maxTextWidth + nameboxTextIconMargin + nameboxIconWidth; int maxCellHight = maxTextHeight; if(nameboxIconHeight > maxTextHeight){ maxCellHight = nameboxIconHeight; } nameboxHeight = graphSize * maxCellHight + (graphSize-1) * nameboxIconMargin; if(canvas != null){ canvas.drawRect(width - (nameboxMarginRight + nameboxWidth) - nameboxPadding*2, nameboxMarginTop, width - nameboxMarginRight, nameboxMarginTop + nameboxHeight + nameboxPadding*2, nameRextPaint); for (int i = 0; i < graphSize; i++) { pIcon.setColor(mCircleGraphVO.getArrGraph().get(i).getColor()); canvas.drawRect(width - (nameboxMarginRight + nameboxWidth) - nameboxPadding, nameboxMarginTop + (maxCellHight * i) + nameboxPadding + (nameboxIconMargin * i), width - (nameboxMarginRight + maxTextWidth) - nameboxPadding - nameboxTextIconMargin, nameboxMarginTop + maxCellHight * (i+1) + nameboxPadding + nameboxIconMargin * i, pIcon); String text = mCircleGraphVO.getArrGraph().get(i).getName(); canvas.drawText(text, width - (nameboxMarginRight + maxTextWidth) - nameboxPadding, nameboxMarginTop + maxTextHeight/2 + maxCellHight * i + maxCellHight/2 + nameboxPadding + nameboxIconMargin * i, pNameText); } } } } } }