Drawable: Understanding canvas width and height - android

I try to implement my own drawable that will corss fade from a simple colored rectangle (that should have the same size as the ImageView where the drawable is in) to a loaded bitmap (over http).
So what I do is override the draw() method of my Drawable:
public void draw(Canvas canvas) {
boolean done = true;
Log.d("Test",
"canvas w: " + canvas.getWidth() + " " + canvas.getHeight());
final int alpha = mAlpha;
final boolean crossFade = mCrossFade;
Paint paint = mStartPaint;
paint.setColor(blueColor);
if (!crossFade || 255 - alpha > 0) {
if (crossFade) {
paint.setAlpha(255 - alpha);
}
// Draw the rect with the color
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);
if (crossFade) {
paint.setAlpha(0xFF);
}
}
if (alpha > 0) {
Bitmap bitmap = mEnd;
paint = mEndPaint;
paint.setAlpha(alpha);
if (mBitmapScalingType == null)
canvas.drawBitmap(bitmap, mEndX, mEndY, paint);
else
mBitmapScalingType.draw(bitmap, canvas.getWidth(),
canvas.getHeight(), canvas, paint);
paint.setAlpha(0xFF);
}
if (!done) {
mHandler.post(mInvalidater);
}
}
So the canvas.getWidth() and canvas.getHeight() returns 128 (width) and 150 (height) which is the same as the ImageView has. But the result is:
The blue rectangle has not been painted over the complete canvas and I can't get figure out why.
The ImageView that displays my fadeable drawable has no specific scaletype set.
Any ideas what could be wrong?
I have also noticed that the width and height calculated from setBounds() is 180 x 212 px
#Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
final int width = right - left;
final int height = bottom - top;
Log.d("Test", "width: "+width+" height: "+height);
}
and if I draw the color with 180 x 212 px
canvas.drawRect(0, 0, 180, 212, paint);
the rectangular is now drawn to fill the canvas completely.
Any idea what could be wrong?
Btw. The ImageView is defined like that:
<ImageView android:id="#+id/playerPic"
android:layout_width="64dp"
android:layout_height="75dp"
android:layout_marginRight="10dp"
/>
in xhdpi 64dp = 128px and 75dp = 150 px

This answer doesn't explain the difference in dimensions, but sometimes it's easier to just do things a different way.
If you just want to fill the canvas with a color, you don't need to use drawRect(), just use drawColor(). There's the simple version that just takes a Color, and one that lets you specify a porter-duff mode. This way you don't need dimensions at all, and you can avoid the whole thing.

Maybe you can try to use DisplayMetrics to get the dimensions.
//Get the width and height of the screen
DisplayMetrics d = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(d);
int width = d.widthPixels;
int height = d.heightPixels;
Then just call drawrect with those values
canvas.drawRect(0, 0, width, height, paint);

Related

Android PorterDuff: Why doesn't PorterDuff.Mode.SRC_IN cut out the rest of the source?

I'm trying to use the PorterDuff library to trim a canvas circle and rectangle to form a quarter of a square in my custom view for my app, I managed to get it to work but not fully because it trims the square out but keeps the rest of the circle in, I'm using the SRC_IN mode which seems like the right mode to use from looking at the android documentation for it but it's not working as expected, this is a snippet of the onDraw method in my custom view class:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int height = getHeight();
int width = getWidth();
canvas.drawCircle(width / 2, height / 2, (width + height) / 10, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
Rect rectangle = new Rect(0, 0, width / 2, height / 2);
paint.setColor(Color.RED);
canvas.drawRect(rectangle, paint);
}
I'm drawing the circle in the center of the screen and then drawing the square in the top left of the screen and the PorterDuff mode should basically get the intersected part between the shapes but it just trims non-intersected square part out but doesn't do the same for the circle.
This is what it looks like:
I can't tell what i'm doing wrong here, hopefully someone can point it out.
the issue is with the source, the rectangle is the source in this context, draw the rectangle first then use PorterDuff.Mode.SRC_IN to cut the circle based on it.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int height = getHeight();
int width = getWidth();
Rect rectangle = new Rect(0, 0, width / 2, height / 2);
paint.setColor(Color.RED);
canvas.drawRect(rectangle, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawCircle(width / 2, height / 2, (width + height) / 10, paint);
}

