Android: Star path will not fill - android

I have a class that creates a Star using Path this code is as follows:
public Star(int x, int y, int size) {
this.x = x;
this.y = y;
this.size = size;
rotateSpeed = 1.5f;
path = new Path();
path.moveTo(x, y-size);
for(int i = 1; i < 11; i++) {
double radius;
if(i % 2 == 0)
radius = size;
else
radius = size/2.2;
double angleRad = Math.toRadians(360/10) * i;
double cosY = y - (Math.cos(angleRad) * radius);
double tanX = x + (Math.sin(angleRad) * radius);
path.lineTo((int)tanX, (int)cosY);
path.moveTo((int)tanX, (int)cosY);
}
path.setLastPoint(x, y-size);
}
The problem is that it only draws an outline of it and won't actually fill it despite the fact that I explicitly state paint.setStyle(Paint.Style.FILL);
My drawing code is:
public void drawObject(Canvas canvas, boolean antialias) {
Paint p = new Paint();
p.setStyle(Paint.Style.FILL);
p.setAntiAlias(antialias);
p.setColor(color);
canvas.drawPath(path, p);
//Outline
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(5);
p.setColor(Color.BLACK);
canvas.drawPath(path, p);
}
I don't understand what I'm doing wrong. Why won't it fill?

Probably "color" in p.setColor(color); is transparent. Make sure the color is e.g. 0xffRRGGBB
Also path.moveTo((int)tanX, (int)cosY); is not needed. The preceeding lineTo() does the job.

Related

Paint stroke not covering all the side in android

