Android : canvas.drawBitmap() method not working properly - android

I already post two question related this (plz refer this
custom piechart 1 and this custom pie chart 2) but not getting answer ,finally i develop my own but getting stuck at some point .
I need to create lines between drawn area as you can see in image .for inner circle i used canvas.drawArc() .for outer circle i used canvas.drawCircle() and for middle lines i used canvas.drawBitmap() method with differ angle .here inner and outer circle drawn properly but for middle arc ,only first two bitmap/arcs are drawn properly but remaining two are not in their exact position .
I used same code for drawing all bitmap/arcs but not getting exact output .I am getting stuck what is the exact problem.need help ,thanks in advance .
you can see my output
here..
Hear is my onCreate() , in which i generate and set view .
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv1 = (LinearLayout) findViewById(R.id.linear);
matrix = new Matrix();
MyView myview = new MyView(this);
lv1.addView(myview);
}
this is my customview class that i want to generate.
public class MyView extends View {
private Paint p ,paint,paint_text;
private int startX;
private int startY;
private int radius;
private ArrayList<Integer> colors;
private ArrayList<Float> values;
Bitmap bitmap;
Context mContext;
RectF rectF , rectF2 ;
public MyView(Context context) {
super(context);
mContext = context;
p = new Paint();
p.setAntiAlias(true);
colors = new ArrayList<Integer>();
values = new ArrayList<Float>();
Display display = getWindowManager().getDefaultDisplay();
width1 = display.getWidth();
height1 = display.getHeight();
startX = 0 ;
startY = 0;
radius = (int) (width1/2);
colors.add(Color.RED);
colors.add(Color.BLUE);
colors.add(Color.YELLOW);
colors.add(Color.GREEN);
values.add(8f);
values.add(2f);
values.add(4f);
values.add(2f);
}
This is my onDraw() method .
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float _angle1 = 0 ,_angle2 = 0 ,_angle3 = 0 , _angle4 =0;
Bitmap myBitmap1 = BitmapFactory.decodeResource(getResources(),R.drawable.saperate_line);
bitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bitmap);
float offset = 0;
float angle = (float) 5.60 ;
matrix.reset();
canvas.translate(0,canvas.getHeight()); //reset where 0,0 is located
canvas.scale(1,-1); // for scaling
rectF = new RectF();
rectF.set(-(getStartX() + getRadius()), -(getStartY() + getRadius()), getStartX() + getRadius(),getStartY() + getRadius());
//for creating outer circle using canvas.drawCircle-----------
paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStrokeWidth(2);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(0, 0, (float) (width1/(1.4)), paint);
//for creating inner circle using canvas.drawArc-----------
for (int i = 0; i < values.size(); i++) {
p.setColor(colors.get(i));
if (i == 0) {
canvas.drawArc(rectF, offset, values.get(i) * angle, true, p);
_angle1 = ((offset+(values.get(i) * angle)))/2;
matrix.postRotate(_angle1);
canvas.drawBitmap(myBitmap1, matrix, null);
}
if(i == 1){
canvas.drawArc(rectF, offset, values.get(i) * angle, true, p);
_angle2 = ((offset + (values.get(i) * angle)))/2;
matrix.postRotate(_angle2);
canvas.drawBitmap(myBitmap1, matrix, null);
}
if(i == 2){
canvas.drawArc(rectF, offset, values.get(i) * angle, true, p);
_angle3 = ((offset + (values.get(i) * angle)))/2;
// _angle3 = (offset + angle);
matrix.postRotate(_angle3);
canvas.drawBitmap(myBitmap1, matrix, null);
}
if (i == 3){
canvas.drawArc(rectF, offset, values.get(i) * angle, true, p);
_angle4 = ((offset + (values.get(i) * angle)))/2;
matrix.postRotate(_angle4);
canvas.drawBitmap(myBitmap1, matrix, null);
}
offset += (values.get(i) * angle);
Log.e("new offset :: ","****************************"+offset);
}
canvas.save();
}

Thanks to every one.
finally i did it using replacing this stuff Rotating Image on A canvas
matrix.postRotate(_angle1);
canvas.drawBitmap(myBitmap1, matrix, null);
with this
Matrix matrix1 = new Matrix();
matrix1.setRotate(_angle1, 0, 0);
canvas.drawBitmap(myBitmap1, matrix1, null);
here ,first you should change the value like this ,
float myAngle = 0;
myAngle = ((90 * values.get(i)) / 16);

Related

Size of watermark differs with phones