How to scale Canvas and draw thin border around the edge?

I have been struggling to get the preview of a PDF in the Android Print Framework to match the print output. However, I am able to get the output to match as a bitmap. However, if I scale the bitmap and put whitespace around the bitmap, the Print Framework crops out the whitespace. So, to prevent that from happening, I would like to draw a thin border around the edge of the entire Canvas.
This is the code that I have to scale the bitmap:
public static Bitmap captureScreen(View v) {
Bitmap screenshot = null;
Bitmap output = null;
try {
if (v != null) {
screenshot = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
int width = screenshot.getWidth();
int height = screenshot.getHeight();
int scaledWidth = (int) (width * 0.5);
int scaledHeight = (int) (height * 0.5);
output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Bitmap scaled = Bitmap.createScaledBitmap(screenshot, scaledWidth, scaledHeight, false);
Canvas canvas = new Canvas(output);
Paint paint = new Paint();
int distX = (width - scaledWidth) / 2;
int distY = (height - scaledHeight) / 2;
canvas.drawBitmap(scaled, distX, distY, paint);
v.draw(canvas);
}
} catch (Exception e) {
Log.d("ScreenShotActivity", "Failed to capture screenshot because:" + e.getMessage());
}
return output;
}
I would like to draw a thin black border around the entire canvas for I can prevent the whitespace from being cropped out by the Print Framework.
Well, you have the width and height of the canvas, just call Canvas.drawRect(0, 0, width - 1, height - 1, strokePaint)?

drawBitmap draw nothing android

I'm developing a custom view and I need to draw drawables inside.
Those drawable must have their position relative.
Here is my code :
#Override
protected void onDraw(Canvas canvas) {
//drawBackground(canvas);
float height = (float) getHeight();
float width = (float) getWidth();
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(width, height);
drawFlowChart(canvas);
drawDrawables(canvas);
canvas.restore();
}
private void drawDrawables(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
Resources res = getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.motor);
canvas.drawBitmap(bitmap, 0.51f, 0.35f, null);
canvas.restore();
}
But it draw nothing.
Any idea ?
I tried to use the drawBitmap(bitmap, src, dest, paint) but I don't know how to position my bitmap with relative position with this definition.
May be getHeight() and getWidth() function that you call in onDraw return 0 so that canvas scaled and nothing to see.
You can refer two that link to update width and height of canvas to draw:
Android getWidth() return 0 in View's onDraw
Android: How to get a custom View's height and width?

Wrong Stroke width drawing circle on custom view Android

I am writing a custom view in android. I want to draw a circle that cover all width and height of my view. this is my code
private void init() {
bgpaint = new Paint();
bgpaint.setColor(bgColor);
bgpaint.setAntiAlias(true);
bgpaint.setStyle(Paint.Style.STROKE);
bgpaint.setStrokeWidth(strokeWidth);
rect = new RectF();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw background circle anyway
int strokeWidth = 50;
rect.set(strokeWidth, strokeWidth, getwidth()- strokeWidth,
getheight() - strokeWidth);
canvas.drawArc(rect, -90, 360, fill, bgpaint);
}
But when I run result will be like this
I want be like this
What the problem with my code?
Probem is in setting your rectangle's coordinates. You must set half of stroke width, not whole stroke width.
float left=strokeWidth / 2;
float top=strokeWidth / 2;
float right=minDimen - strokeWidth/ 2;
float bottom=minDimen - strokeWidth / 2;
rect.set(left, top, right,bottom);
Because it comes to circle, I assume your width and height of your circleView are the same dimension. That is minDimen in my code. Because I use smaller dimension of those two.
final int minDimen = Math.min(width, height);
setMeasuredDimension(minDimen, minDimen);
(this must be called in onMeasure method)
Anyway, it's good method to set width and height in same dimensions.

How to create white border around bitmap?

