add a circle at the edge of arc android? - android

how to add small circle at the edge of arc.
and it should be also move with arc edge in the clock direction.
right now i am successfully animate the arc using changing the sweep angle.
and black dot is remaining.
below is the code of getView and animation class
--- init method and implement constructor ----
mRectF = new RectF(mWidth / 2 - 360, mHeight / 2 - 360, mWidth / 2 + 360, mHeight / 2 + 360);
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//draw circle background
mPaint.setColor(getResources().getColor(R.color.timer_background_color));
canvas.drawCircle(mWidth / 2, mHeight / 2, 360, mPaint);
mPaint.setColor(getResources().getColor(R.color.actionbar_back_color));
canvas.drawArc(mRectF, mStartAnagle, mSweepAngle, false, mPaint);
}
public class TimerAnimation extends Animation{
public TimerAnimation (float startAngle, float sweepAngle, long duration) {
mStartAnagle = startAngle;
mSweepAngle = sweepAngle;
setDuration(duration);
setRepeatCount(Animation.INFINITE);
setInterpolator(new LinearInterpolator());
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if (!isComplete) {
mSweepAngle = mSweepAngle + 6;
if (mSweepAngle >= 360) {
isComplete = true;
mSweepAngle = 360;
}
} else {
mStartAnagle = mStartAnagle + 6;
mSweepAngle = mSweepAngle - 6;
if (mStartAnagle >= 360)
mStartAnagle = 0;
if (mStartAnagle == 270 || mSweepAngle <= 0) {
isComplete = false;
mSweepAngle = 0;
}
}
invalidate();
}
}

Maybe you should use Path:
Path path = new Path();
// Set the starting position of the path to (0,0).
path.moveTo(0, 0);
path.arcTo(...); //draw your arc here
path.circleTo(); //draw a small circle here at the end of arc
Also maybe you should calc the arc's end position and use as a center for small circle.

Step 1: Calculate the position of the black dot
Suggest the central position is (centerX, centerY), the position of black dot is (x,y), then,
x = radius * cos(mStartAnagle+mSweepAngle) + centerX;
y = radius * sin(mStartAnagle+mSweepAngle) + centerY;
Step 2: Draw the black dot
Suggest the dot image is R.drawable.dot ,
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.dot)
.copy(Bitmap.Config.ARGB_8888, true);
canvas.drawBitmap(bitmap, x, y, null);

Related

How to rotate text drawn using canvas.drawTextOnPath()?

I am using drawTextOnPath() to create a bent text like in given picture below but I can't figure out how to rotate the text?
What I want is below highlighted in picture with arrow and circle.
What i have been able to achieve is below
So I just want to rotate these texts?
You can achieve the result you want without drawTextOnPath() using:
float radius = 300;
float cx = getWidth() / 2; //Center of the circle
float cy = getHeight() / 2; //Center of the circle
for (int degree = 0; degree < 360; degree += 30) {
canvas.save();
canvas.translate(cx, cy);
canvas.rotate(degree);
String text = "" + degree;
float textCenterX = radius + textPaint.measureText(text) / 2;
float textCenterY = 0 - textPaint.getTextSize() / 2;
if (degree < 180) {
canvas.rotate(-90, textCenterX, textCenterY);
} else {
canvas.rotate(+90, textCenterX, textCenterY);
}
canvas.drawText("" + degree, radius, 0, textPaint);
canvas.restore();
}
You just need to flip the canvas and draw on it
canvas.save()
canvas.scale(1, -1, width / 2, height / 2)
// your drawing code
canvas.restore()

Draw rectangles on circle formula