Hi i am creating indicator for doughnut chart but it is covering only three side. Here i added onDraw method.The selected chart index arc should be highlighted with indicator.The indicator should be cover all the four sides.
Code:
#Override
public void draw(Canvas canvas, int x, int y, int width, int height, Paint paint) {
paint.setAntiAlias(true);
paint.setStyle(Style.FILL);
int legendSize = getLegendSize(mRenderer, height / 5, 0);
int left = x;
int top = y;
int right = x + width;
int sLength = mDataset.getItemCount();
double total = 0;
String[] titles = new String[sLength];
for (int i = 0; i < sLength; i++) {
total += mDataset.getValue(i);
titles[i] = mDataset.getCategory(i);
}
if (mRenderer.isFitLegend()) {
legendSize = drawLegend(canvas, mRenderer, titles, left, right, y, width, height, legendSize, paint, true);
}
int bottom = y + height - legendSize;
drawBackground(mRenderer, canvas, x, y, width, height, paint, false, Renderer.NO_COLOR);
float currentAngle = mRenderer.getStartAngle();
float labelCurrentAngle = mRenderer.getStartAngle();
// int mRadius = Math.min(Math.abs(right - left), Math.abs(bottom - top));
// int radius = (int) (500 * 0.35) + 50;
//Log.i("radius++", "" + radius);
int mRadius = Math.min(Math.abs(right - left), Math.abs(bottom - top));
double rCoef = 0.35 * mRenderer.getScale();
double decCoef = 0.2 / sLength;
int radius = (int) (mRadius * rCoef);
if (mCenterX == 0) {
mCenterX = (left + right) / 2;
}
if (mCenterY == 0) {
mCenterY = (bottom + top) / 2;
}
// Hook in clip detection after center has been calculated
mPieMap.setDimensions(radius, mCenterX, mCenterY);
boolean loadPieCfg = !mPieMap.areAllSegmentPresent(sLength);
if (loadPieCfg) {
mPieMap.clearPieSegments();
}
float shortRadius = radius * 0.9f;
float longRadius = radius * 1.1f;
RectF oval = new RectF(mCenterX - radius, mCenterY - radius, mCenterX + radius, mCenterY + radius);
for (int i = 0; i < sLength; i++) {
float value = (float) mDataset.getValue(i);
float angle = (float) (value / total * 360);
int color = mRenderer.getRenderers().get(i).getColor();
if (mRenderer.getRenderers().get(i).isClicked()) {
ClickedArc.CANVAS = canvas;
ClickedArc.CURRENT_ANGLE = currentAngle;
ClickedArc.ANGLE = angle;
ClickedArc.COLOR = color;
ClickedArc.INDEX = mRenderer.getRenderers().get(i).getDataIndex();
} else {
Paint paint3 = new Paint(Paint.ANTI_ALIAS_FLAG);
paint3.setStrokeCap(Paint.Cap.ROUND);
paint3.setStyle(Style.FILL);
int[] colors = {color, color};
RadialGradient gradient3 = new RadialGradient(mCenterX, mCenterY, radius, colors, null, android.graphics.Shader.TileMode.CLAMP);
paint3.setShader(gradient3);
paint3.setAntiAlias(true);
canvas.drawArc(oval, currentAngle, angle, true, paint3);
}
try {
if (mRenderer.getRenderers().get(ClickedArc.INDEX).isClicked()) {
Paint paint3 = new Paint(Paint.ANTI_ALIAS_FLAG);
paint3.setStyle(Style.FILL);
paint3.setColor(Color.WHITE);
Paint shadow = new Paint(Paint.ANTI_ALIAS_FLAG);
int[] colors = {Color.BLACK, Color.BLACK};
RadialGradient gradient3 = new RadialGradient(mCenterX, mCenterY, radius, colors, null, android.graphics.Shader.TileMode.CLAMP);
shadow.setShader(gradient3);
shadow.setAntiAlias(true);
shadow.setColor(Color.BLACK);
shadow.setStrokeWidth(8);
shadow.setDither(true);
shadow.setStyle(Paint.Style.STROKE);
shadow.setStrokeCap(Paint.Cap.BUTT);
shadow.setAntiAlias(true);
int shadowRadius = radius + 2;
RectF shadowOval = new RectF(mCenterX - shadowRadius, mCenterY - shadowRadius, mCenterX + shadowRadius, mCenterY + shadowRadius);
canvas.drawArc(shadowOval, ClickedArc.CURRENT_ANGLE - 1, ClickedArc.ANGLE + 2, true, shadow);
canvas.drawArc(oval, ClickedArc.CURRENT_ANGLE, ClickedArc.ANGLE, true, paint3);
}
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
if (loadPieCfg) {
mRenderer.getRenderers().get(i).setDataIndex(i);
mPieMap.addPieSegment(i, value, currentAngle, angle + currentAngle);
}
currentAngle += angle;
}
radius -= (int) mRadius * decCoef;
shortRadius -= mRadius * decCoef - 2;
List<RectF> prevLabelsBounds = new ArrayList<RectF>();
for (int i = 0; i < sLength; i++) {
float value = (float) mDataset.getValue(i);
float angle = (float) (value / total * 360);
drawLabel(canvas, mDataset.getCategory(i), mRenderer, prevLabelsBounds, mCenterX, mCenterY, shortRadius / 2 + 50, longRadius / 2 + 50,
labelCurrentAngle, angle, left, right, mRenderer.getLabelsColor(), paint, true, mRenderer.getRenderers().get(i));
Point sPoint = mRenderer.getRenderers().get(i).getCenterPoint();
Point ePoint = new Point((int) mRenderer.getRenderers().get(i).getTextWidth(), (int) mRenderer.getRenderers().get(i).getTextHeight());
mPieMap.addLabelSegment(i, value, sPoint, ePoint);
labelCurrentAngle += angle;
}
prevLabelsBounds.clear();
}

Multiple rectangles not drawing on canvas

I am trying to create a family tree like structure in android. I am using canvas to draw rectangle and line for family members names and connecting line.
I am drawing rectangle and line by the following method with the help of link
DrawView.java
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
import android.view.View;
public class DrawView extends View {
Paint paint = new Paint();
float mx, my, mdensity;
Paint mBGPaint, mTXTPaint,mLINEPaint,mBRDPaint;
String text;
public DrawView(Context context, float x, float y, float density, String text) {
super(context);
paint.setColor(Color.RED);
paint.setStrokeWidth(8);
paint.setStyle(Paint.Style.STROKE);
mx = x;
my = y;
mdensity = density;
this.text = text;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
init();
mLINEPaint.setStrokeWidth(8);
//draw rect border
canvas.drawRect(100, 100, 200, 200, mBRDPaint);
// //draw text
canvas.drawText(text, 150, 150, mTXTPaint);
// //draw line
float x = mx+150;
canvas.drawLine(x, 10, x, 100, mLINEPaint);
}
public void init() {
//rectangle background
mBGPaint = new Paint();
mBGPaint.setColor(Color.parseColor("#80123456"));
//your text
mTXTPaint = new Paint();
mTXTPaint.setColor(Color.parseColor("#123456"));
//your line
mLINEPaint = new Paint();
mLINEPaint.setColor(0xFFFF00FF);
//rectangle border
mBRDPaint = new Paint();
mBRDPaint.setStyle(Paint.Style.STROKE);
mBRDPaint.setStrokeWidth(10);
mBRDPaint.setColor(Color.parseColor("#80123456"));
}
}
Now I am trying to add multiple views in LinearLayout with orientation horizontal like below :
float density = getApplicationContext().getResources().getDisplayMetrics().density;
DrawView drawView;
float x = 100, y = 200;
int count1 = 1;
int id;
LinearLayout layout2 = new LinearLayout(this);
layout2.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
layout2.setOrientation(LinearLayout.HORIZONTAL);
main_layout.addView(layout2);
DrawView drawView1;
CircleView circleView;
for (Map.Entry<String, ArrayList<String>> entry : map.entrySet()) {
String key = entry.getKey();
if (count1 < 2) {
x = dirButton.getX();
y = dirButton.getY();
}
drawView1 = new DrawView(this, x, y, density, key);
drawView1.setId(butId++);
drawView1.setLayoutParams(params);
layout2.addView(drawView1);
count1++;
x = x + 100;
}
But when I do this only one view is added to the canvas and others are not visible. I have no experience in working with canvas in android , I would be glad if someone could guide me with this problem.
I tried working on your project but it is too broad to edit on the answer sheet. I must suggest to look these:
Multiple rect.
Rectangle with view
if (count1 < 2) {
x = dirButton.getX();
y = dirButton.getY();
}
from the code line above, you set condition for when the line is executed.
and use if statement.
int count1 = 1; //Count was initialized to 1
This makes the code to enter the if statement on first call
count1++;
This line increases the value of count to 2, hence the if block do not execute again...
And y value never change which leads to overlay.
May be what you missed is regular increament of y
y+=something;
Hope that helps
Please check how i have done this,
You can check from here form myapp, how it work
// How i call to draw rectangle
This is the SDV.class
public static boolean isDrawing = false;
public static float mStartX;
public static float mStartY;
public static float mx;
public static float my;
public static void Shape14(float x, float y, float radius) {
Path path = new Path();
y -= 2 * radius;
radius *= 2;
path.moveTo(x + radius, y + radius);
path.lineTo(x - radius, y + radius);
path.lineTo(x, y);
path.lineTo(x + radius, y + radius);
float div = (2 * radius) / 5;
float top = y + radius;
RectF rect1 = new RectF(x + radius / 4, y, x + radius / 1.9f, y
+ radius);
RectF rect2 = new RectF(x + div / 2, top, x + div / 2 + div, top + div
* 2);
RectF rect3 = new RectF(x - div / 2 - div, top, x - div / 2, top + div
* 2);
RectF rect4 = new RectF(x - div / 2, top, x + div / 2, top + div);
HashMap<String, Object> hm = new HashMap<String, Object>();
hm.put("type", shape14);
hm.put("paint", new Paint(DrawingView.mColorPaint));
hm.put("path", path);
hm.put("rect1", rect1);
hm.put("rect2", rect2);
hm.put("rect3", rect3);
hm.put("rect4", rect4);
al.add(hm);
Gmap.mDrawingView.invalidate();
}
Here is our view,
public class DrawingView extends View {
public static Paint mPaint;
public static int mCurrentShape;
Point p1, p2, p3, p4;
public static Paint mDotedPaint;
public static Paint mColorPaint;
GoogleMap googleMap;
SeekBar sbWidth;
public static float sx, sy;
public DrawingView(Context context, GoogleMap googleMap, SeekBar sbWidth) {
super(context);
this.googleMap = googleMap;
this.sbWidth = sbWidth;
mPaint = new Paint(Paint.DITHER_FLAG);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(SDV.colorChoosen);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(SDV.width);
mDotedPaint = new Paint(Paint.DITHER_FLAG);
mDotedPaint.setAntiAlias(true);
mDotedPaint.setDither(true);
mDotedPaint.setColor(SDV.colorChoosen);
mDotedPaint.setStyle(Paint.Style.STROKE);
mDotedPaint.setStrokeJoin(Paint.Join.ROUND);
mDotedPaint.setStrokeCap(Paint.Cap.ROUND);
mDotedPaint.setStrokeWidth(SDV.width);
mColorPaint = new Paint(Paint.DITHER_FLAG);
mColorPaint.setAntiAlias(true);
mColorPaint.setDither(true);
mColorPaint.setFilterBitmap(true);
mColorPaint.setStyle(Paint.Style.FILL);
mColorPaint.setStrokeWidth(SDV.width);
mColorPaint.setColor(SDV.colorChoosen);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
sx = super.getWidth() * 0.5f;
sy = super.getHeight() * 0.5f;
if (SDV.isDrawing) {
new OnGoingDrawings().HandleAllOnGoingDrawings(mCurrentShape,
canvas);
} else {
new ShapeDrawer().DrawEverything(canvas, googleMap, sbWidth);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
SDV.mx = event.getX();
SDV.my = event.getY();
switch (mCurrentShape) {
case SDV.shape14:
TouchEvents.Shape14(event);
break;
return true;
}
}
Here is touch listener,
public static void Shape14(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
SDV.isDrawing = true;
SDV.mStartX = SDV.mx;
SDV.mStartY = SDV.my;
Gmap.mDrawingView.invalidate();
break;
case MotionEvent.ACTION_MOVE:
Gmap.mDrawingView.invalidate();
break;
case MotionEvent.ACTION_UP:
SDV.isDrawing = false;
float x = SDV.mStartX,
y = SDV.mStartY;
float DifX = Math.abs(SDV.mx - SDV.mStartX);
float DifY = Math.abs(SDV.my - SDV.mStartY);
float radius;
if (DifY > DifX)
radius = Math.abs(SDV.my - SDV.mStartY);
else
radius = Math.abs(SDV.mx - SDV.mStartX);
SDV.Shape14(x, y, radius);
break;
}
}

Make ball bounce and eventually come to rest

I've written some code to move a ball around the screen using an orientation sensor. I wanted to get the ball to bounce when it hits the bottom of the screen, sort of like under gravity. Could somebody help out with implementing the physics in my existing code? Flipping the velocity doesn't seem to work. Here's my ball class:
package perseus.gfx.test;
import everything
public class Ball extends View {
RectF lol;
Paint paint, lpaint;
Bitmap bitmap;
Canvas canvas;
private float ballx = 150;
private float bally = 140;
private double speedx = 0;
private double speedy = 0; //ignore
private double accx, accy=0;
private float rad = 20;
private float mult = 0.5f;
private double xv, yv, xS, yS;
int width, height;
int xmax, ymax;
int xmin, ymin;
public Ball(Context context) {
super(context);
lol = new RectF();
paint = new Paint();
paint.setColor(Color.CYAN);
lpaint = new Paint();
lpaint.setColor(Color.GRAY);
}
public void moveBall() {
xv = accx * mult;
yv = accy * mult;
xS = xv * mult;
yS = yv * mult;
ballx -= xS;
bally -= yS;
// Collision detection
if (ballx + rad > xmax) {
ballx = xmax-rad;
}
else if (ballx - rad < 0) {
ballx = rad;
}
if (bally + rad > 2*ymax/3) //Shouldn't take up the whole screen
{
bally = 2*ymax/3 - rad;
}
else if (bally - rad < 0) {
bally = rad;
}
try {
Thread.sleep(20);
} catch(InterruptedException e) {}
invalidate();
}
#Override
public void onMeasure(int widthM, int heightM)
{
width = View.MeasureSpec.getSize(widthM);
height = View.MeasureSpec.getSize(heightM);
xmax = width-1;
ymax = height-1;
xmin = 0;
ymin = 0;
setMeasuredDimension(width, height);
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
}
#Override
public void onDraw(Canvas canvas)
{
canvas.drawBitmap(bitmap, 0, 0, paint);
lol.set(ballx - rad, bally-rad, ballx + rad, bally+rad);
canvas.drawLine(0, 2*height/3, width, 2*height/3, lpaint);
canvas.drawOval(lol, paint);
canvas.drawText(xv + " " + yv, 0, height/2, lpaint);
canvas.save();
moveBall();
canvas.restore();
}
}
So the key is to just add a bit of friction, just remove a tiny bit of acceleration (negative!) at each step in moveBall(). E.g.
float friction = -0.001;
xv = accx * mult + friction;
yv = accy * mult + friction;
Then adjust the variable friction accordingly to suit your needs. For the collision you need to invert the velocity, e.g. bounce on bottom:
bally = -bally;

How to drawing a path with a bitmap?

I have a little drawing app and want to use "complex" shapes as brushes, i.e. a star.
Drawing with a simple brush already works with this code:
remotePath.reset();
remotePath.moveTo(start_x, start_y);
float dx = Math.abs(end_x - start_x);
float dy = Math.abs(end_y - start_y);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
remotePath.quadTo(start_x, start_y, (end_x + start_x) / 2, (end_y + start_y) / 2);
}
remotePath.lineTo(end_x, end_y);
// commit the path to our offscreen
mCanvas.drawPath(remotePath, remotePaint);
// kill this so we don't double draw
remotePath.reset();
invalidate();
I basically want the same functionality using this bitmap:
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.brush_star);
My solution currently is using a list of points (coordinates) to draw the bitmap. The problem with that solution is that it only draws bitmaps at the given points resulting in having gaps between each drawn bitmap. I rather would like to get a smooth line while drawing like with a simple brush without any gaps in between.
Current code for the bitmap drawing:
protected void onDraw(Canvas canvas) {
// Make canvas white
canvas.drawColor(Color.WHITE);
// Paintable area
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
for (Point point : points) {
canvas.drawBitmap(complexBrush, point.x, point.y, p);
}
}
What's the best way to do so?
Thanks for any help!
I use this
Point's class:
public class Point implements Serializable {
float x, y;
float dx, dy;
}
Paint object:
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
paint.setColor(Color.RED);
paint.setAntiAlias(true);
draw on canvas:
private void drawCanvas(Canvas canvas, List<Point> pts){
if (pts.size() > 1){
Path path = new Path();
final int SMOOTH_VAL = 6;
for(int i = pts.size() - 2; i < pts.size(); i++){
if(i >= 0){
Point point = pts.get(i);
if(i == 0){
Point next = pts.get(i + 1);
point.dx = ((next.x - point.x) / SMOOTH_VAL);
point.dy = ((next.y - point.y) / SMOOTH_VAL);
}
else if(i == pts.size() - 1){
Point prev = pts.get(i - 1);
point.dx = ((point.x - prev.x) / SMOOTH_VAL);
point.dy = ((point.y - prev.y) / SMOOTH_VAL);
}
else{
Point next = pts.get(i + 1);
Point prev = pts.get(i - 1);
point.dx = ((next.x - prev.x) / SMOOTH_VAL);
point.dy = ((next.y - prev.y) / SMOOTH_VAL);
}
}
}
boolean first = true;
for(int i = 0; i < pts.size(); i++){
Point point = pts.get(i);
if(first){
first = false;
path.moveTo(point.x, point.y);
}
else{
Point prev = pts.get(i - 1);
path.cubicTo(prev.x + prev.dx, prev.y + prev.dy, point.x - point.dx, point.y - point.dy, point.x, point.y);
}
}
canvas.drawPath(path, paint);
} else {
if (pts.size() == 1) {
Point point = pts.get(0);
canvas.drawCircle(point.x, point.y, 2, paint);
}
}
}
Draw on bitmap canvas:
private void drawBitmap(Bitmap bmp, List<Point> pts) {
Canvas c = new Canvas(bmp);
drawCanvas(c, pts);
}