For example I want a white border of 10pixel around all 4 side of the bitmap. I am not using it for imageview
I am currently using this code to crop image. May I know how I could add a white border into it?
public Bitmap scaleCenterCrop(Bitmap source, int newHeight, int newWidth) {
int sourceWidth = source.getWidth();
int sourceHeight = source.getHeight();
// Compute the scaling factors to fit the new height and width, respectively.
// To cover the final image, the final scaling will be the bigger
// of these two.
float xScale = (float) newWidth / sourceWidth;
float yScale = (float) newHeight / sourceHeight;
float scale = Math.max(xScale, yScale);
// Now get the size of the source bitmap when scaled
float scaledWidth = scale * sourceWidth;
float scaledHeight = scale * sourceHeight;
// Let's find out the upper left coordinates if the scaled bitmap
// should be centered in the new size give by the parameters
float left = (newWidth - scaledWidth) / 2;
float top = (newHeight - scaledHeight) / 2;
// The target rectangle for the new, scaled version of the source bitmap will now
// be
RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);
// Finally, we create a new bitmap of the specified size and draw our new,
// scaled bitmap onto it.
Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, source.getConfig());
Canvas canvas = new Canvas(dest);
canvas.drawBitmap(source, null, targetRect, null);
return dest;
}
I wrote a function for this:
private Bitmap addWhiteBorder(Bitmap bmp, int borderSize) {
Bitmap bmpWithBorder = Bitmap.createBitmap(bmp.getWidth() + borderSize * 2, bmp.getHeight() + borderSize * 2, bmp.getConfig());
Canvas canvas = new Canvas(bmpWithBorder);
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(bmp, borderSize, borderSize, null);
return bmpWithBorder;
}
Basically it creates a new Bitmap adding 2 * bordersize to each dimension and then paints the original Bitmap over it, offsetting it with bordersize.
As for a way of doing this. You make your bitmap bigger than the one your adding to it and then fill the canvas with the background you want. If you need to add other effects you can look into the canvas options for clipping the rect and adding rounded corners and such.
RectF targetRect = new RectF(left+10, top+10, left + scaledWidth, top + scaledHeight);
Bitmap dest = Bitmap.createBitmap(newWidth+20, newHeight+20, source.getConfig());
Canvas canvas = new Canvas(dest);
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(source, null, targetRect, null);
You can draw 4 rectangles after painting your bitmap's stuff.
point 0,0,3,sizey
point 0,0,sizex,3
point 0,sizey-3,sizex,sizey
point sizex-3,0,sizex,sizey
the accepted answer is nice but in the cases that bitmap contains a transparent background, it fills all over the background of source bitmap with white pixels. so it doesn't work fine for all cases.
a better way to achieve this goal is using Canvas#drawLine method like the following code:
Bitmap drawBorder(Bitmap source) {
int width = source.getWidth();
int height = source.getHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, source.getConfig());
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setStrokeWidth(50);
paint.setColor(Color.WHITE);
canvas.drawLine(0, 0, width, 0, paint);
canvas.drawLine(width, 0, width, height, paint);
canvas.drawLine(width, height, 0, height, paint);
canvas.drawLine(0, height, 0, 0, paint);
canvas.drawBitmap(source, 0, 0, null);
return bitmap;
}
in this way we first create a second bitmap using source bitmap width, height and config and use drawline() mathod four times to draw four lines using coordinates of end points of each line around the second bitmap and then draw the source bitmap on the second bitmap that must be returned.
A super easy way of doing it would be to set the ImageView background to white and add a padding value.
If that doesn't work, create a FrameLayout with w/h of wrap_content, set its background to white, put the ImageView in there, and set the ImageView's margins to the desired border width.
Its not elegant but you can always just draw a rectangle behind it, you already have the code to do this and any performance impact is going to be unnoticeable
You can create your targetRectangle 20px wider and 20px higher
RectF targetRect = new RectF(left, top, left + scaledWidth + 20, top + scaledHeight + 20);
and paint the background white
Try this it will also add border to your canvas
canvas.drawLine(0, 0, canvas.getWidth(), 0, paint2);
canvas.drawLine(0, 0, 0, canvas.getHeight(), paint2);
canvas.drawLine(0, canvas.getHeight(), canvas.getWidth(),
canvas.getHeight(), paint2);
canvas.drawLine(canvas.getWidth(), 0, canvas.getWidth(),
canvas.getHeight(), paint2);

Categories

Resources