When I draw path in android, the line seems too thin. So I made a experiment:
draw a round rect using drawPath and using drawRoundRect.
here is my code:
//setPaint
mStrokeWidth = 1;
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setColor(Color.RED);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
//draw using drawRoundRect
mStrokeWidth = 1;
#Override
public void draw(Canvas canvas) {
Rect bounds = getBounds();
mRect.set(bounds.left + mStrokeWidth / 2,
bounds.top + mStrokeWidth / 2,
bounds.right - mStrokeWidth / 2,
bounds.bottom - mStrokeWidth / 2
);
canvas.drawRoundRect(mRect, 10, 10, mPaint);
}
//draw using drawPath
mStrokeWidth = 1;
#Override
public void draw(Canvas canvas) {
Rect bounds = getBounds();
mRect.set(bounds.left + mStrokeWidth / 2,
bounds.top + mStrokeWidth / 2,
bounds.right - mStrokeWidth / 2,
bounds.bottom - mStrokeWidth / 2
);
Path path = new Path();
path.addRoundRect(mRect, 10, 10, Path.Direction.CCW);
canvas.drawPath(path, mPaint);
}
here is the result:
the left is using drawRoundRect and the right using drawPath
you can see that the left is thicker than the right. I want to know what caused the phenomenon?
Related
I've read over 20 questions/answers but I still can't get what I want. I want to cut a circle inside a rectangle as seen below:
Here is my code:
#Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setARGB(180, 0, 0, 0);
canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
Path circularPath = new Path();
circularPath.addCircle(getWidth() / 2, getHeight() / 2, radius, Path.Direction.CCW);
canvas.clipPath(circularPath, Region.Op.REPLACE);
canvas.drawColor(0x00000000);
}
My background (setARGB) displays correctly, however nothing is clipped. I've also tried different Op values other than REPLACE, forced software rasterization (as I've read on some Android versions clipPath doesn't support some of the Ops) by calling setLayerType(LAYER_TYPE_SOFTWARE, null); on constructor, but no avail. How do I achieve the desired effect?
Note: My minimum SDK version is 15, so I don't need to support anything lower than 4.0.
Use clipPath before drawRect.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = this.getWidth();
int height = this.getHeight();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawPaint(mPaint);
float rectWidth = Utils.dpToPx(100.0f);
Path circularPath = new Path();
circularPath.addCircle(width / 2.0f, rectWidth / 2.0f, rectWidth / 3.0f, Path.Direction.CCW);
canvas.clipPath(circularPath, Region.Op.DIFFERENCE);
mPaint.setColor(Color.BLUE);
canvas.drawRect((width - rectWidth) / 2.0f, 0.0f, ((width - rectWidth) / 2.0f) + rectWidth, rectWidth, mPaint);
}
Try clipping your path in dispatchDraw():
#Override
protected void dispatchDraw(Canvas canvas)
{
canvas.clipPath(mClipPath, mRegion); // previously created path & region
super.dispatchDraw(canvas);
}
Remove the path clipping code from your onDraw method, and that should do it.
Edit:
When creating your path, make sure you do so only after a measure occurred, for example:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mClipPath.reset();
float radius = Math.min((float)getMeasuredWidth() / 2f, (float)getMeasuredHeight() / 2f) + 5;
mClipPath.addCircle((float)getMeasuredWidth() / 2f, (float)getMeasuredHeight() / 2f, radius, Path.Direction.CCW);
}
I have a small question. I'm painting some RectFs on my canvas, but nothing happened.
I paint one filled RectF and another stroked RectF => filled background with a border.
For the first "field" everything is ok, but not for the second "field" (rect2).
The positions are checked. The last text is printed in that RectF (rect2) where the rect is not painted.
I uploaded an image on my FTP server. For the description of my problem it might be easier to understand.
Here is my code. I think it is a logical error?
// Fill rect
rect.set(fPosition, fPositionY, pts[2] - 10, fRectHeight);
paint.setColor(Color.GREEN);
paint.setStyle(android.graphics.Paint.Style.FILL);
paint.setStrokeWidth(0);
canvas.drawRoundRect(rect, 10, 10, paint);
// Border
paint.setColor(Color.BLACK);
paint.setStyle(android.graphics.Paint.Style.STROKE);
paint.setStrokeWidth(3);
canvas.drawRoundRect(rect, 10, 10, paint);
// Text
float fTextSize = fRectHeight - 40;
TextPaint textPaint = new TextPaint();
textPaint.setColor(Color.BLACK);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setTextSize(fTextSize);
float textHeight = textPaint.descent() - textPaint.ascent();
float textOffset = (textHeight / 2) - textPaint.descent();
canvas.drawText(dataStrings.getGewicht(), rect.centerX(), rect.centerY() + textOffset, textPaint);
// Leistung pro Stunde
rect2 = new RectF();
paint = new android.graphics.Paint();
fPositionY = fPositionY + fRectHeight + 50;
float fRectWidth = pts[2] / 2;
fRectHeight = (float) (pts[17] / 5);
// Fill rect
rect2.set(fPosition, fPositionY, fRectWidth, fRectHeight);
paint.setColor(Color.YELLOW);
paint.setStyle(android.graphics.Paint.Style.FILL);
paint.setStrokeWidth(0);
canvas.drawRoundRect(rect2, 10, 10, paint);
// Border
paint.setColor(Color.BLACK);
paint.setStyle(android.graphics.Paint.Style.STROKE);
paint.setStrokeWidth(3);
canvas.drawRoundRect(rect2, 10, 10, paint);
// Text
fTextSize = fRectHeight - 40;
textPaint = new TextPaint();
textPaint.setColor(Color.BLACK);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setTextSize(fTextSize);
textHeight = textPaint.descent() - textPaint.ascent();
textOffset = (textHeight / 2) - textPaint.descent();
canvas.drawText(dataStrings.getLeistung(), rect2.centerX(), rect2.centerY() + textOffset, textPaint);
I'm drawing a circle and I want on the circle line rectangles that are rotated in the right degree. Like on this image:
With the help off someone on SO I've found a way to do that (almost).
I've got this:
Like you see there are 2 things missing:
1) they are not on the circle line
2) They are not rotated
I know how to rotate on a canvas in android but then they are all mixed up.
This is my code:
int r = 200;
canvas.save();
mPaint.setStyle(Paint.Style.STROKE);
canvas.translate(rect.width() / 2, rect.height() / 2);
canvas.drawCircle(0, 0, r, mPaint);
mPaint.setStyle(Paint.Style.FILL);
for (int alpha = 0; alpha <= 360; alpha += 20) {
double x = r/3 * Math.cos(Math.toRadians(alpha));
double y = r/3 * Math.sin(Math.toRadians(alpha));
canvas.translate((float)x, (float)y);
canvas.drawRect(0, 0, 70, 20, mPaint);
}
canvas.restore();
Can someone point me out on what I'm doing wrong, why they aren't showing on the line of the circle? And second how to rotate, because when I do canvas.rotate(alpha) they aren't in a circle anymore.
EDIT:
My code is now like this:
int r = 200;
canvas.save();
mPaint.setStyle(Paint.Style.STROKE);
canvas.translate(rect.width() / 2, rect.height() / 2);
canvas.drawCircle(0, 0, r, mPaint);
mPaint.setStyle(Paint.Style.FILL);
for (int alpha = 0; alpha <= 360; alpha += 20) {
double x = r * Math.cos(Math.toRadians(alpha));
double y = r * Math.sin(Math.toRadians(alpha));
canvas.rotate(20.f);
canvas.drawRect(0, 0, 70, 20, mPaint);
}
canvas.restore();
and it gives me this:
EDIT 2:
code is like:
int r = 200;
canvas.save();
mPaint.setStyle(Paint.Style.STROKE);
canvas.translate(rect.width() / 2, rect.height() / 2);
canvas.drawCircle(0, 0, r, mPaint);
mPaint.setStyle(Paint.Style.FILL);
for (int alpha = 0; alpha <= 360; alpha += 20) {
double x = r * Math.cos(Math.toRadians(alpha));
double y = r * Math.sin(Math.toRadians(alpha));
canvas.rotate(20.f);
canvas.save();
canvas.translate(0, -200);
canvas.drawRect(0, 0, 70, 20, mPaint);
canvas.restore();
}
canvas.restore();
Result:
Doing a rotation with a translation sounds like an impossible task. Draw your rectangles straight and rotate the Canvas with Canvas.rotate() instead.
See: Canvas API
public static Bitmap getCircularBitmapWithWhiteBorder(Bitmap bitmap,
int borderWidth) {
if (bitmap == null || bitmap.isRecycled()) {
return null;
}
final int width = bitmap.getWidth() + borderWidth;
final int height = bitmap.getHeight() + borderWidth;
Bitmap canvasBitmap = Bitmap.createBitmap(width*2, height, Bitmap.Config.ARGB_8888);
BitmapShader shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);
Canvas canvas = new Canvas(canvasBitmap);
float radius = width > height ? ((float) height) / 2f : ((float) width) / 2f;
canvas.drawCircle(width / 2, height / 2, radius, paint);
paint.setShader(null);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(borderWidth);
canvas.drawCircle(width / 2, height / 2, radius - borderWidth / 2, paint);
return canvasBitmap;
}
I have circular image, i want to attach next to circular image sort of a rectangle..similar to his
this is a circle, next to it there is a rectangle attached. how can I do this?
You could draw your Rectangle more or less like this:
canvas.drawRect(width/2 + radius, height/2 - radius, width , height, paint);
just adjust the parameters for positioning it correctly if needed
In hardware accelerated custom View added in ScrollView or ListView both of the following code snippets produces same result: (please ignore best practises for a sec)
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// centering stuff
float centerX = getWidth() / 2f;
float centerY = getHeight() / 2f;
float size = 80;
float halfSize = size / 2f;
float left = centerX - halfSize;
float top = centerY - halfSize;
RectF oval = new RectF(left, top, left + size, top + size);
Path path = new Path();
path.addArc(oval, 160, 359);
Paint paint = new Paint();
paint.setTextSize(30);
paint.setStyle(Style.STROKE);
canvas.drawTextOnPath("Hello world", path, 0, 0, paint); //<--- line A
canvas.drawCircle(centerX, centerY, 10, paint); //<--- line B
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// centering stuff
float centerX = getWidth() / 2f;
float centerY = getHeight() / 2f;
float size = 80;
float halfSize = size / 2f;
float left = centerX - halfSize;
float top = centerY - halfSize;
RectF oval = new RectF(left, top, left + size, top + size);
Path path = new Path();
path.addArc(oval, 160, 359);
Paint paint = new Paint();
paint.setTextSize(30);
paint.setStyle(Style.STROKE);
canvas.drawCircle(centerX, centerY, 10, paint); //<--- line B
canvas.drawTextOnPath("Hello world", path, 0, 0, paint); //<--- line A
}
Same Result:
But with later code snippet, as soon as you scroll the ScrollView (I have invisible dummy View below so I can scroll) and helloworld touches ActionBar, something very intersting happens and you see something that intelligent humankind used to see in old Windows OS .
I know drawTextOnPath() is not supported in hardware accelration mode, but then why it works if you call it first?
drawTextOnPath() is supported by hardware acceleration after Android 4.1
This is mentioned officially here: https://code.google.com/p/android/issues/detail?id=37925
but the next comment seems to indicate your problem in a way so maybe a bug.
Of course for pre 4.1 just dont make it use HW accel - Set a software layer type on your View by calling View.setLayerType(View.LAYER_TYPE_SOFTWARE, null) and try to get a tradeoff on perf vs errors