I want to add watermark to my app. The screen is divided into 4 rectangle,size of each are ScreenWidth * 1/4ScreenHeight.And each rectangle contains two watermarks.The code is posted below:
final ViewGroup rootView = (ViewGroup) getActivity().findViewById(android.R.id.content);
ScreenInitControl screenInitControl = new ScreenInitControl(getActivity());
int screenWidth = screenInitControl.getScreenWidth();
int screenHeight = screenInitControl.getScreenHeight();
int bitmapWidth = screenWidth;
int bitmapHeight = screenHeight / 4;
Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
bitmap.setHasAlpha(true);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
// init paint
Paint paint = new Paint();
paint.setColor(Color.GRAY);
paint.setAlpha(100);
paint.setAntiAlias(true);
paint.setTextAlign(Paint.Align.LEFT);
paint.setTextSize(50);
// the water mark text
String watermarkText = Config.getIns().getName();
// draw the first watermark in a row
int x = 30;
int y = bitmapHeight / 2;
Path path = new Path();
path.moveTo(x, y);
path.lineTo(bitmapWidth / 2, 30);
canvas.drawTextOnPath(watermarkText, path, 0, 30, paint);
// draw the second watermark in a row
Path path1 = new Path();
x = bitmapWidth / 2 + 30;
y = bitmapHeight / 2;
path1.moveTo(x, y);
path1.lineTo(bitmapWidth, 30);
canvas.drawTextOnPath(watermarkText, path1, 0, 30, paint);
BitmapDrawable bitmapDrawable = new BitmapDrawable(bitmap);
bitmapDrawable.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
// add the watermark onto background
final View watermarkView = new View(getActivity());
watermarkView.setLayoutParams(new LinearLayout.LayoutParams(screenWidth, screenHeight));
if (Build.VERSION.SDK_INT < 16) {
watermarkView.setBackgroundDrawable(bitmapDrawable);
} else {
watermarkView.setBackground(bitmapDrawable);
}
rootView.postDelayed(new Runnable() {
#Override
public void run() {
rootView.addView(watermarkView);
}
}, 50);
The code work fine in Samsung galaxy A5. But when I install the app on OPPO R11S, the wartermark comes to be 1/3 size of that in Samsung.Do I ignore something?
Finally,I find it that BitmapDrawable bitmapDrawable = new BitmapDrawable(bitmap) is deprecated. I use BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), bitmap) instead and the problem is solved.

Google Maps v2 Custom Tile Provider

i am developing a custom tile provider to show traffic data on Google Maps. In high zoom levels it is good for me. But polylines are overlapping at low level zoom.
My custom tile provider class is
public class PolylineTileProvider implements TileProvider {
private static final String TAG = "TileOverlay";
private final int mTileSize = 256;
private final SphericalMercatorProjection mProjection = new SphericalMercatorProjection(mTileSize);
private final int mScale = 2;
private final int mDimension = mScale * mTileSize;
private final List<PolylineOptions> polylines;
public PolylineTileProvider(List<PolylineOptions> polylines) {
this.polylines = polylines;
}
#Override
public Tile getTile(int x, int y, int zoom) {
Matrix matrix = new Matrix();
float scale = ((float) Math.pow(2, zoom) * mScale);
matrix.postScale(scale, scale);
matrix.postTranslate(-x * mDimension, -y * mDimension);
Bitmap bitmap = Bitmap.createBitmap(mDimension, mDimension, Bitmap.Config.ARGB_8888); //save memory on old phones
Canvas c = new Canvas(bitmap);
c.setMatrix(matrix);
drawCanvasFromArray(c, scale);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
return new Tile(mDimension, mDimension, baos.toByteArray());
}
private void drawCanvasFromArray(Canvas c, float scale) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setShadowLayer(0, 0, 0, 0);
paint.setAntiAlias(true);
if (polylines != null) {
for (int i = 0; i < polylines.size(); i++) {
List<LatLng> route = polylines.get(i).getPoints();
paint.setColor(polylines.get(i).getColor());
paint.setStrokeWidth(getLineWidth(polylines.get(i).getWidth(), scale));
Path path = new Path();
if (route != null && route.size() > 1) {
Point screenPt1 = mProjection.toPoint(route.get(0)); //first point
MarkerOptions m = new MarkerOptions();
m.position(route.get(0));
path.moveTo((float) screenPt1.x, (float) screenPt1.y);
for (int j = 1; j < route.size(); j++) {
Point screenPt2 = mProjection.toPoint(route.get(j));
path.lineTo((float) screenPt2.x, (float) screenPt2.y);
}
}
c.drawPath(path, paint);
}
}
}
private float getLineWidth(float width, float scale) {
return width / (scale);
}
}
Trafic layer is shown at Google Maps android application so good.
How can i make a similar layer. Thanks in advance.
the reason why it gets blured, or is maybe no more seen on the screen is because you create one image that is then scaled using the matrix you provided.
instead you shoudn't use a Matrix and generate the images in the right size.
Todo so, remove you your setMatrix call on the Canvas
and
add the Points to the Path with the right scaled coordinates.
x = screenPt1.x * scale - x * mDimension;
y = screenPt1.y * scale - y * mDimension;
then you get the exact line as specified, in every zoom level.