I'm trying to draw the spectrum of an audio file on a circle. Like this:
So on the circle I just want rectangles drawn like you see on the image.
I've got this code:
public void onRender(Canvas canvas, FFTData data, Rect rect) {
canvas.drawCircle(rect.width()/2, rect.height()/2, 200, mPaint);
for (int i = 0; i < data.bytes.length / mDivisions; i++) {
byte rfk = data.bytes[mDivisions * i];
byte ifk = data.bytes[mDivisions * i + 1];
float magnitude = (rfk * rfk + ifk * ifk);
int dbValue = (int) (10 * Math.log10(magnitude));
}
}
Where FFTData is the Fast Fourier Transformation data that Android gives me. Now in my dbValue I got the strength of the signal. mDivisions is how much bars I want. Currently set on 16 because I don't know how much I can set on the circle.
I'm stuck on how I can draw the rectangle with his center on the circle line... So I want a rectangle whose height is based on the dbValue so that I get high and low rectangles. And the center must be placed on my circle line.
Can someone help me on this math formula?
Run a loop over all 360 degrees of the circle (at wanted step), and, for each point, convert Polar (this angle and the radius of the circle) coordinates into Cartesian, as described here, for instance. This way you get the location of the centre of your rectangle.
Translate the system of the coordinates, making origin to be at the wanted point on the circle line and then rotate by the circle angle at that point.
Alternatively, you can build a trapezoid by getting corners at angle +- some offset and radius +- some offset (proportional to your value to plot). It will have shorter inner edge and longer outer edge. Such trapezoids may look better if painted side by side.
i think all you have needed is a pencil and a paper and a little math and also some free time to play :-)
public class MainActivity extends Activity {
ImageView drawingImageView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
drawingImageView = (ImageView) this.findViewById(R.id.DrawingImageView);
Paint paint;
paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(16);
final Bitmap bitmap = Bitmap.createBitmap((int) getWindowManager()
.getDefaultDisplay().getWidth(), (int) getWindowManager()
.getDefaultDisplay().getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
int centerX =400;
int centerY =400;
int R = 200;
canvas.drawCircle(centerX, centerY, R, paint);
int h = 100;
paint.setColor(Color.RED);
Path p = new Path();
p.moveTo(centerX + R - h/2, centerY);
p.lineTo(centerX + R + h/2, centerY);
canvas.drawPath(p, paint);
p = mySpectrumDrawer(centerX,centerY,R,h,15);
canvas.drawPath(p, paint);
h = 50;
p = mySpectrumDrawer(centerX,centerY,R,h,30);
canvas.drawPath(p, paint);
h = 60;
p = mySpectrumDrawer(centerX,centerY,R,h,60);
canvas.drawPath(p, paint);
h = 80;
p = mySpectrumDrawer(centerX,centerY,R,h,90);
canvas.drawPath(p, paint);
drawingImageView.setImageBitmap(bitmap);
}
private Path mySpectrumDrawer(int centerX, int centerY,int R,int height, int angel){
Path p = new Path();
int dX = (int) (R*(Math.cos(Math.toRadians(angel))));
int dY = (int) (R*(Math.sin(Math.toRadians(angel))));
int dhx = (int) (height/2*(Math.cos(Math.toRadians(angel))));
int dhy = (int) (height/2*(Math.sin(Math.toRadians(angel))));
p.moveTo(centerX + dX - dhx , centerY - dY + dhy);
p.lineTo(centerX + dX + dhx , centerY - dY - dhy);
return p;
}
}

Redraw View Android

