I have a loop af paint that create a circle, all work but on different device I get different paint dimension, I think that is a problem of density, how can I solve the problem?
this is my code:
for(int i=0; i<360; i = i + 12){
x = (float) (view.getWidth()/2 + raggio_in * Math.cos(Math.toRadians(i)));
y = (float) (view.getHeight()/2 + raggio_in * Math.sin(Math.toRadians(i)));
Paint paint = new Paint();
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
paint.setColor(getResources().getColor(R.color.tutorial_text));
if(i < progress){
paint.setColor(Color.WHITE);
}
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate(i, x, y);
canvas.drawRoundRect(new RectF((float) x-deltaX, (float) y-deltaY, (float) x+deltaX, (float) y+deltaY), raggio_elemento, raggio_elemento, paint);
canvas.restore();
x = (float) (view.getWidth()/2 + raggio_out * Math.cos(Math.toRadians(i)));
y = (float) (view.getHeight()/2 + raggio_out * Math.sin(Math.toRadians(i)));
paint = new Paint();
paint.setColor(getResources().getColor(R.color.tutorial_text));
if(i < progress){
paint.setColor(Color.WHITE);
}
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate(i, x, y);
canvas.drawRoundRect(new RectF((float) x-deltaX, (float) y-deltaY, (float) x+deltaX, (float) y+deltaY), raggio_elemento, raggio_elemento, paint);
canvas.restore();
}
You can calculate the dimension in dp this way:
pixels * density + 0.5f
density can be access calling context.getResources().getDisplayMetrics().density
0.5f is used for rounding
Related
I'm trying to animate a background in a game I'm making.
https://imgur.com/kQGWsLu
Should look like this(terrible recording quality)
My first attempt work well on my phone but a lot of people saw their fps cut in half:
public void draw(Canvas canvas, Paint paint) {
if (mPaint == null) {
mPaint = new Paint();
mPaint.setColor(Color.GRAY);
mPaint.setStrokeWidth(1);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setShader(new RadialGradient(canvas.getWidth() / 4, canvas.getHeight() / 3,
Math.max(1, canvas.getHeight() / 2), Color.rgb(5, 12, 127), Color.rgb(32, 36, 100), Shader.TileMode.REPEAT));
}
width = canvas.getWidth();
height = canvas.getHeight();
canvas.save();
rotation += 4;
if (rotation > 360)
rotation = 0;
canvas.rotate(rotation, width / 2, height / 2);
canvas.drawCircle(width / 2, height / 2, height * 1.3f, mPaint);
canvas.restore();
}
So now I'm thinking about of pre rendering a image and drawing it as a bitmap that covers the screen and rotates instead. I also cut the calculations to only update every other frame instead.
public void draw(Canvas canvas, Paint paint) {
if (!initiated) {
centerX = (int) (canvas.getWidth() / 2);
centerY = (int) (canvas.getHeight() / 2);
width = (int) (canvas.getWidth() * 2f);
height = (int) (canvas.getHeight() * 2f);
map = factory.GFX().getBackground();
initiated = true;
}
if (!skipUpdate) {
x = CalculatorService.getXCircle(rotation, distance, centerX);
y = CalculatorService.getYCircle(rotation, distance, centerY);
body.set((int) x - width / 2,
(int) y - height / 2,
(int) x + (width / 2),
(int) y + (height / 2));
rotation += rotationSpeed;
if (rotation > 360)
rotation = 0;
}
skipUpdate = !skipUpdate;
canvas.drawBitmap(map, null, body, paint);
}
Though since the image is small in size 320*320 I had to scale it up and draw it bigger then the screen to avoid it moving out of bounds and showing the basic black background, and I'm not sure if this is really a improvement, do anyone have a better way of doing it or any advice on how I can improve?
What i am trying to do: I am trying to set ... after 7 characters in canavs for text
How to achieve this ?
private void drawText(Canvas canvas, float tmpAngle, float sweepAngle, String mStr) {
float cx = (mRadius) / 2 + mPadding;
float cy = (mRadius) / 2 + mPadding;
float radius = mRadius / 2 + mPadding;
float x = cx - radius + (mPadding * 2);
float y = cy;
float textWidth = radius - (mPadding * 10);
TextPaint textPaint = new TextPaint();
textPaint.set(this.mTextPaint);
textPaint.setColor(Color.WHITE);
Typeface plain = Typeface.createFromAsset(getContext().getAssets(), "fonts/AvenirLTStd-Heavy.otf");
Typeface bold = Typeface.create(plain, Typeface.BOLD);
textPaint.setTypeface(bold);
float angle = tmpAngle + sweepAngle / 2;
canvas.save();
canvas.rotate(180+(angle), cx, cy); // +180 for start from right
canvas.drawText(mStr, x, y, textPaint);
canvas.restore();
}
You can do something like this,
TextPaint textPaint = new TextPaint();
// textPaint attributes
CharSequence ellipsizedText = TextUtils.ellipsize("Your text", textPaint, width,
TextUtils.TruncateAt.END);
canvas.drawText(ellipsizedText, 0, ellipsizedText.length(), x0, y0, textPaint);
But in your case you need it after 7 characters, better just check the text length and append ... after the text.
Hi im having difficulties on drawing dots on arc's both ends (start and end)
Although I can draw arc on canvas. Heres my sample code for drawing arc.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float width = (float) getWidth();
float height = (float) getHeight();
float radius;
if (width > height) {
radius = height / 4;
} else {
radius = width / 4;
}
float center_x, center_y;
final RectF oval = new RectF();
center_x = width / 2;
center_y = height / 2;
oval.set(center_x - radius,
center_y - radius,
center_x + radius,
center_y + radius);
float percent = 25;
float arcRadius = 360;
float angle = arcRadius * (percent/100);
canvas.drawArc(oval, 270, 360, false, trackpaint);
canvas.drawArc(oval, 270, angle, false, arcPaint);
}
the only missing is putting circles on start and end points of the arc. I've tried this link but it doest work Calculate Arc Center Point, Knowing It's Start and End Degrees. Any help will be much appreciated. Thank you
the coordinate of the start point is:
double startX = Math.cos(Math.toRadians(270)) * radius + center_x;
double startY = Math.sin(Math.toRadians(270)) * radius + center_y;
the coordinate of the end point is:
double endX = Math.cos(Math.toRadians(270 + angle)) * radius + center_x;
double endY = Math.sin(Math.toRadians(270 + angle)) * radius + center_y;
and then you can draw circle using the start point and end point:
canvas.drawCircle(startX, startY, 10, paint);
canvas.drawCircle(endX, endY, 10, paint);
Get path from the ARC
Use PathMeasure class to retrieve Path length and path TAN, using starting or Ending X and Y coordinates of the ARC
Use this X and Y coordinates to draw circle.
Example for circle at the start of the ARC:
final Path mPath = new Path();
mPath.addArc(oval, startAngle, sweepAngle);
PathMeasure pm = new PathMeasure(mPath, false);
float[] xyCoordinate = { arcStarting.x , arcStarting.y };
float pathLength = pm.getLength();
pm.getPosTan(0, xyCoordinate, null);//"0 for starting point"
PointF point = new PointF(xyCoordinate[0], xyCoordinate[1]);
canvas.drawCircle(point.x, point.y, 10, YourPaintHere)
I have tried a lot of different approaches from examples around the web, but I can't seem to get this to work. I am trying to make a method that draws a curved line between 2 points on a canvas. The curve should be defined by a radius parameter.
Below is my current code.
public OverlayBuilder drawCurvedArrow(int startX, int startY, int endX, int endY, int curveRadius, int padding, int color) {
PointF mPoint1 = new PointF(startX, startY);
PointF mPoint2 = new PointF(endX, endY);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(12);
paint.setColor(color);
Path myPath = new Path();
myPath.moveTo(startX, startY);
myPath.quadTo(mPoint1.x, mPoint1.y, mPoint2.x, mPoint2.y);
canvas.drawPath(myPath, paint);
return this;
}
Edit
The problem is that I can't figure out how to curve the line that is drawn on the canvas.
I found a solution to my problem myself. Even though there were some great answers, they weren't an exact solution to my particular problem.
Here is what I did:
Found the point in between the 2 given points
Calculated the angle 90 degrees between the 2 points
Calculated the point X pixels from the middle point using the calculated degree from before.
Used "path.cubicTo" with these 3 points (Takes both negative and positive values to determine which way the line should curve).
Here is my code if anyone else should run into the same problem:
public OverlayBuilder drawCurvedArrow(int x1, int y1, int x2, int y2, int curveRadius, int color, int lineWidth) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(lineWidth);
paint.setColor(ContextCompat.getColor(context, color));
final Path path = new Path();
int midX = x1 + ((x2 - x1) / 2);
int midY = y1 + ((y2 - y1) / 2);
float xDiff = midX - x1;
float yDiff = midY - y1;
double angle = (Math.atan2(yDiff, xDiff) * (180 / Math.PI)) - 90;
double angleRadians = Math.toRadians(angle);
float pointX = (float) (midX + curveRadius * Math.cos(angleRadians));
float pointY = (float) (midY + curveRadius * Math.sin(angleRadians));
path.moveTo(x1, y1);
path.cubicTo(x1,y1,pointX, pointY, x2, y2);
canvas.drawPath(path, paint);
return this;
}
And here is an example of how the implementation looks like:
I think you are using wrong method for this purpose, one of the solutions that I can suggest is below
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);
and for calculation of startAngle you will need
int startAngle = (int) (180 / Math.PI * Math.atan2(point.y - point1.y, point.x - point1.x));
for sweepAngle you can find detailed description here.
Suppose you have two points mPoint1 and mPoint2
int w=canvas.getWidth();
int h=canvas.getHeight();
int w_2= (w / 2);
int h_2= (h / 2);
PointF mPoint1 = new PointF(0, 0); //starts at canvas left top
PointF mPoint2 = new PointF(w_2, h_2);//mid of the canvas
Path drawPath1 =drawCurve(mPoint1, mPoint2);
canvas.drawPath(drawPath1, paint);
Method to draw the line
private Path drawCurve(PointF mPointa, PointF mPointb) {
Path myPath = new Path();
myPath.moveTo(mPointa.x, mPointa.y);
final float x2 = (mPointb.x + mPointa.x) / 3;
final float y2 = (mPointb.y + mPointa.y) / 3;
myPath.quadTo(x2, y2, mPointb.x, mPointb.y);
return myPath;
}
I have an arc and i wish to draw scale marks at 0, 45, 90, 135, 180 degrees, can anyone help me with the math needed to achive the x,y of points 5 and 30 on this sketch?:
here is my code for drawing the 1 scale mark.
private void drawScale(Canvas canvas) {
//canvas.drawOval(scaleRect, scalePaint);
canvas.save();
Paint p = new Paint();
p.setColor(Color.WHITE);
p.setStrokeWidth(10f);
canvas.drawLine(rectF.left-getWidth()/20, rectF.height()/2, rectF.left, rectF.height()/2, p);
canvas.restore();
}
You can calculate its rotation using sin and cos. Lets assume that you have zero point A and want to rotate it to point B which is rotated for 30°.
Something like this:
Basically new point is at (cx+x,cy+y). In this particular case definition of sin and cos would be next:
sin = x/R
cos = y/R
It is not hard to get exact x and y. So to rotate point on particular angle in circle with know radius we need to calculate coordinates in next way:
x = cx + sin(angle) * R;
y = cy + cos(angle) * R;
Now lets get back to Android and Canvas!
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
float cx = getWidth() / 2f;
float cy = getHeight() / 2f;
float scaleMarkSize = getResources().getDisplayMetrics().density * 16; // 16dp
float radius = Math.min(getWidth(), getHeight()) / 2;
for (int i = 0; i < 360; i += 45) {
float angle = (float) Math.toRadians(i); // Need to convert to radians first
float startX = (float) (cx + radius * Math.sin(angle));
float startY = (float) (cy - radius * Math.cos(angle));
float stopX = (float) (cx + (radius - scaleMarkSize) * Math.sin(angle));
float stopY = (float) (cy - (radius - scaleMarkSize) * Math.cos(angle));
canvas.drawLine(startX, startY, stopX, stopY, scalePaint);
}
canvas.restore();
}
Code will draw marks with step of 45°. Note you need to convert angle to radians and for Y axis I used minus cause on canvas it is flipped. Here is what I have got:
If you know the point at the center of the circle and the radius of the circle it becomes pretty easy if you use vectors to your advantage.
First you're gonna need the unit vectors at each angle
0 deg -> (-1,0)
45 deg -> (-1/sqrt(2), (1/sqrt(2))
90 deg -> (0,1)
135 deg -> (1/sqrt(2), (1/sqrt(2))
180 deg -> (1,0)
You can then calculate the necessary points using the formula below
point = center + (unit vector * distance from center)
Here is a more concrete example since andrew added one.
private static final float RADIUS = 400.0f;
private static final float MARK_LENGTH = 30.0f;
private static final UnitVector[] UNIT_VECTORS = new UnitVector[] {
new UnitVector(-1,0), // 0 deg
new UnitVector((float) (-1/Math.sqrt(2)), (float) (1/Math.sqrt(2))), // 45 deg
new UnitVector(0, 1), // 90 deg
new UnitVector((float) (1/Math.sqrt(2)), (float) (1/Math.sqrt(2))), // 135 deg
new UnitVector(1, 0), // 180 deg
new UnitVector((float) (1/Math.sqrt(2)), (float) (-1/Math.sqrt(2))), // 225 deg
new UnitVector(0, -1), // 270 deg
new UnitVector((float) (-1/Math.sqrt(2)), (float) (-1/Math.sqrt(2))), // 315 deg
};
static class UnitVector {
final float x;
final float y;
UnitVector(final float x, final float y) {
this.x = x;
this.y = y;
}
}
// Call this from onDraw
public void drawMarks(final Canvas canvas) {
for (final UnitVector unitVector : UNIT_VECTORS) {
this.drawMarkWithVector(unitVector, canvas);
}
}
private void drawMarkWithVector(final UnitVector unitVector, final Canvas canvas) {
final float centerPointX = this.getWidth() / 2;
final float centerPointY = this.getHeight() / 2;
final float startX = centerPointX + (unitVector.x * RADIUS);
final float startY = centerPointY + (unitVector.y * RADIUS);
final float endX = centerPointX + (unitVector.x * (RADIUS + MARK_LENGTH));
final float endY = centerPointY + (unitVector.y * (RADIUS + MARK_LENGTH));
canvas.drawLine(startX, startY, endX, endY, this.paint);
}
Here is the result of the code above