This is how my custom drawable looks by default.
But when scrolled, it overlaps with the AppBarLayout.
The code for the Drawable goes like this:
#Override
public void draw(#NonNull Canvas canvas) {
// get drawable dimensions
Rect bounds = getBounds();
float width = bounds.right - bounds.left;
float height = bounds.bottom - bounds.top;
float w2 = width / 2;
float h2 = height / 2;
float radius = Math.min(w2, h2) - mStrokeWidth / 2;
mPath.reset();
mPath.addCircle(width / 2, height / 2, radius, Path.Direction.CW);
canvas.clipPath(mPath);
// draw background gradient
float barHeight = height / themeColors.length;
mRectF.left = 0;
mRectF.top = 0;
mRectF.right = width;
mRectF.bottom = height;
for (int i = 0; i < themeColors.length; i++) {
mPaint.setColor(themeColors[i]);
canvas.drawRect(0, i * barHeight, width, (i + 1) * barHeight, mPaint);
}
mRectF.set(0, 0, width, height);
canvas.clipRect(mRectF, Region.Op.REPLACE);
if (mStrokeWidth != 0)
canvas.drawCircle(width / 2, height / 2, width / 2 - mStrokeWidth / 2, mStrokePaint);
}
Support Library Version: 25.3.1, 26.1.0
What I've tried:
- Different Region values for clipping path instead of REPLACE
- Clipping path rectangle first and then clipping circle.
How do I fix this ?
I am posting my solution as answer.
The reason it was getting overlapped was because the canvas was getting clipped twice without saving it.
I removed this statement:
canvas.clipRect(mRectF, Region.Op.REPLACE);
and before clipping the canvas for the first time
i saved its state using
canvas.save();
canvas.clipPath(mPath);
and then when i was drawing the stroke, i need the original canvas so i restored it
canvas.restore();
if (mStrokeWidth != 0)
canvas.drawCircle(width / 2, height / 2, width / 2 - mStrokeWidth / 2, mStrokePaint);
This fixed the issue.
Final Drawable Code:
#Override
public void draw(#NonNull Canvas canvas) {
// get drawable dimensions
Rect bounds = getBounds();
float width = bounds.right - bounds.left;
float height = bounds.bottom - bounds.top;
float w2 = width / 2;
float h2 = height / 2;
float radius = Math.min(w2, h2) - mStrokeWidth / 2;
mPath.reset();
mPath.addCircle(width / 2, height / 2, radius, Path.Direction.CW);
canvas.save();
canvas.clipPath(mPath);
// draw background gradient
float barHeight = height / themeColors.length;
mRectF.left = 0;
mRectF.top = 0;
mRectF.right = width;
mRectF.bottom = height;
for (int i = 0; i < themeColors.length; i++) {
mPaint.setColor(themeColors[i]);
canvas.drawRect(0, i * barHeight, width, (i + 1) * barHeight, mPaint);
}
mRectF.set(0, 0, width, height);
//canvas.clipRect(mRectF, Region.Op.REPLACE);
canvas.restore();
if (mStrokeWidth != 0)
canvas.drawCircle(width / 2, height / 2, width / 2 - mStrokeWidth / 2, mStrokePaint);
}
Related
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()
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?
I have a drawable of a gauge and I try to draw a line on it. (Fixed position, not fixed value)
Unfortunately I don't have any experience with drawing. (And I think its hard to understand)
At the moment it looks like this:
https://i.stack.imgur.com/5tkXEl.png
As you can see, its not positioned right and I'm quite afraid of looking at it on other devices.
Code (Class extends View):
private void init(Context context) {
bounds = new Rect();
gaugeBackground = ContextCompat.getDrawable(context, R.drawable.gauge);
arcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
arcPaint.setColor(Color.RED);
arcPaint.setStyle(Paint.Style.STROKE);
arcPaint.setStrokeWidth(7f);
}
protected void onDraw(Canvas canvas) {
canvas.getClipBounds(bounds);
gaugeBackground.setBounds(bounds);
gaugeBackground.draw(canvas);
float padding = 5;
float size = getWidth() < getHeight() ? getWidth() : getHeight();
float width = size - (2 * padding);
float height = size - (2 * padding);
float radius = (width < height ? width /2 : height /2);
float rectLeft = (width /2) - radius + padding;
float rectTop = (getHeight() /2) - radius + padding;
float rectRight = (getWidth() /2) - radius + padding + width;
float rectBottom = (getHeight() /2) - radius + padding + height;
RectF mRect = new RectF(rectLeft, rectTop, rectRight, rectBottom);
canvas.drawArc(mRect, 119f, 300f, false, arcPaint);
}
Can someone provide helpful information? I hope I finally get the whole RectF/Drawing stuff..
Oh well, I had multiple issues with my implementation.
Main issue: The Background Image was 'warped', so the gauge wasn't really round.
Also: The code was kind of messed up and way too complicated.
I leave the question here - maybe it helps someone in the future.
protected void onDraw(Canvas canvas) {
canvas.getClipBounds(bounds);+
//0.86 == Aspect Ratio of the Background
gaugeBackground.setBounds(0, 0, bounds.right, (int) Math.round(bounds.bottom * 0.86));
gaugeBackground.draw(canvas);
float padding = 3;
float size = getWidth() < getHeight() ? getWidth() : getHeight();
float width = size - (2 * padding);
float height = size - (2 * padding);
RectF mRect = new RectF(padding, padding, width, height);
canvas.drawArc(mRect, 134f, 271f, false, arcPaint);
}
I am unable set image instead of close text using canvas.Close
text is created using below method.
private void drawCenterText(Canvas canvas, Paint paint) {
paint.setColor(mToggleMenuTextColor);
switch (mOrientation) {
case VERTICAL_RIGHT:
canvas.drawText(openMenuText,
getWidth() - paint.measureText(openMenuText),
getHeight() / 2, paint);
break;
case VERTICAL_LEFT:
canvas.drawText(openMenuText, 2, getHeight() / 2, paint);
break;
case HORIZONTAL_TOP:
canvas.drawText(openMenuText,
(getWidth() / 2) - (paint.measureText(openMenuText) / 2),
textSize, paint);
break;
case HORIZONTAL_BOTTOM:
canvas.drawText(openMenuText,
(getWidth() / 2) - (paint.measureText(openMenuText) / 2),
getHeight() - (textSize), paint);
break;
}
}
I hope this should help you out, I have not tried it but it should let you get going.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
int bitWidth = b.getWidth();
int bitHeight = b.getHeight();
if (width > 0 && height > 0)
canvas.drawBitmap(b, (width / 2) - (bitWidth / 2), height - (bitHeight), mPaint);
else
canvas.drawBitmap(b, 0, 0, mPaint);
}
Where b is my bitmap with any icon initialized as
b = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
I would like to draw the right part of an image, but the result is alwas a stretched image of the entire bitmap, or a stretched part of the center of the bitmap. What am I doing wrong here?
int width;
int percentToDraw;
Random rand = new Random();
percentToDraw = rand.nextInt(100);
width = bmp.getWidth() * (100 - percentToDraw) / 100;
src = new Rect();
src.top = 0;
src.bottom = bmp.getHeight();
src.right = bmp.getWidth();
src.left = src.right - width;
dst = new Rect;
dst.top = getHeight() / 2 - bmp.getHeight() / 2;
dst.bottom = dst.top + (src.bottom - src.height);
dst.right = getWidth() / 2 + bmp.getWidth() / 2;
dst.left = dst.right - width;
canvas.drawBitmap(bmp, src, dst, paint);
I have been fiddling and googling about this for two days now, widthout finding anything close to a solution :-(
In case anyone else stumbles over this, I have been unable to make it work "correctly" but I've found a working solution using clipRect:
int width;
width = (int) Math.floor(bmp.getWidth() * percentToDraw / 100.0);
src = new Rect();
src.top = 0;
src.bottom = bmp.getHeight();
src.left = 0;
src.right = bmp.getWidth();
dst = new Rect();
dst.top = getHeight() / 2 - bmp.getHeight() / 2;
dst.bottom = dst.top + (src.bottom - src.top);
dst.left = getWidth() / 2 - bmp.getWidth() / 2;
dst.right = dst.left + (src.right - src.left);
canvas.clipRect(dst.right - width, 0, getWidth(), getHeight(), Region.Op.REPLACE);
canvas.drawBitmap(bmp, src, dst, paint);
canvas.clipRect(0, 0, getWidth(), getHeight(), Region.Op.REPLACE);