I now have a View that is added programically after the onCreate (Depending on some other variables). Everything works as it should and it draws part of a circle.
But my question is how do i redraw it later on ? I need to change the angle in the circle after some data is fetched.
Code for the WindRose :
public class WindRose extends View {
public WindRose(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvasTest = canvas;
float height = (float) getHeight();
float width = (float) getWidth();
float radius;
if (width > height) {
radius = height / 2;
} else {
radius = width / 2;
}
// radius = (height )/ 2;
Path path = new Path();
path.addCircle(width, height, radius, Path.Direction.CCW);
// / 2
Resources resources = getResources();
int color = resources.getColor(R.color.green_back);
paint.setColor(color);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.FILL);
float center_x, center_y;
center_x = width / 2;
center_y = height / 2;
//Formulas :
//SD = Start Degree
//ED = End Degree
//If cakepiece passes 0 (East)
//SD, 360-(SD+ED)
//Else :
//SD, (ED-SD)
oval.set(center_x - radius, center_y - radius, center_x + radius, center_y + radius);
if (End > Start) {
canvas.drawArc(oval, Start, (End - Start), true, paint);
} else if (End < Start) {
canvas.drawArc(oval, Start, ((360 - Start) + End), true, paint);
}
}
}
If i update the Start and End variables nothing happens. i have also tried to call invalidate on the class but also there i dont get any redrawing.
Invalidate that i've tried :
WindRose windrose = new WindRose(this);
windrose.invalidate();
Was called from the main class which WindRose is part of.
How should i do this correctly ?
Maybe calling the invalidate() method of the view will help.
You can read more here(http://developer.android.com/reference/android/view/View.html), but:
"To force a view to draw, call invalidate()."
Note, also that you can invalidate only parts of a view

How to draw Arc between two points on the Canvas?

I have two points in the canvas, now I'm able to draw a line between those points like this below image by using
This code canvas.drawLine(p1.x, p1.y, p2.x, p2.y, paint);
I want to draw the arc between two points like below image.
How can I draw like this.
Finally I got the solution from this code:
float radius = 20;
final RectF oval = new RectF();
oval.set(point1.x - radius, point1.y - radius, point1.x + radius, point1.y+ radius);
Path myPath = new Path();
myPath.arcTo(oval, startAngle, -(float) sweepAngle, true);
To calculate startAngle, use this code:
int startAngle = (int) (180 / Math.PI * Math.atan2(point.y - point1.y, point.x - point1.x));
Here, point1 means where you want to start drawing the Arc. sweepAngle means the angle between two lines. We have to calculate that by using two points like the blue points in my Question image.
Do something like this:
//Initialized paint on a class level object.
Paint p = new Paint();
p.setColor(Color.BLACK);
//Calculate the rect / bounds of oval
RectF rectF = new RectF(50, 20, 100, 80);
#Override
protected void onDraw(Canvas canvas) {
//Do the drawing in onDraw() method of View.
canvas.drawArc (rectF, 90, 45, false, p);
}
first we need to visual how the coordinates are in terms of start and sweep angels then it will become more clear.
so if you wanted just the right top piece of the circle, we could do something like this:
val rect = RectF(0f, 0f, 500f, 300f)
val paint = Paint()
paint.apply {
strokeWidth = 5f
setStyle(Paint.Style.STROKE)
color = COLOR.BLUE
}
path.addArc(rect, 270f, 90f)
..
this starts at 270 (per the diagram above and 'sweeps` 90 degrees forward. you then have this shape:
let's create one more so you get the hang of it. this time let's use a negative value: we want to create a semi half moon (arc) starting from the right side:
path.addArc(rect, 0f, -180f)
here we started at 0 and 'sweeped` -180 degrees.
and the results are:
I was trying to do something a little different and it's all about calculating sweep and start angles.
I wanted to show an arc that represents progress on a circle that goes from top to bottom.
So I had progress value from 0...100 and I want to show an arc that start from top to bottom to fill the circle when the progress is 100.
To calculate the sweepAngle I use:
int sweepAngle = (int) (360 * (getProgress() / 100.f));
Next is to calculate the startAngle
int startAngle = 270 - sweepAngle / 2;
Start Angle is calculated this way because:
It's always going to start from the left side, starting from the top to bottom. So starting angle at the top equals 270 (Note that it goes clockwise and 0 = 3 o'clock, so 12 o'clock equals 270 degrees)
Next I want to calculate how far I'm going to get away from my starting point (270) and to do that I only calculate half of the sweep angle because only half of the arc will be on the left side and the other half on the right side.
So considering I have progress of 25%
sweepAngle = 90 degrees (90 degrees is quarter of a circle)
start angle = 225 (45 degrees away from 270)
If you want the progress to go from other sides (Left to right, right to left etc..) you will only need to replace 270 with the starting the angle.
I may be late to answer but I got more information.
After Android Lollipop there are two ways to address this problem
public void drawArc(RectF oval, float startAngle, float sweepAngle,
boolean useCenter, Paint paint)
public void drawArc(float left, float top, float right, float bottom,
float startAngle, float sweepAngle, boolean useCenter, Paint paint)
Usage:
RectF rectF = new RectF(left, top, right, bottom);
// method 1
canvas.drawArc (rectF, 90, 45, true, paints[0]);
// method 2
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
canvas.drawArc (left, top, right, bottom, 0, 45, true, paints[1]);
}
Sweep angle is nothing more than angle of Sector which is drawn clockwise eg. for below code
private void drawArcs(Canvas canvas) {
RectF rectF = new RectF(left, top, right, bottom);
// white arc
canvas.drawArc (rectF, 90, 45, true, paints[0]);
// Green arc
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
canvas.drawArc (left, top, right, bottom, 0, 45, true, paints[1]);
}
// Red stroked arc
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
canvas.drawArc (left, top, right, bottom, 180, 45, true, paints[2]);
}
}
Result will look like this
Same can be achieved with the help of defining Paths and then iterating over them in onDraw method as illustrated in this snippet:
public class ArcDrawable extends Drawable {
private int left, right, top, bottom;
private Paint[] paints = new Paint[3];
private HashMap<Path, Paint> pathMap = new HashMap();
public ArcDrawable() {
// white paint
Paint whitePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
whitePaint.setColor(Color.WHITE);
paints[0]= whitePaint;
// green paint
Paint greenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
greenPaint.setColor(Color.GREEN);
paints[1]= greenPaint;
// red paint
Paint redPaint =new Paint(Paint.ANTI_ALIAS_FLAG);
redPaint.setColor(Color.RED);
redPaint.setStyle(Paint.Style.STROKE);
paints[2]= redPaint;
}
#Override
public void draw(Canvas canvas) {
//----------USE PATHS----------
// Define and use custom Path
for (Map.Entry<Path, Paint> entry : pathMap.entrySet()) {
// Draw Path on respective Paint style
canvas.drawPath(entry.getKey(), entry.getValue());
}
// -------OR use conventional Style---------
//drawArcs(canvas);
}
//Same result
private void drawArcs(Canvas canvas) {
RectF rectF = new RectF(left, top, right, bottom);
// method 1
canvas.drawArc (rectF, 90, 45, true, paints[0]);
// method 2
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
canvas.drawArc (left, top, right, bottom, 0, 45, true, paints[1]);
}
// method two with stroke
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
canvas.drawArc (left, top, right, bottom, 180, 45, true, paints[2]);
}
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
int width = bounds.width();
int height = bounds.height();
left = bounds.left;
right = bounds.right;
top = bounds.top;
bottom = bounds.bottom;
final int size = Math.min(width, height);
final int centerX = bounds.left + (width / 2);
final int centerY = bounds.top + (height / 2);
pathMap.clear();
//update pathmap using new bounds
recreatePathMap(size, centerX, centerY);
invalidateSelf();
}
private Path recreatePathMap(int size, int centerX, int centerY) {
RectF rectF = new RectF(left, top, right, bottom);
// first arc
Path arcPath = new Path();
arcPath.moveTo(centerX,centerY);
arcPath.arcTo (rectF, 90, 45);
arcPath.close();
// add to draw Map
pathMap.put(arcPath, paints[0]);
//second arc
arcPath = new Path();
arcPath.moveTo(centerX,centerY);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
arcPath.arcTo (rectF, 0, 45);
}
arcPath.close();
// add to draw Map
pathMap.put(arcPath, paints[1]);
// third arc
arcPath = new Path();
arcPath.moveTo(centerX,centerY);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
arcPath.arcTo (rectF, 180, 45);
}
arcPath.close();
// add to draw Map
pathMap.put(arcPath, paints[2]);
return arcPath;
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(#Nullable ColorFilter colorFilter) {
}
#Override
public int getOpacity() {
return 0;
}
}
Complete source code:
https://github.com/hiteshsahu/Arc-Drawable
a sample for draw arc.
public static Bitmap clipRoundedCorner(Bitmap bitmap, float r, boolean tr, boolean tl, boolean bl, boolean br)
{
int W = bitmap.getWidth();
int H = bitmap.getHeight();
if (r < 0)
r = 0;
int smallLeg = W;
if(H < W )
smallLeg = H;
if (r > smallLeg)
r = smallLeg / 2;
float lineStop = r/2;
Path path = new Path();
path.moveTo(0,0);
if(tr)
{
path.moveTo(0, lineStop);
path.arcTo(new RectF(0,0, r,r), 180, 90, false);
}
path.lineTo(W-lineStop, 0);
if(tl)
path.arcTo(new RectF(W-r,0, W,r), 270, 90, false);
else
path.lineTo(W, 0);
path.lineTo(W, H-lineStop);
if(bl)
path.arcTo(new RectF(W-r,H-r, W,H), 0, 90, false);
else
path.lineTo(W, H);
path.lineTo(lineStop, H);
if(br)
path.arcTo(new RectF(0,H-r, r,H), 90, 90, false);
else
path.lineTo(0,H);
if(tr)
path.lineTo(0,lineStop);
else
path.lineTo(0,0);
Bitmap output = Bitmap.createBitmap(W, H, Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
canvas.drawPath(path, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, 0, 0, paint);
return output;
}
A simple solution was suggested here by Langkiller. This draws a cubic line from the start point via the control point to the end point.
Path path = new Path();
float startX = 0;
float startY = 2;
float controlX = 2;
float controlY = 4;
float endX = 4
float endY = 2
conePath.cubicTo(startX, startY, controlX, controlY,endX, endY);
Paint paint = new Paint();
paint.setARGB(200, 62, 90, 177);
paint.setStyle(Paint.Style.FILL);
canvas.drawPath(path, paint)

How to draw a triangle, a star, a square or a heart on the canvas?

I am able to draw a circle and a rectangle on canvas by using
path.addCircle()
and
path.addRect().
And now I am wondering how to draw a triangle or a star or a square or a heart?
For future direct answer seekers, I have drawn an almost symmetric star using canvas, as shown in the image:
The main tool is using Paths.
Assuming you have setup:
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
Path path = new Path();
Then in you onDraw you can use the path like I do below. It will scale properly to any sizes canvas
#Override
protected void onDraw(Canvas canvas) {
float mid = getWidth() / 2;
float min = Math.min(getWidth(), getHeight());
float fat = min / 17;
float half = min / 2;
float rad = half - fat;
mid = mid - half;
paint.setStrokeWidth(fat);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(mid + half, half, rad, paint);
path.reset();
paint.setStyle(Paint.Style.FILL);
// top left
path.moveTo(mid + half * 0.5f, half * 0.84f);
// top right
path.lineTo(mid + half * 1.5f, half * 0.84f);
// bottom left
path.lineTo(mid + half * 0.68f, half * 1.45f);
// top tip
path.lineTo(mid + half * 1.0f, half * 0.5f);
// bottom right
path.lineTo(mid + half * 1.32f, half * 1.45f);
// top left
path.lineTo(mid + half * 0.5f, half * 0.84f);
path.close();
canvas.drawPath(path, paint);
super.onDraw(canvas);
}
For everybody that needs a heart shape:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.view.View;
public class Heart extends View {
private Path path;
private Paint paint;
public Heart(Context context) {
super(context);
path = new Path();
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Fill the canvas with background color
canvas.drawColor(Color.WHITE);
paint.setShader(null);
float width = getContext().getResources().getDimension(R.dimen.heart_width);
float height = getContext().getResources().getDimension(R.dimen.heart_height);
// Starting point
path.moveTo(width / 2, height / 5);
// Upper left path
path.cubicTo(5 * width / 14, 0,
0, height / 15,
width / 28, 2 * height / 5);
// Lower left path
path.cubicTo(width / 14, 2 * height / 3,
3 * width / 7, 5 * height / 6,
width / 2, height);
// Lower right path
path.cubicTo(4 * width / 7, 5 * height / 6,
13 * width / 14, 2 * height / 3,
27 * width / 28, 2 * height / 5);
// Upper right path
path.cubicTo(width, height / 15,
9 * width / 14, 0,
width / 2, height / 5);
paint.setColor(Color.RED);
paint.setStyle(Style.FILL);
canvas.drawPath(path, paint);
}
}
Sorry for all the numbers but these worked best for me :) The result looks like this:
You have to find out the math behind that figures. The triangle and the star are quite easy to draw. Here is how you can draw a heart: http://www.mathematische-basteleien.de/heart.htm
To draw special paths you should create them by adding points, ellipses etc. The canvas supports a clipping mask of a specified path, so you can set the clipping mask of a heart, push the paths to the matrix, draw the content of the heart, and then pop it again.
That's what I'm doing to achieve a simulated 2D page curl effect on andriod: http://code.google.com/p/android-page-curl/
Hope this helps!
this method will return a path with the number of corners given inside a square of the given width. Add more parameters to handle small radius and such things.
private Path createStarBySize(float width, int steps) {
float halfWidth = width / 2.0F;
float bigRadius = halfWidth;
float radius = halfWidth / 2.0F;
float degreesPerStep = (float) Math.toRadians(360.0F / (float) steps);
float halfDegreesPerStep = degreesPerStep / 2.0F;
Path ret = new Path();
ret.setFillType(FillType.EVEN_ODD);
float max = (float) (2.0F* Math.PI);
ret.moveTo(width, halfWidth);
for (double step = 0; step < max; step += degreesPerStep) {
ret.lineTo((float)(halfWidth + bigRadius * Math.cos(step)), (float)(halfWidth + bigRadius * Math.sin(step)));
ret.lineTo((float)(halfWidth + radius * Math.cos(step + halfDegreesPerStep)), (float)(halfWidth + radius * Math.sin(step + halfDegreesPerStep)));
}
ret.close();
return ret;
}
If you need to draw a star inside a square, you can use the code below.
posX and posY are the coordinates for the upper left corner of the square that encloses the tips of the star (the square is not actually drawn).
size is the width and height of the square.
a is the top tip of the star. The path is created clockwise.
This is by no means a perfect solution, but it gets the job done very quickly.
public void drawStar(float posX, float posY, int size, Canvas canvas){
int hMargin = size/9;
int vMargin = size/4;
Point a = new Point((int) (posX + size/2), (int) posY);
Point b = new Point((int) (posX + size/2 + hMargin), (int) (posY + vMargin));
Point c = new Point((int) (posX + size), (int) (posY + vMargin));
Point d = new Point((int) (posX + size/2 + 2*hMargin), (int) (posY + size/2 + vMargin/2));
Point e = new Point((int) (posX + size/2 + 3*hMargin), (int) (posY + size));
Point f = new Point((int) (posX + size/2), (int) (posY + size - vMargin));
Point g = new Point((int) (posX + size/2 - 3*hMargin), (int) (posY + size));
Point h = new Point((int) (posX + size/2 - 2*hMargin), (int) (posY + size/2 + vMargin/2));
Point i = new Point((int) posX, (int) (posY + vMargin));
Point j = new Point((int) (posX + size/2 - hMargin), (int) (posY + vMargin));
Path path = new Path();
path.moveTo(a.x, a.y);
path.lineTo(b.x, b.y);
path.lineTo(c.x, c.y);
path.lineTo(d.x, d.y);
path.lineTo(e.x, e.y);
path.lineTo(f.x, f.y);
path.lineTo(f.x, f.y);
path.lineTo(g.x, g.y);
path.lineTo(h.x, h.y);
path.lineTo(i.x, i.y);
path.lineTo(j.x, j.y);
path.lineTo(a.x, a.y);
path.close();
canvas.drawPath(path, paint);
}
In addition to ellipse and rectangular you will need those two (as minimum):
drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
drawLines(float[] pts, int offset, int count, Paint paint)
example:
How to draw a line in android
Documentation on Canvas: http://developer.android.com/reference/android/graphics/Canvas.html
Its very good to use instance of Shape class ))
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
HeartShape shape = new HeartShape();
ShapeDrawable shapeDrawable = new ShapeDrawable(shape);
shapeDrawable.setColorFilter(new LightingColorFilter(0, Color.BLUE));
LinearLayout linearLayout = new LinearLayout(this);
linearLayout.setLayoutParams(new LinearLayout.LayoutParams(350 * 3, 350 * 3));
linearLayout.setBackground(shapeDrawable);
setContentView(linearLayout);
}
Create a shape class which was render nice Heart
public class HeartShape extends Shape {
private final int INVALID_SIZE = -1;
private Path mPath = new Path();
private RectF mRectF = new RectF();
private float mOldWidth = INVALID_SIZE;
private float mOldHeight = INVALID_SIZE;
private float mScaleX, mScaleY;
public HeartShape() {
}
#Override
public void draw(Canvas canvas, Paint paint) {
canvas.save();
canvas.scale(mScaleX, mScaleY);
float width = mRectF.width();
float height = mRectF.height();
float halfWidth = width/2;
float halfHeight = height/2;
float stdDestX = 5 * width / 14;
float stdDestY = 2 * height / 3;
PointF point1 = new PointF(stdDestX, 0);
PointF point2 = new PointF(0, height / 15);
PointF point3 = new PointF(stdDestX / 5, stdDestY);
PointF point4 = new PointF(stdDestX, stdDestY);
// Starting point
mPath.moveTo(halfWidth, height / 5);
mPath.cubicTo(point1.x, point1.y, point2.x, point2.y, width / 28, 2 * height / 5);
mPath.cubicTo(point3.x, point3.y, point4.x, point4.y, halfWidth, height);
canvas.drawPath(mPath, paint);
canvas.scale(-mScaleX, mScaleY, halfWidth, halfHeight);
canvas.drawPath(mPath, paint);
canvas.restore();
}
#Override
protected void onResize(float width, float height) {
mOldWidth = mOldWidth == INVALID_SIZE ? width : Math.max(1, mOldWidth);
mOldHeight = mOldHeight == INVALID_SIZE ? height : Math.max(1, mOldHeight);
width = Math.max(1, width);
height = Math.max(1, height);
mScaleX = width / mOldWidth;
mScaleY = height / mOldHeight;
mOldWidth = width;
mOldHeight = height;
mRectF.set(0, 0, width, height);
}
#Override
public void getOutline(#NonNull Outline outline) {
// HeartShape not supported outlines
}
#Override
public HeartShape clone() throws CloneNotSupportedException {
HeartShape shape = (HeartShape) super.clone();
shape.mPath = new Path(mPath);
return shape;
}
}
Well if you want to draw only the shapes you specify I recommend first creat a class for each shape and make each shape implement draw() method where you can pass canvas and paint object.
For example to create multiple stars, first create a class "Star" and implement the logic there.
class Star(
var cx: Float = 0f,
var cy: Float = 0f,
var radius: Float = 0f,
var angle: Float = 0f,
var color: Int = Color.WHITE,
var numberOfSpikes: Int = 5,
var depth: Float = 0.4f
) {
val path: Path = Path()
val point: PointF = PointF()
fun init() {
path.rewind()
var totalAngle = 0f
for (i in 0 until numberOfSpikes * 2) {
val x = cx
var y = cy
y -= if (i % 2 != 0) (radius * depth)
else (radius * 1f)
StaticMethods.rotate(cx, cy, x, y, totalAngle, false, point)
totalAngle += 360f / (numberOfSpikes * 2)
if (i == 0) {
path.moveTo(point.x, point.y)
} else {
path.lineTo(point.x, point.y)
}
}
}
fun draw(canvas: Canvas, paint: Paint) {
paint.apply {
style = Paint.Style.FILL
color = this#Star.color
}
canvas.drawPath(path, paint)
}
}
object StaticMethods {
/**
* Rotate a point around a center with given angle
* #param cx rotary center point x coordinate
* #param cy rotary center point y coordinate
* #param x x coordinate of the point that will be rotated
* #param y y coordinate of the point that will be rotated
* #param angle angle of rotation in degrees
* #param anticlockWise rotate clockwise or anti-clockwise
* #param resultPoint object where the result rotational point will be stored
*/
fun rotate(cx: Float, cy: Float, x: Float, y: Float, angle: Float, anticlockWise: Boolean = false, resultPoint: PointF = PointF()): PointF {
if (angle == 0f) {
resultPoint.x = x
resultPoint.y = y
return resultPoint
}
val radians = if (anticlockWise) {
(Math.PI / 180) * angle
} else {
(Math.PI / -180) * angle
}
val cos = Math.cos(radians)
val sin = Math.sin(radians)
val nx = (cos * (x - cx)) + (sin * (y - cy)) + cx
val ny = (cos * (y - cy)) - (sin * (x - cx)) + cy
resultPoint.x = nx.toFloat()
resultPoint.y = ny.toFloat()
return resultPoint
}
/**
* Inline function that is called, when the final measurement is made and
* the view is about to be draw.
*/
inline fun View.afterMeasured(crossinline function: View.() -> Unit) {
viewTreeObserver.addOnGlobalLayoutListener(object :
ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
if (measuredWidth > 0 && measuredHeight > 0) {
viewTreeObserver.removeOnGlobalLayoutListener(this)
function()
}
}
})
}
}
And then create a custom view where you can draw your shapes, below is the code that creates 100 random stars and draws them on canvas.
class StarView : View {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
val stars: ArrayList<Helper.Star> = arrayListOf()
val paint: Paint = Paint().apply {
isAntiAlias = true
}
init {
// after the view is measured and we have the width and height
afterMeasured {
// create 100 stars
for (i in 0 until 100) {
val star = Helper.Star(
cx = width * Math.random().toFloat(),
cy = height * Math.random().toFloat(),
radius = width * 0.1f * Math.random().toFloat(),
)
star.init()
stars.add(star)
}
}
}
override fun onDraw(canvas: Canvas) {
stars.forEach {
it.draw(canvas, paint)
}
}
}
Here is the result:
If you want to create a lot of different complex shapes you can use this library. Where you can pass svg shapes using the Path Data as a string. By first creating complex shapes using any SVG vector editor such as
Microsoft Expression Design
Adobe Illustrator
Inkscape

Categories

Resources