Edited: Android Draw needle image to this circle similar to meter guage

So far i am able to draw semi circle using some code reference form stackoverflow. Now i wish to draw needle to this semi circle.
I have no idea as how to do it. Here is the Semi circle code
package in.ashish29agre.stackoverflow.sample.semicircle;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.view.View;
public class MyGraphView extends View {
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private float[] value_degree;
private int[] COLORS = { Color.GREEN, Color.RED, Color.GREEN, Color.RED };
// size of bigger half circle
RectF rectf = null;
// size of smaller half circle
// RectF rectf2 = new RectF(45, 45, 275, 275);
// size of the smallest half circle
// RectF rectf3 = new RectF(80, 80, 240, 240);
int temp = 0;
public MyGraphView(Context context, float[] values) {
super(context);
rectf = new RectF();
value_degree = new float[values.length];
for (int i = 0; i < values.length; i++) {
value_degree[i] = values[i];
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
rectf.set(100, 100, width - 100, height - 100);
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
for (int i = 0; i < value_degree.length; i++) {
// set type of "brush"
paint.setStrokeWidth(128);
paint.setStyle(Paint.Style.STROKE);
// agree
if (i == 0) {
final Path path = new Path();
paint.setColor(COLORS[i]);
// draw 3 paths to show 3 curves
path.addArc(rectf, 180, value_degree[i] - 4);
// path.addArc(rectf2, 180, value_degree[i] - 5);
// path.addArc(rectf3, 180, value_degree[i] - 6);
// draw the path
canvas.drawPath(path, paint);
// disagree
} else {
temp += (int) value_degree[i - 1];
paint.setColor(COLORS[i]);
final Path path = new Path();
path.addArc(rectf, temp + 180 , value_degree[i] - 0);
// path.addArc(rectf2, temp + 180 + 5, value_degree[i] - 5);
// path.addArc(rectf3, temp + 180 + 6, value_degree[i] - 6);
// draw the path
canvas.drawPath(path, paint);
}
drawLine(canvas);
}
}
private void drawLine(Canvas canvas) {
float startAngle = 180;
float sweepAngle = 180;
Paint mPaint = new Paint();
mPaint.setStrokeWidth(22f);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
float startX = rectf.centerX();
float startY = rectf.centerY();
float radius = rectf.centerX() - 100;
float angle = (float) ((startAngle + sweepAngle / 2) * Math.PI / 180);
float stopX = (float) (startX + radius * Math.cos(angle));
float stopY = (float) (startY + radius * Math.sin(angle));
canvas.drawLine(startX, startY, stopX, stopY, mPaint);
}
}
From the Activity i call it using
float values[] = { 10, 20, 30, 40};
LinearLayout linear = (LinearLayout) findViewById(R.id.graph_view);
values = calculateData(values);
// draw the graf
linear.addView(new MyGraphView(this, values));
This is my activity xml code
<?xml version="1.0" encoding="utf-8"?>
<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"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="in.ashish29agre.stackoverflow.sample.semicircle.SemiCircleActivity">
<LinearLayout
android:id="#+id/graph_view"
android:layout_width="200dp"
android:layout_height="200dp"
android:orientation="vertical"
android:layout_centerInParent="true"
android:layout_alignParentTop="true"></LinearLayout>
</RelativeLayout>
Currently the view looks like this now i wish to add meter hand which can move from 0-180 degree.
Any help will be appreciated.
Now that i have drawn needle i wish to make to a needle bitmap??
You may want to animate the needle bitmap. Here is my code:
RotateAnimation animation =
new RotateAnimation(
mCurrentAngle,
toAngle,
Animation.RELATIVE_TO_SELF,
pivotX,
Animation.RELATIVE_TO_SELF,
pivotY);
animation.setRepeatCount(0);
animation.setFillAfter(true);
animation.setFillEnabled(true);
animation.setInterpolator(new OvershootInterpolator());
animation.setDuration(duration);
mMeterPointer.startAnimation(animation);
Where mMeterPointer is an ImageView of the needle.
The pivotX and pivotY values should where the needle image should rotate. Their values should be 0 to 1, e.g. 0.5 and 0.5 if you want it to rotate on the middle of the bitmap.
Add to your class:
Matrix matrix;
Bitmap needle;
to your MyGraphView constructor:
matrix = new Matrix();
and this to the end of onMeasure():
needle = decodeSampledBitmapFromResource(context.getResources(),
R.drawable.icon_resource, (int)rectf.width(), (int)rectf.height());
and in drawLine():
private void drawLine(Canvas canvas) {
float startAngle = 180;
float sweepAngle = 180;
// of course you'll need to calculate the actual angle here:
float angle = (float) ((startAngle + sweepAngle / 2) * Math.PI / 180);
matrix.reset();
// move the needle to IT's center. You might need different
// values depending on your drawable, probably the axis will not
// be in the middle
matrix.postTranslate(-bitmap.getWidth() / 2, -bitmap.getWidth() / 2);
// and rotate it
matrix.postRotate(angle);
// move the needle to the semi circle's center
matrix.postTranslate(rectf.centerX(), rectf.centerY());
canvas.drawBitmap(needle, matrix, null);
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
Adding to #bennegeek's answer can you try this
private void setAngleToNeedle() {
int value = 675;
int maxValue = 1000;
int angleDifference = 180;
int endingAngle = (int) (((float) value / maxValue) * (angleDifference));
final RotateAnimation rotateAnim = new RotateAnimation(-90, -90 + endingAngle,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 1f);
rotateAnim.setDuration(1500);
rotateAnim.setInterpolator(new LinearInterpolator());
rotateAnim.setFillAfter(true);
needleIv.startAnimation(rotateAnim);
}

How to draw a pie chart using RectF which will be easy to support multiple screen size in android?

Hi I am using following code to draw a pie chart in android :-
public class Chart extends View
{
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private float[] value_degree;
private String[] Title_value;
RectF rectf = new RectF(50, 5, 200, 150);
Rect rect = new Rect(50, 5, 200, 150);
float temp = 0;
public Chart(Context context, float[] values, String[] heading)
{
super(context);
value_degree = new float[values.length];
Title_value = new String[heading.length];
for (int i = 0; i < values.length; i++)
{
value_degree[i] = values[i];
Log.i("abc", "" + values[i]);
Title_value[i] = heading[i];
}
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Random r;
int centerX = (rect.left + rect.right) / 2;
int centerY = (rect.top + rect.bottom) / 2;
int radius = (rect.right - rect.left) / 2;
int color;
radius *= 0.5; // 1 will put the text in the border, 0 will put the text
// in the center. Play with this to set the distance of
// your text.
for (int i = 0; i < value_degree.length; i++)
{
if (i == 0)
{
r = new Random();
color = Color.argb(100, r.nextInt(256), r.nextInt(256),
r.nextInt(256));
paint.setColor(color);
Log.i("Color", "" + color);
canvas.drawArc(rectf, temp, value_degree[i], true, paint);
paint.setColor(Color.BLACK); // set this to the text color.
paint.setTextSize(12);
/*
* paint.setTextAlign(Align.CENTER);
*/float medianAngle = (temp + (value_degree[i] / 2f))
* (float) Math.PI / 180f; // this angle will place the
// text in the center of the
// arc.
canvas.drawText(Title_value[i],
(float) (centerX + (radius * Math.cos(medianAngle))),
(float) (centerY + (radius * Math.sin(medianAngle))),
paint);
} else
{
temp += value_degree[i - 1];
r = new Random();
color = Color.argb(255, r.nextInt(256), r.nextInt(256),
r.nextInt(256));
paint.setColor(color);
Log.i("Else Color", "" + color);
canvas.drawArc(rectf, temp, value_degree[i], true, paint);
paint.setColor(Color.BLACK); // set this to the text color.
paint.setTextSize(12);
/*
* paint.setTextAlign(Align.CENTER);
*/float medianAngle = (temp + (value_degree[i] / 2f))
* (float) Math.PI / 180f; // this angle will place the
// text in the center of the
// arc.
canvas.drawText(Title_value[i],
(float) (centerX + (radius * Math.cos(medianAngle))),
(float) (centerY + (radius * Math.sin(medianAngle))),
paint);
}
}
}
}
It is showing chart in good quality in small screen sizes. But when I check it on big screen like S4 or other same screen size device it is shows a pie chart in very small size.
What should I do so it will draw perfectly on small as well as big screens?
Please help me, suggest something.
Also suggest how can I take color value to each slice in pie chart as it is assigned randomly.

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