Android - Set Drawable to Canvas but keep Aspect Ratio - android

I'm having a small issue with my android code . This is the first time i'm using canvas and i want to set an image to it but keep some type of aspect ratio.
This is what i'm doing right now and this maps the image onto the canvas completely . It sets the image width-height to canvas width-height.
Drawable drawable = image; // gets the image
drawable.SetBounds(0, 0, canvasWidth, canvasHeight);
drawable.Draw(canvas);
This works well on certain devices but when the device is large , the stretching is really bad . I want to set the bounds in such a way that it keeps an aspect ratio . If the canvas width-height is not the same aspect ratio , it will try to fill the canvas with an aspect ratio but might leave some space off in x or y axis .
int imgWidth = 500;
int imgHeight = 800;
I want to map the image on the canvas based on these values and stretch as necessary but keeping this aspect ratio even if part of the canvas is not filled .
What is the best way to do this ?
Thanks

I've already answered a similar question here.
To preserve the aspect ratio I did this:
val drawable = resources.getDrawable(R.drawable.my_vector, null)
val aspectRatio = drawable.intrinsicWidth.toFloat() / drawable.intrinsicHeight
val desiredWidthInPx = 100 // could be your view size or canvas size
val derivedHeightInPx = (desiredWidthInPx / aspectRatio).toInt()
drawable.setBounds(0, 0, desiredWidthInPx, derivedHeightInPx)
drawable.draw(canvas)
The above is for when the drawable width is smaller than its height.
If the drawable height is larger than its width, then first set the height to your desired size and then calculate the width.

Provide correct images in each resource folder. Correct images means correct resolution and screen density. Refer to this link http://developer.android.com/guide/topics/resources/providing-resources.html

Related

How to convert coordinates on Bitmap to real coordiates on Image View displayed on screen