how to rotate text using canvas in Android

i was draw a pie chart using canvas in android and using the below code i draw a text on each slice of that pie chart (draw arc on path), now i want to draw the text length wise i.e. from center to end of the each slice,so how to rotate the arc using start and sweep angle.
p.addArc(mEventsRect, fStartAngle, fSweepAngle);
mBgPaints.setColor(iTextColor);
canvas.drawTextOnPath(sTextValue, p, fHOffSet, fVOffSet, mBgPaints);
You can try this snippet: (from: http://www.helloandroid.com/tutorials/how-use-canvas-your-android-apps-part-2)
int x = 75;
int y = 185;
paint.setColor(Color.GRAY);
paint.setTextSize(25);
String rotatedtext = "Rotated helloandroid :)";
//Draw bounding rect before rotating text:
Rect rect = new Rect();
paint.getTextBounds(rotatedtext, 0, rotatedtext.length(), rect);
canvas.translate(x, y);
paint.setStyle(Paint.Style.FILL);
canvas.drawText(rotatedtext , 0, 0, paint);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(rect, paint);
canvas.translate(-x, -y);
paint.setColor(Color.RED);
canvas.rotate(-45, x + rect.exactCenterX(),y + rect.exactCenterY());
paint.setStyle(Paint.Style.FILL);
canvas.drawText(rotatedtext, x, y, paint);
A bit late to the party but I had to figure this one out and it's a bit simpler than what I found around. You'll already have the x and y for your text, use these to rotate the canvas
canvas.rotate(yourDegrees, x, y)
canvas.drawText(yourText, x, y, yourPaint)
canvas.rotate(-yourDegrees, x, y)
The negative sign negates the first rotation. You could swap it around to rotate in the opposite direction.
You could do this in a loop but the rotation cycle must be done each time either coordinate changes.
may be this will help you,,
here 39.5 is radius,, this will perfectly show result on mdpi screen
protected void onDraw(){
canvas.save();
PointF pf = PointOnCircle(35f, 45f, new PointF(39.5f, 39.5f));
canvas.rotate(-45, pf.x, pf.y);
canvas.drawText("67%", pf.x, pf.y, red);//23.5
canvas.restore();
canvas.save();
PointF pfa = PointOnCircle(35f, 135f, new PointF(39.5f, 39.5f));
canvas.rotate(45, pfa.x, pfa.y);
canvas.drawText("33%", pfa.x, pfa.y, red);//23.5
canvas.restore();
canvas.save();
pfa = PointOnCircle(27.5f, 225f, new PointF(39.5f, 39.5f));
canvas.rotate(-45, pfa.x, pfa.y);
canvas.drawText("45%", pfa.x, pfa.y, red);//23.5
canvas.restore();
canvas.save();
pfa = PointOnCircle(27.5f, 315f, new PointF(39.5f, 39.5f));
canvas.rotate(45, pfa.x, pfa.y);
canvas.drawText("55%", pfa.x, pfa.y, red);//23.5
canvas.restore();}
protected static final PointF PointOnCircle(float radius, float angleInDegrees, PointF origin) {
// Convert from degrees to radians via multiplication by PI/180
float x = (float) (radius * Math.cos(angleInDegrees * Math.PI / 180F)) + origin.x;
float y = (float) (radius * Math.sin(angleInDegrees * Math.PI / 180F)) + origin.y;
return new PointF(x, y);
}
Here's how i finally did it after two days of search with help of this library https://github.com/Ken-Yang/AndroidPieChart
And equations to center text done with help of my friends and alot of search
on MainActivity onCreate or oncreateView if you are using fragments:
PieChart pie = (PieChart) rootView.findViewById(R.id.pieChart);
ArrayList<Float> alPercentage = new ArrayList<Float>();
alPercentage.add(2.0f);
alPercentage.add(8.0f);
alPercentage.add(20.0f);
alPercentage.add(10.0f);
alPercentage.add(10.0f);
alPercentage.add(10.0f);
alPercentage.add(10.0f);
alPercentage.add(10.0f);
alPercentage.add(10.85f);
alPercentage.add(9.15f);
try {
// setting data
pie.setAdapter(alPercentage);
// setting a listener
pie.setOnSelectedListener(new OnSelectedLisenter() {
#Override
public void onSelected(int iSelectedIndex) {
Toast.makeText(getActivity(),
"Select index:" + iSelectedIndex,
Toast.LENGTH_SHORT).show();
}
});
} catch (Exception e) {
if (e.getMessage().equals(PieChart.ERROR_NOT_EQUAL_TO_100)) {
Log.e("kenyang", "percentage is not equal to 100");
}
}
public class PieChart extends View {
public interface OnSelectedLisenter {
public abstract void onSelected(int iSelectedIndex);
}
private OnSelectedLisenter onSelectedListener = null;
private static final String TAG = PieChart.class.getName();
public static final String ERROR_NOT_EQUAL_TO_100 = "NOT_EQUAL_TO_100";
private static final int DEGREE_360 = 360;
private static String[] PIE_COLORS = null;
private static int iColorListSize = 0;
ArrayList<Float> array;
private Paint paintPieFill;
private Paint paintPieBorder;
private Paint paintCenterCircle;
private ArrayList<Float> alPercentage = new ArrayList<Float>();
private int mCenterX = 320;
private int mCenterY = 320;
private int iDisplayWidth, iDisplayHeight;
private int iSelectedIndex = -1;
private int iCenterWidth = 0;
private int iShift = 0;
private int iMargin = 0; // margin to left and right, used for get Radius
private int iDataSize = 0;
private Canvas canvas1;
private RectF r = null;
private RectF centerCircle = null;
private float fDensity = 0.0f;
private float fStartAngle = 0.0f;
private float fEndAngle = 0.0f;
float fX;
float fY;
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
PIE_COLORS = getResources().getStringArray(R.array.colors);
iColorListSize = PIE_COLORS.length;
array = new ArrayList<Float>();
fnGetDisplayMetrics(context);
iShift = (int) fnGetRealPxFromDp(30);
iMargin = (int) fnGetRealPxFromDp(40);
centerCircle = new RectF(200, 200, 440, 440);
// used for paint circle
paintPieFill = new Paint(Paint.ANTI_ALIAS_FLAG);
paintPieFill.setStyle(Paint.Style.FILL);
// used for paint centerCircle
paintCenterCircle = new Paint(Paint.ANTI_ALIAS_FLAG);
paintCenterCircle.setStyle(Paint.Style.FILL);
paintCenterCircle.setColor(Color.WHITE);
// used for paint border
paintPieBorder = new Paint(Paint.ANTI_ALIAS_FLAG);
paintPieBorder.setStyle(Paint.Style.STROKE);
paintPieBorder.setStrokeWidth(fnGetRealPxFromDp(3));
paintPieBorder.setColor(Color.WHITE);
Log.i(TAG, "PieChart init");
}
// set listener
public void setOnSelectedListener(OnSelectedLisenter listener) {
this.onSelectedListener = listener;
}
float temp = 0;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.i(TAG, "onDraw");
float centerX = (r.left + r.right) / 2;
float centerY = (r.top + r.bottom) / 2;
float radius1 = (r.right - r.left) / 2;
radius1 *= 0.5;
float startX = mCenterX;
float startY = mCenterY;
float radius = mCenterX;
float medianAngle = 0;
Path path = new Path();
for (int i = 0; i < iDataSize; i++) {
// check whether the data size larger than color list size
if (i >= iColorListSize) {
paintPieFill.setColor(Color.parseColor(PIE_COLORS[i
% iColorListSize]));
} else {
paintPieFill.setColor(Color.parseColor(PIE_COLORS[i]));
}
fEndAngle = alPercentage.get(i);
// convert percentage to angle
fEndAngle = fEndAngle / 100 * DEGREE_360;
// if the part of pie was selected then change the coordinate
if (iSelectedIndex == i) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
float fAngle = fStartAngle + fEndAngle / 2;
double dxRadius = Math.toRadians((fAngle + DEGREE_360)
% DEGREE_360);
fY = (float) Math.sin(dxRadius);
fX = (float) Math.cos(dxRadius);
canvas.translate(fX * iShift, fY * iShift);
}
canvas.drawArc(r, fStartAngle, fEndAngle, true, paintPieFill);
float angle = (float) ((fStartAngle + fEndAngle / 2) * Math.PI / 180);
float stopX = (float) (startX + (radius/2) * Math.cos(angle));
float stopY = (float) (startY + (radius/2) * Math.sin(angle));
// if the part of pie was selected then draw a border
if (iSelectedIndex == i) {
canvas.drawArc(r, fStartAngle, fEndAngle, true, paintPieBorder);
canvas.drawLine(startX, startY, stopX, stopY, paintPieFill);
canvas.restore();
}
fStartAngle = fStartAngle + fEndAngle;
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// get screen size
iDisplayWidth = MeasureSpec.getSize(widthMeasureSpec);
iDisplayHeight = MeasureSpec.getSize(heightMeasureSpec);
if (iDisplayWidth > iDisplayHeight) {
iDisplayWidth = iDisplayHeight;
}
/*
* determine the rectangle size
*/
iCenterWidth = iDisplayWidth / 2;
int iR = iCenterWidth - iMargin;
if (r == null) {
r = new RectF(iCenterWidth - iR, // top
iCenterWidth - iR, // left
iCenterWidth + iR, // right
iCenterWidth + iR); // bottom
}
if (centerCircle == null) {
// centerCircle=new RectF(left, top, right, bottom);
}
setMeasuredDimension(iDisplayWidth, iDisplayWidth);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// get degree of the touch point
double dx = Math.atan2(event.getY() - iCenterWidth, event.getX()
- iCenterWidth);
float fDegree = (float) (dx / (2 * Math.PI) * DEGREE_360);
fDegree = (fDegree + DEGREE_360) % DEGREE_360;
// get the percent of the selected degree
float fSelectedPercent = fDegree * 100 / DEGREE_360;
// check which pie was selected
float fTotalPercent = 0;
for (int i = 0; i < iDataSize; i++) {
fTotalPercent += alPercentage.get(i);
if (fTotalPercent > fSelectedPercent) {
iSelectedIndex = i;
break;
}
}
if (onSelectedListener != null) {
onSelectedListener.onSelected(iSelectedIndex);
}
invalidate();
return super.onTouchEvent(event);
}
private void fnGetDisplayMetrics(Context cxt) {
final DisplayMetrics dm = cxt.getResources().getDisplayMetrics();
fDensity = dm.density;
}
private float fnGetRealPxFromDp(float fDp) {
return (fDensity != 1.0f) ? fDensity * fDp : fDp;
}
public void setAdapter(ArrayList<Float> alPercentage) throws Exception {
this.alPercentage = alPercentage;
iDataSize = alPercentage.size();
float fSum = 0;
for (int i = 0; i < iDataSize; i++) {
fSum += alPercentage.get(i);
}
if (fSum != 100) {
Log.e(TAG, ERROR_NOT_EQUAL_TO_100);
iDataSize = 0;
throw new Exception(ERROR_NOT_EQUAL_TO_100);
}
}
in your Layout:
<com.example.piecharts.PieChart
android:id="#+id/pieChart"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</com.example.piecharts.PieChart>
This question is pretty old, but I figured I would write a general answer.Here I assume you want to draw your pie chart in the middle of the canvas and that you have your start and seep angles in an array.
x = canvas.getWidth/2 //Horizontal center of canvas view
y = canvas.getHeight/2 //Vertical center of canvas view
canvas.rotate(fStartAngle[i]+ fSweepAngle[i]/2, x ,y ); //Rotates canvas to a line in the middle
//of start and end of arc
canvas.translate(50f,0);//Moves the text a little out of the center of the circle (50f is arbitrary)
paintText.setStyle(Paint.Style.FILL);
canvas.drawText(rotatedtext, x, y, paintText);
//Undo the translations and rotations so that next arc can be drawn normally
canvas.translate(-50f,0);
canvas.rotate(-(temp+ value_degree[i]/2), x ,y );
it's 2023 there might be other answers out there but here is one that is sure to work
//the path where your text/paint will be drawn across
Path path = new Path();
path.addArc(mEventsRect, fStartAngle, fSweepAngle);//add this if you want your path to be drawn across the arc of your sector
//if you are using a text get the width
float textWidth = mTextPaint.measureText("text");
//this is the y co-ordinate your text will start from
int hOffset = 100;
//this is the x co-ordinate your text will start from
int vOffset = 100;
//we will be using the matrix to rotate the bunds of our current path
Matrix matrix = new Matrix();
//we will use this to get the bounds of our current path
RectF bounds = new RectF();
path.computeBounds(bounds,true);
//we are using the matrix to rotate the bound (with is the bound of the path) by 90 degrees
matrix.setRotate(90,bounds.centerX(),bounds.centerY());
the we transform the points in the path using the matrix
path.transform(matrix);
//you can now draw the text on the path
canvas.drawTextOnPath("text", path, hOffset, vOffset , mBgPaints);

Categories

Resources