I have an Image View which displays an image (e.g 2000x1000 pixels) and I have a coordinate (X,Y) on that image (not the image view). The canvas of my Image View is 600x800 for example. How can I convert the point (X,Y) to screen coordinate so that I can draw a path with them on the OnDraw(...) method of Image View. Any help is appreciated! Thank you.
Update: If I use matrix to draw the path between coordinates, it works but the path and objects i draw become really small. Here is the code i used.
final Matrix matrix = canvas.getMatrix();
matrix.preConcat( _view.getImageMatrix() );
matrix.preScale( 1.0f /_inSampleSize, 1.0f / _inSampleSize);
canvas.setMatrix( matrix );
//I draw the path here
Update: I add a picture to show the effect when using matrix to draw the path. I would like to have the 4 line and the 4 corner balls to be in normal size. The red color is the boundary of the Image View which holds the picture.
I think that might depend on how exactly you are displaying your image. Your ImageView (600x800) is not the same aspect ratio as your bitmap (2000x1000).
You are keeping the bitmap's aspect ratio stable as you scale it down? If so, which part (height or width) takes up the full screen and which has black (or whatever else) as padding? This will help you determine your scale factor.
scale_factor = goal_height/height1; //if height is what you are scaling by
scale_factor = goal_width/width1; //if width is what you are scaling by.
I would try:
x_goal = x1 * scale_factor;
y_goal = y1 * scale_factor;
That is, if you have a point (1333, 900) in your image, and your image takes up the full width, you would multiply both x and y by 600/2000 to get (399.9, 270). (you might want to round that decimal).
If you are NOT keeping the bitmaps aspect ratio stable (that is, you're squeezing it to fit), then you'd have a height_scale_factor and a width_scale factor. So you'd take (1333,900) and multiply x by 600/2000 and y by 800/1000 to get (399.9,720).

How to get scaled bitmap with respect to screen size with out getting Out of memory exception

In my activity I am creating a Bitmap of the width and height same as the device resolution (width and height )
what I am doing
Bitmap mBitmap = Bitmap.createBitmap(screenWidth, screenHeight,
Bitmap.Config.ARGB_8888);
and screenWidth and screenHeight is
screenHeight = displaymetrics.heightPixels;
screenWidth = displaymetrics.widthPixels;
Now if I make this bitmap I get my heap goes up to 19mb , which is not so good.
So tell me 2 things
1. What is a good way of creating the bitmap with respect to screen with and height with minimum memory consumed
2. How can I destroy the bitmap after using it ?
Please provide me a little source code or link of source code.
What is a good way of creating the bitmap with respect to screen with and height with minimum memory consumed ?
Never ever create a Bitmap with the screen height or width, because that will be very huge based on the density of the device.Instead use the height and width of the screen and calculate the aspect ratio. Now fix the height to a constant value (like 1200px) and then calculate the width based on the aspect ratio. The ImageView or any other view will scale this properly.
If you really want transparency then use Bitmap.Config.ARGB_8888, if not could use RGB_565 or something else from the list here: http://developer.android.com/reference/android/graphics/Bitmap.Config.html
Follow below points if you are not creating a fresh bitmap but decoding the bitmap from some resource:
You need to use BitmapFactory.Options.inSampleSize to reduce the sampling. So how to calculate the inSampleSize if you already know the height and width? Refer this here: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
So you do all this, and still the inSampleSize is not high enough and you get the OutOfMemoryError ? Don't worry, if this is the case, you need to catch this OutOfMemoryError and in that increase your inSampleSize and do the bitmap decode again. Put this logic in a loop, so the crash never happens.
How can I destroy the bitmap after using it ?
This is really simple. Make sure to call the Bitmap.recycle() method. And remove all reference to the bitmap. Once you do this, the resource will be released and GC will do the cleaning up.
UPDATE: When you create the a bitmap with screen_width and screen_height you would end up with a huge bitmap. Instead create a less resolution bitmap that fits the whole screen. You can see the following code on how this is done.
float screenHeight = displaymetrics.heightPixels;
float screenWidth = displaymetrics.widthPixels;
float aspectRatio = screenWidth/screenHeight;
int modifiedScreenHeight = 1000;
int modifiedScreenWidth = (int) (modifiedScreenHeight * aspectRatio);
Bitmap mBitmap = Bitmap.createBitmap(modifiedScreenWidth, modifiedScreenHeight, Bitmap.Config.ARGB_8888);
Now you got the bitmap with the right aspect ratio. You could use this bitmap as it is in the ImageView to fill the whole screen. Make sure you put android:scaleType="fitXY" for the ImageView.

How to fix scale of image

I want to have a ImageView of fixed x / y ratio on all of the screens.
for example famous 16/9 ratio.
And I also want that The ImageView be as large as possible.
how could I do it in android?
Thanks a lot.
The above answer will alter the aspect ratio.
You cannot achieve your goal by only modifying the xml layout file. You have to do this in Java code.
The basic steps are:
read your image file into a Bitmap, and obtain the initial width and height and aspect ratio of the Bitmap.
calculate the scaling factors for x and y dimension respectively, and use the smaller one of the two factors to scale your image
factorX= ScreenWitdhInPixel/ImgWidth
factorY= ScreenHeightInPixel/ImgHeight
factor= (factorX<factorY?factorX:factorY)
scale your image up using the factor value calculated in step 2
Matrix matrix = new Matrix();
matrix.postScale(factor, factor);
Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
Please refer to this post for a full example: ImageView fit without stretching the image
You must set android:scaleType="fitXY" to your imageView.
And if you want your ImageView be as large as its parent, you can write this lines of codes:
android:layout_width="match_parent"
android:layout_height="match_parent"

why getBounds( ) of Drawable remains the same even after scaling?

I used an ImageView to display a jpg file under my project 'assets' folder, the intrinsic dimensions are: 1280x854, which I confirmed by calling myDrawable.getIntrinsicWidth() and myDrawable.getIntrinsicHeight().
My image view implemented pinch zoom so the image was scaled up to 2x for example. Then the following code got executed:
Drawable drawable = getDrawable();
Rect bounds =drawable.getBounds();
Log.i("activity", String.format("drawalbe : top:%d, left: %d, (%d, %d)", bounds.top, bounds.left, bounds.width(), bounds.height()));
the drawable bounds is always 1280x854 no matter what the current scale is, and the top, left is always 0, 0.
Isn't the bounds should be scaled accordingly? Could anyone give me some hints? Thank you.
It's because of this.
Scale matrix only affects how the view is drawn, but does not change its dimensions.
Although you can create a Rect of the size of your image and apply the same scale matrix to Rect with Matrix.mapRect(Rect r) and Rect will have the scaled dimensions.

How should I deal with an Live wallpaper so it supports multiple aspect ratios?

I have a live wallpaper all set up (for simplicity's sake, assume it consists of two rotating bitmaps positioned side by side, each rotating at different speeds; also note that the bitmaps' edges (not the invisible box around the image, but rather the actual pixels of the drawing) are touching.)
I described the translate for each as a percentage of the canvas size (a value between 0 and 1 multiplied by c.getHeight(), and c.getWidth() (where c is a canvas).)
Anyways, everything works fine as long the aspect ratio is maintained. So xxhdpi and xhdpi # 1080x1920 and 720x1280 is all good, but then when i run it on a nexus s # 480x800, the two bitmaps are no longer positioned properly.
I do not want to distort my images (they are circles and must remain circles), so I was thinking the best way would be the limit the size of the canvas and center it.
So for the nexus s # 480x800, i would actually want to only draw in a 450x800 area which i would center on my screen.
In fact, I would always want to have my 2 bitmaps in an area respecting the height = width * 0.5625.
How should I go about doing this?
NB: I based my live wallpaper around the cube sample.
Please let me know if I left any important details out,
Thank you!
Werner
inside your Engine, implement following to capture the screen's height and width
int screenHeight, screenWidth;
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
screenHeight = height; screenWidth = width;
}
and wherever you decode your bitmap, do it like this:-
//get bitmap
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap);
//scale bitmap
bitmap = Bitmap.createScaledBitmap(bitmap, screenWidth, screenWidth * 0.5625, true);
This will fill your entire screen with a circle of correct aspect ratio (width * 0.5625).
And then make it centred by:-
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
final SurfaceHolder holder = getSurfaceHolder();
Canvas canvas = holder.lockCanvas();
int x = ( screenHeight - screenWidth*0.5625 ) / 2;
canvas.drawBitmap(bitmap, 0, x, paint);

Categories

Resources